diff --git a/connector-ui/.browserslistrc b/connector-ui/.browserslistrc new file mode 100644 index 000000000..427441dc9 --- /dev/null +++ b/connector-ui/.browserslistrc @@ -0,0 +1,17 @@ +# This file is used by the build system to adjust CSS and JS output to support the specified browsers below. +# For additional information regarding the format and rule options, please see: +# https://github.com/browserslist/browserslist#queries + +# For the full list of supported browsers by the Angular framework, please see: +# https://angular.io/guide/browser-support + +# You can see what browsers were selected by your queries by running: +# npx browserslist + +last 1 Chrome version +last 1 Firefox version +last 2 Edge major versions +last 2 Safari major versions +last 2 iOS major versions +Firefox ESR +not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line. diff --git a/connector-ui/.dockerignore b/connector-ui/.dockerignore new file mode 100644 index 000000000..12c789440 --- /dev/null +++ b/connector-ui/.dockerignore @@ -0,0 +1,8 @@ +.idea +.vscode +misc +openapi +tmp +node_modules +src/assets/config/*.json +.env* diff --git a/connector-ui/.editorconfig b/connector-ui/.editorconfig new file mode 100644 index 000000000..2c2cc3650 --- /dev/null +++ b/connector-ui/.editorconfig @@ -0,0 +1,17 @@ +# Editor configuration, see https://editorconfig.org +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true +end_of_line = lf + +[*.ts] +quote_type = single + +[*.md] +max_line_length = off +trim_trailing_whitespace = false diff --git a/connector-ui/.env.local-dev b/connector-ui/.env.local-dev new file mode 100644 index 000000000..ccb1d67d5 --- /dev/null +++ b/connector-ui/.env.local-dev @@ -0,0 +1,11 @@ +# .env file configured for default npm run start commands +# see app-config-properties.ts for list of all configurable values +EDC_UI_ACTIVE_PROFILE=sovity-open-source + +EDC_UI_USE_FAKE_BACKEND=true +EDC_UI_MANAGEMENT_API_URL=http://edc.fake-backend +EDC_UI_MANAGEMENT_API_KEY=no-api-key-required-in-local-dev +EDC_UI_CATALOG_URLS=http://existing-other-connector/api/dsp,http://does-not-exist-but-is-super-long-so-we-can-test/api/dsp, http://how-wrapping-works-in-subtext-of-catalog-url-select/api/dsp +EDC_UI_LOGOUT_URL=https://example.com/logout +EDC_UI_CONNECTOR_ENDPOINT=http://localhost:3000/api/dsp +EDC_UI_MANAGEMENT_API_URL_SHOWN_IN_DASHBOARD=http://localhost:3000/api/control/management diff --git a/connector-ui/.github/ISSUE_TEMPLATE/bug_report.yaml b/connector-ui/.github/ISSUE_TEMPLATE/bug_report.yaml new file mode 100644 index 000000000..93c91f3fd --- /dev/null +++ b/connector-ui/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -0,0 +1,62 @@ +name: Bug Report Template +description: Report a bug to help us improve +labels: ["kind/bug"] +body: + - type: textarea + id: description + attributes: + label: Description - What happened? * + description: A clear and concise description of the bug. + placeholder: Tell us what you see! + validations: + required: true + - type: textarea + id: expected + attributes: + label: Expected Behavior * + description: A clear and concise description of what you expected to happen. + placeholder: Tell us what you expected! + validations: + required: true + - type: textarea + id: observed + attributes: + label: Observed Behavior * + description: A clear and concise description of what happened instead. + placeholder: Tell us what you observed! + validations: + required: true + - type: textarea + id: steps + attributes: + label: Steps to Reproduce + description: Steps to reproduce the behavior. + placeholder: Tell us how to reproduce the issue! + validations: + required: false + - type: textarea + id: context + attributes: + label: Context Information + description: Add any other context about the problem here. + validations: + required: false + - type: textarea + id: logs + attributes: + label: Relevant log output + description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. + render: shell + validations: + required: false + - type: textarea + id: screenshots + attributes: + label: Screenshots + description: If applicable, add screenshots or other information to help explain your problem. + validations: + required: false + - type: markdown + attributes: + value: | + _* These fields are mandatory, without filling them it is not possible to create the issue._ diff --git a/connector-ui/.github/ISSUE_TEMPLATE/config.yml b/connector-ui/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..0086358db --- /dev/null +++ b/connector-ui/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1 @@ +blank_issues_enabled: true diff --git a/connector-ui/.github/ISSUE_TEMPLATE/documentation.md b/connector-ui/.github/ISSUE_TEMPLATE/documentation.md new file mode 100644 index 000000000..4ca8c166d --- /dev/null +++ b/connector-ui/.github/ISSUE_TEMPLATE/documentation.md @@ -0,0 +1,30 @@ +--- +name: Documentation Update Request +about: Create a report to help us improve our documentation +title: "" +labels: "task/documentation" +assignees: "" +--- + +# Documentation Update Request + +## Description + + +## Current Documentation + + +## Proposed Changes + + +## Justification + + +## Additional Context + + +## Deadline + + +## Notes + diff --git a/connector-ui/.github/ISSUE_TEMPLATE/epic_template.md b/connector-ui/.github/ISSUE_TEMPLATE/epic_template.md new file mode 100644 index 000000000..390a48995 --- /dev/null +++ b/connector-ui/.github/ISSUE_TEMPLATE/epic_template.md @@ -0,0 +1,46 @@ +--- +name: Epic +about: Help us with new ideas +title: "" +labels: "kind/epic" +assignees: "" +--- + +# Epic + +## Description + + +### Requirements + + + +## Work Breakdown + + +```[tasklist] +### Stories +- [ ] Create Stories which can be converted into issues +``` + +### Security Constraints + +- [ ] Final solution design has been challenged for security related topics + +## Initiative / goal + + +### Hypothesis + + +## Acceptance criteria and must have scope + + +## Stakeholders + + +## Timeline + + +## Need for refinement + diff --git a/connector-ui/.github/ISSUE_TEMPLATE/feature_request.md b/connector-ui/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..979aa2a5b --- /dev/null +++ b/connector-ui/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,36 @@ +--- +name: Feature Request +about: Help us with new features +title: "" +labels: "kind/enhancement" +assignees: "" +--- + +# Feature Request + +## Description + +- As a USER who PRECONDITIONS, I want to DO_THING, so I can ACCOMPLISH_GOAL. + +## Which Areas Would Be Affected? + + +## Why Is the Feature Desired? + + +## How does this tie into our current product? + + +## Stakeholders + + +## Solution Proposal and Work Breakdown + + +```[tasklist] +- [ ] Fix the GitHub Projects Labels, Sprint and other Metadata +- [ ] Refine a Solution Proposal / Work Breakdown +- [ ] (For Tech Team): Include acceptance criteria for the sub-tasks of the work breakdown +- [ ] Add security related tasks and checks +- [ ] Final solution design has been challenged for security related topics +``` diff --git a/connector-ui/.github/ISSUE_TEMPLATE/process.md b/connector-ui/.github/ISSUE_TEMPLATE/process.md new file mode 100644 index 000000000..f49cf43b2 --- /dev/null +++ b/connector-ui/.github/ISSUE_TEMPLATE/process.md @@ -0,0 +1,24 @@ +--- +name: Refine Process Request +about: Existing processes must be adapted or new ones created +title: "" +labels: ["task/refine-process","task/documentation"] +assignees: "" +--- + +# Process Refinement Request + +## Description + + +## Current State + + +## Proposed Changes + + +## Related Issues or PRs + + +## Additional Information + diff --git a/connector-ui/.github/ISSUE_TEMPLATE/release.md b/connector-ui/.github/ISSUE_TEMPLATE/release.md new file mode 100644 index 000000000..247365ad8 --- /dev/null +++ b/connector-ui/.github/ISSUE_TEMPLATE/release.md @@ -0,0 +1,34 @@ +--- +name: Release +about: Create an issue to track a release process. +title: "Release x.y.z" +labels: ["task/release", "scope/ce"] +assignees: "" +--- + +# Release + +## Work Breakdown + +Feel free to edit this release checklist in-progress depending on what tasks need to be done: +- [ ] Decide a release version depending on major/minor/patch changes in the CHANGELOG.md. +- [ ] Update this issue's title to the new version +- [ ] `release-prep` PR: + - [ ] Update the CHANGELOG.md. + - [ ] Check that all the entries have a link to an issue or a pull request. + - [ ] Add a clean `Unreleased` version. + - [ ] Add the version to the old section. + - [ ] Add the current date to the old version. + - [ ] Reorder, reword or combine changelog entries from a product perspective for consistency. + - [ ] Check the [Dependabot Alerts](https://github.com/sovity/edc-ui/security/dependabot) for anything fixable pre-release. + - [ ] Write or review a `Deployment Migration Notes` section. + - [ ] Write or review a release summary. + - [ ] Remove empty sections from the patch notes. + - [ ] Review the Screenshots in the Readme and update them if necessary. + - [ ] Merge the `release-prep` PR. +- [ ] Wait for the main branch to be green. +- [ ] Create a release and re-use the changelog section as release description, and the version as title. +- [ ] Check if the pipeline built the release versions in the Actions-Section (or you won't see it). +- [ ] Revisit the changed list of tasks and compare it with [.github/ISSUE_TEMPLATE/release.md](https://github.com/sovity/edc-ui/blob/main/.github/ISSUE_TEMPLATE/release.md). Propose changes where it + makes sense. +- [ ] Close this issue. diff --git a/connector-ui/.github/PULL_REQUEST_TEMPLATE.md b/connector-ui/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..de7153040 --- /dev/null +++ b/connector-ui/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,10 @@ +_What issues does this PR close?_ + + +```[tasklist] +### Checklist +- [ ] The PR title is short and expressive. +- [ ] I have updated the CHANGELOG.md and linked the changes to their issues. See [changelog_update.md](https://github.com/sovity/authority-portal/blob/main/docs/dev/changelog_updates.md) for more information. +- [ ] I have updated the Deployment Migration Notes Section in the CHANGELOG.md for any configuration / external API changes. +- [ ] I have performed a **self-review** +``` diff --git a/connector-ui/.github/workflows/add_issue_to_project.yml b/connector-ui/.github/workflows/add_issue_to_project.yml new file mode 100644 index 000000000..655427ddb --- /dev/null +++ b/connector-ui/.github/workflows/add_issue_to_project.yml @@ -0,0 +1,17 @@ +name: Add issue to project action + +on: + issues: + types: + - opened + +jobs: + add_issue_to_project: + if: "!(startsWith(github.event.issue.title, '[Zammad Ticket') && github.event.issue.user.login == 'sovitybot')" + name: add_issue_to_project + runs-on: ubuntu-latest + steps: + - uses: actions/add-to-project@v1.0.2 + with: + project-url: https://github.com/orgs/sovity/projects/9 + github-token: ${{ secrets.ADD_ISSUE_TO_PROJECT_PAT }} diff --git a/connector-ui/.github/workflows/automerge.yml b/connector-ui/.github/workflows/automerge.yml new file mode 100644 index 000000000..f0357d2b5 --- /dev/null +++ b/connector-ui/.github/workflows/automerge.yml @@ -0,0 +1,18 @@ +name: auto-merge + +on: + pull_request: + +jobs: + automerge: + runs-on: ubuntu-latest + continue-on-error: true + permissions: + pull-requests: write + contents: write + steps: + - uses: fastify/github-action-merge-dependabot@v3 + continue-on-error: true + with: + target: patch + diff --git a/connector-ui/.github/workflows/build-and-release-image.yaml b/connector-ui/.github/workflows/build-and-release-image.yaml new file mode 100644 index 000000000..8e57acc14 --- /dev/null +++ b/connector-ui/.github/workflows/build-and-release-image.yaml @@ -0,0 +1,73 @@ +name: Build and Release Image + + +on: + workflow_dispatch: + inputs: + imageName: + description: 'Name of the image, that will be available as package' + required: true + type: string + push: + branches: + - main + tags: + - "*" + + +env: + # Use docker.io for Docker Hub if empty + REGISTRY: ghcr.io + # github.repository as / + IMAGE_NAME: edc-ui + IMAGE_NAME_BASE: ${{ github.repository_owner }} + + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + # Checkout code + - uses: actions/checkout@v3 + + - name: Log in to the Container registry (push-only) + uses: docker/login-action@v2 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Store last commit's author, message and SHA + run: | + echo "Saving local commit details in src/assets/config/version.txt" + git log -1 > src/assets/config/version.txt + echo "Saving build date to src/assets/config/ui-build-date.txt" + date --utc +%FT%TZ > src/assets/config/ui-build-date.txt + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v4 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_BASE }}/${{ env.IMAGE_NAME }} + labels: | + org.opencontainers.image.title=EDC-UI extended by sovity + org.opencontainers.image.description=EDC-UI for sovity's extended EDC-Connector. + tags: | + type=semver,pattern={{version}},enable=${{ startsWith(github.ref, 'refs/tags/') }} + type=raw,value=latest,enable={{is_default_branch}} + type=sha,enable={{is_default_branch}} + type=raw,value=${{ inputs.imageName }},enabled=${{ github.event_name == 'workflow_dispatch' }} + + - name: Build EDC-UI image + uses: docker/build-push-action@v4 + with: + file: docker/Dockerfile + context: . + build-args: | + EDC_UI_CONFIGURATION=production + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} diff --git a/connector-ui/.github/workflows/code_analysis.yml b/connector-ui/.github/workflows/code_analysis.yml new file mode 100644 index 000000000..0e9cab29b --- /dev/null +++ b/connector-ui/.github/workflows/code_analysis.yml @@ -0,0 +1,59 @@ +name: Code Analysis + +on: + workflow_dispatch: + pull_request: + branches: [main] + paths-ignore: + - "**.md" + - "docs/**" + +jobs: + is_java_project: + runs-on: ubuntu-latest + outputs: + pom_exists: ${{ steps.check_files.outputs.files_exists }} + checkstyle_active: ${{ steps.check_checkstyle.outputs.checkstyle_active }} + spotbugs_active: ${{ steps.check_spotbugs.outputs.spotbugs_active }} + steps: + - name: Checkout code + uses: actions/checkout@v3 + - name: Check file existence + id: check_files + uses: andstor/file-existence-action@v2 + with: + files: "pom.xml" + - name: check_checkstyle + id: check_checkstyle + run: echo "checkstyle_active=$(if grep -q "maven-checkstyle-plugin" pom.xml; then echo "true"; else echo "false"; fi)" >> $GITHUB_OUTPUT + - name: check_spotbugs + id: check_spotbugs + run: echo "spotbugs_active=$(if grep -q "spotbugs-maven-plugin" pom.xml; then echo "true"; else echo "false"; fi)" >> $GITHUB_OUTPUT + run_checkstyle: + needs: [is_java_project] + if: needs.is_java_project.outputs.pom_exists == 'true' && needs.is_java_project.outputs.checkstyle_active == 'true' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set up JDK ${{ matrix.java }} + uses: actions/setup-java@v3 + with: + distribution: "temurin" + java-version: "17" + cache: "maven" + - name: Run style checks + run: mvn -B checkstyle:check --file pom.xml + run_spotbugs: + needs: [is_java_project] + if: needs.is_java_project.outputs.pom_exists == 'true' && needs.is_java_project.outputs.spotbugs_active == 'true' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set up JDK ${{ matrix.java }} + uses: actions/setup-java@v3 + with: + distribution: "temurin" + java-version: "17" + cache: "maven" + - name: Run static code analysis + run: mvn -B compile spotbugs:check --file pom.xml diff --git a/connector-ui/.github/workflows/license_scan.yml b/connector-ui/.github/workflows/license_scan.yml new file mode 100644 index 000000000..52e335bcc --- /dev/null +++ b/connector-ui/.github/workflows/license_scan.yml @@ -0,0 +1,45 @@ +name: Trivy License Scan + +on: + push: + +jobs: + license_scan1: + name: License scan (rootfs) + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Run license scanner + uses: aquasecurity/trivy-action@0.26.0 + env: + #try default GitHub DBs, if failing, use AWS mirror instead (https://github.com/aquasecurity/trivy-action/issues/389) + TRIVY_DB_REPOSITORY: ghcr.io/aquasecurity/trivy-db,public.ecr.aws/aquasecurity/trivy-db + TRIVY_JAVA_DB_REPOSITORY: ghcr.io/aquasecurity/trivy-java-db,public.ecr.aws/aquasecurity/trivy-java-db + with: + scan-type: "rootfs" + scan-ref: "." + scanners: "license" + severity: "CRITICAL,HIGH" + exit-code: 1 + license_scan2: + name: License scan (repo) + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout code + uses: actions/checkout@v3 + - name: Run license scanner + uses: aquasecurity/trivy-action@0.26.0 + env: + #try default GitHub DBs, if failing, use AWS mirror instead (https://github.com/aquasecurity/trivy-action/issues/389) + TRIVY_DB_REPOSITORY: ghcr.io/aquasecurity/trivy-db,public.ecr.aws/aquasecurity/trivy-db + TRIVY_JAVA_DB_REPOSITORY: ghcr.io/aquasecurity/trivy-java-db,public.ecr.aws/aquasecurity/trivy-java-db + with: + scan-type: "repo" + scan-ref: "." + scanners: "license" + severity: "CRITICAL,HIGH" + exit-code: 1 diff --git a/connector-ui/.github/workflows/secret_scan.yml b/connector-ui/.github/workflows/secret_scan.yml new file mode 100644 index 000000000..b0eb00f02 --- /dev/null +++ b/connector-ui/.github/workflows/secret_scan.yml @@ -0,0 +1,29 @@ +name: Trivy Secret Scan + +on: + push: + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +jobs: + secret-scan: + name: secret_scan + runs-on: ubuntu-latest + strategy: + fail-fast: false + steps: + - name: Checkout code + uses: actions/checkout@v3 + - name: Run vulnerability scanner + uses: aquasecurity/trivy-action@0.26.0 + env: + #try default GitHub DBs, if failing, use AWS mirror instead (https://github.com/aquasecurity/trivy-action/issues/389) + TRIVY_DB_REPOSITORY: ghcr.io/aquasecurity/trivy-db,public.ecr.aws/aquasecurity/trivy-db + TRIVY_JAVA_DB_REPOSITORY: ghcr.io/aquasecurity/trivy-java-db,public.ecr.aws/aquasecurity/trivy-java-db + with: + scan-type: "fs" + exit-code: "1" + ignore-unfixed: true + scanners: secret diff --git a/connector-ui/.github/workflows/security_scan.yml b/connector-ui/.github/workflows/security_scan.yml new file mode 100644 index 000000000..703d1dcc5 --- /dev/null +++ b/connector-ui/.github/workflows/security_scan.yml @@ -0,0 +1,51 @@ +name: Trivy Security Scan + +on: + push: + workflow_dispatch: + +jobs: + security_scan_rootfs: + name: security_scan_rootfs + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Run static analysis (rootfs) + uses: aquasecurity/trivy-action@0.26.0 + env: + #try default GitHub DBs, if failing, use AWS mirror instead (https://github.com/aquasecurity/trivy-action/issues/389) + TRIVY_DB_REPOSITORY: ghcr.io/aquasecurity/trivy-db,public.ecr.aws/aquasecurity/trivy-db + TRIVY_JAVA_DB_REPOSITORY: ghcr.io/aquasecurity/trivy-java-db,public.ecr.aws/aquasecurity/trivy-java-db + with: + scan-type: "rootfs" + scanners: "vuln,misconfig" + ignore-unfixed: true + format: "sarif" + output: "trivy-results-rootfs.sarif" + severity: "CRITICAL,HIGH" + security_scan_repo: + name: security_scan_repo + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + - name: Run static analysis (repo) + uses: aquasecurity/trivy-action@0.26.0 + env: + #try default GitHub DBs, if failing, use AWS mirror instead (https://github.com/aquasecurity/trivy-action/issues/389) + TRIVY_DB_REPOSITORY: ghcr.io/aquasecurity/trivy-db,public.ecr.aws/aquasecurity/trivy-db + TRIVY_JAVA_DB_REPOSITORY: ghcr.io/aquasecurity/trivy-java-db,public.ecr.aws/aquasecurity/trivy-java-db + with: + scan-type: "repo" + scanners: "vuln,misconfig" + ignore-unfixed: true + format: "sarif" + output: "trivy-results-repo.sarif" + severity: "CRITICAL,HIGH" + - name: Upload Trivy scan results to GitHub Security tab (repo) + uses: github/codeql-action/upload-sarif@v2 + continue-on-error: true + with: + sarif_file: "trivy-results-repo.sarif" + category: "code" diff --git a/connector-ui/.github/workflows/test_angular_project.yaml b/connector-ui/.github/workflows/test_angular_project.yaml new file mode 100644 index 000000000..4e7bf1eaa --- /dev/null +++ b/connector-ui/.github/workflows/test_angular_project.yaml @@ -0,0 +1,27 @@ +name: Angular GitHub CI +on: + push: + branches: + - '*' + +jobs: + ci: + timeout-minutes: 5 + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - uses: actions/setup-node@v3 + with: + node-version: 16 + cache: 'npm' + cache-dependency-path: 'package-lock.json' + + - name: Install Dependencies + run: | + npm ci + + - name: Test + run: | + npm run test -- --browsers=ChromeHeadless --watch=false diff --git a/connector-ui/.gitignore b/connector-ui/.gitignore new file mode 100644 index 000000000..a165f8f24 --- /dev/null +++ b/connector-ui/.gitignore @@ -0,0 +1,44 @@ +# See http://help.github.com/ignore-files/ for more about ignoring files. + +# compiled output +/dist +/tmp +/out-tsc +# Only exists if Bazel was run +/bazel-out + +# dependencies +/node_modules + +# profiling files +chrome-profiler-events*.json + +# IDEs and editors +/.idea +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# IDE - VSCode +.vscode/* +.history/* + +# misc +/.sass-cache +/connect.lock +/coverage +*.log +/typings + +# System Files +.DS_Store +Thumbs.db + +.terraform* +.angular/ + +# ENV +.env diff --git a/connector-ui/.pre-commit-README.md b/connector-ui/.pre-commit-README.md new file mode 100644 index 000000000..3f10297fa --- /dev/null +++ b/connector-ui/.pre-commit-README.md @@ -0,0 +1,28 @@ +# Pre-Commit-Hook + +The defined pre-commit-hook prevents committing passwords to the repository. In +case a password is detected git commit fails. + +## Install pre-commit + +1. Install pre-commit-hook tool `$ pip install pre-commit` +2. Install detect-secrets `$ pip install detect-secrets` + +## Enable secret-scanning pre-commit hook + +1. Update pre-commit-hook `$ pre-commit autoupdate` +2. Enable defined pre-commit-hook `$ pre-commit install` + +## On repository initialization of pre-commit hook with detect-secrets + +If no `.secrets.baseline` is present, simply generate it: + +1. `$ detect-secrets scan --disable-plugin KeywordDetector --disable-plugin AWSKeyDetector > .secrets.baseline` +2. Use Notepad++ or IntelliJ-Editor to convert `.secrets.baseline` to UTF-8 + +## Add false-positives or force adding secrets + +1. `$ detect-secrets scan --baseline .secrets.baseline` +2. If secrets are identified, add them to .secrets.baseline manually For more + details see: + https://github.com/Yelp/detect-secrets#adding-secrets-to-baseline diff --git a/connector-ui/.pre-commit-config.yaml b/connector-ui/.pre-commit-config.yaml new file mode 100644 index 000000000..199a5c037 --- /dev/null +++ b/connector-ui/.pre-commit-config.yaml @@ -0,0 +1,7 @@ +repos: + - repo: https://github.com/Yelp/detect-secrets + rev: v1.4.0 + hooks: + - id: detect-secrets + args: ['--baseline', '.secrets.baseline'] + exclude: package.lock.json diff --git a/connector-ui/.prettierignore b/connector-ui/.prettierignore new file mode 100644 index 000000000..753d16651 --- /dev/null +++ b/connector-ui/.prettierignore @@ -0,0 +1,11 @@ +build +coverage +e2e +node_modules +.angular +.github +dist + +package-lock.json + +openapi diff --git a/connector-ui/.secrets.baseline b/connector-ui/.secrets.baseline new file mode 100644 index 000000000..aadd87dde --- /dev/null +++ b/connector-ui/.secrets.baseline @@ -0,0 +1,131 @@ +{ + "version": "1.4.0", + "plugins_used": [ + { + "name": "ArtifactoryDetector" + }, + { + "name": "AzureStorageKeyDetector" + }, + { + "name": "Base64HighEntropyString", + "limit": 4.5 + }, + { + "name": "BasicAuthDetector" + }, + { + "name": "CloudantDetector" + }, + { + "name": "DiscordBotTokenDetector" + }, + { + "name": "GitHubTokenDetector" + }, + { + "name": "HexHighEntropyString", + "limit": 3.0 + }, + { + "name": "IbmCloudIamDetector" + }, + { + "name": "IbmCosHmacDetector" + }, + { + "name": "JwtTokenDetector" + }, + { + "name": "MailchimpDetector" + }, + { + "name": "NpmDetector" + }, + { + "name": "PrivateKeyDetector" + }, + { + "name": "SendGridDetector" + }, + { + "name": "SlackDetector" + }, + { + "name": "SoftlayerDetector" + }, + { + "name": "SquareOAuthDetector" + }, + { + "name": "StripeDetector" + }, + { + "name": "TwilioKeyDetector" + } + ], + "filters_used": [ + { + "path": "detect_secrets.filters.allowlist.is_line_allowlisted" + }, + { + "path": "detect_secrets.filters.common.is_ignored_due_to_verification_policies", + "min_level": 2 + }, + { + "path": "detect_secrets.filters.heuristic.is_indirect_reference" + }, + { + "path": "detect_secrets.filters.heuristic.is_likely_id_string" + }, + { + "path": "detect_secrets.filters.heuristic.is_lock_file" + }, + { + "path": "detect_secrets.filters.heuristic.is_not_alphanumeric_string" + }, + { + "path": "detect_secrets.filters.heuristic.is_potential_uuid" + }, + { + "path": "detect_secrets.filters.heuristic.is_prefixed_with_dollar_sign" + }, + { + "path": "detect_secrets.filters.heuristic.is_sequential_string" + }, + { + "path": "detect_secrets.filters.heuristic.is_swagger_file" + }, + { + "path": "detect_secrets.filters.heuristic.is_templated_secret" + } + ], + "results": { + "terraform\\terraform.tfstate.backup": [ + { + "type": "Base64 High Entropy String", + "filename": "terraform\\terraform.tfstate.backup", + "hashed_secret": "d9382ccd310be3886186f889ecf94f3994eb9529", + "is_verified": false, + "line_number": 101 + }, + { + "type": "Base64 High Entropy String", + "filename": "terraform\\terraform.tfstate.backup", + "hashed_secret": "2803e1c27bcb762d1877c3c2b286a7aa1bd0d55c", + "is_verified": false, + "line_number": 118 + } + ], + "terraform\\terraform.tfvars": [ + { + "type": "Base64 High Entropy String", + "filename": "terraform\\terraform.tfvars", + "hashed_secret": "d9382ccd310be3886186f889ecf94f3994eb9529", + "is_verified": false, + "line_number": 1 + } + ] + }, + "generated_at": "2022-12-06T12:00:42Z" +} diff --git a/connector-ui/.trivyignore b/connector-ui/.trivyignore new file mode 100644 index 000000000..3b17b7880 --- /dev/null +++ b/connector-ui/.trivyignore @@ -0,0 +1,3 @@ +# This concerns the dev dependency @angular-devkit/build-angular. +# This dependency uses webpack-merge@5.8.0 that uses wildcard which uses minimist 1.2.0 (which is causing the error) +CVE-2021-44906 diff --git a/connector-ui/CHANGELOG.md b/connector-ui/CHANGELOG.md new file mode 100644 index 000000000..2aac1e4ce --- /dev/null +++ b/connector-ui/CHANGELOG.md @@ -0,0 +1,1087 @@ +# Changelog + +All notable changes to this project will be documented in this file - formatted +and maintained according to the rules documented on . + +This file will not cover changes about documentation, code clean-up, samples, or +the CI pipeline. With each version (respectively milestone), the core features +are highlighted. Relevant changes to existing implementations can be found in +the detailed section referring to by linking pull requests or issues. + +## [vx.x.x] - UNRELEASED + +### Overview + +### Detailed Changes + +#### Major + +#### Minor + +#### Patch + +### Deployment Migration Notes + +_No special deployment migration steps required_ + +## [v4.1.8] - 2024-12-13 + +### Overview + +MDS Patch release + +### Detailed Changes + +#### Patch + +- Fix wrong placeholders for On Request data offer type + ([#878](https://github.com/sovity/edc-ui/issues/878)) +- Rearrange Sidebar Navigation Groups + ([#836](https://github.com/sovity/edc-ui/issues/836)) + +### Deployment Migration Notes + +_No special deployment migration steps required_ + +## [v4.1.7] - 2024-10-21 + +### Overview + +Fixing a label in the asset creation dialog. + +### Detailed Changes + +#### Patch + +- Fix wrong label for description field on asset creation dialog + ([#870](https://github.com/sovity/edc-ui/issues/870)) + +### Deployment Migration Notes + +_No special deployment migration steps required_ + +## [v4.1.6] - 2024-10-07 + +### Overview + +MDS Patch release + +### Detailed Changes + +#### Patch + +- Fixed a button label stating "Method Parameterization" instead of "Path + Parameterization" ([#857](https://github.com/sovity/edc-ui/issues/857)) +- Made the Custom Http Method mandatory if the corresponding option is chosen + ([#739](https://github.com/sovity/edc-ui/issues/739)) +- Fixed inconsistent renaming of "Contract Definition" to "Data Offer" after + i18n ([#831](https://github.com/sovity/edc-ui/issues/831)) + +### Deployment Migration Notes + +_No special deployment migration steps required_ + +## [v4.1.5] - 2024-09-26 + +### Overview + +MDS Patch release + +### Detailed Changes + +#### Patch + +- Fixed the gaps in renaming "Contract Definition" to "Data Offer" + ([#831](https://github.com/sovity/edc-ui/issues/831)) +- Replaced hints with info boxes in On Request data source + ([#820](https://github.com/sovity/edc-ui/issues/820)) +- Fixed cropping of Contract Offer Ids on catalog browser page + ([#795](https://github.com/sovity/edc-ui/issues/795)) +- Used the `createDataOffer` endpoint to create an asset, policies and a + contract definition in a single call + ([#841](https://github.com/sovity/edc-ui/issues/841)) +- Fixed config not being applied properly after a version upgrade +- Fixed Date to DateTime conversion issues when using operators less than `LT` + and greater than `GT` ([#846](https://github.com/sovity/edc-ui/issues/846)) +- Added initial support for UI internationalization + ([#680](https://github.com/sovity/edc-ui/issues/680)) +- Implemented Data Offer wizard wording change request by MDS +- ([PR#850](https://github.com/sovity/edc-ui/pull/850)) + +### Deployment Migration Notes + +_No special deployment migration steps required_ + +## [v4.1.4] - 2024-09-17 + +### Overview + +MDS Patch release + +### Detailed Changes + +Various bugfixes. + +#### Patch + +- Changed wording on the data offer creation page + ([#817](https://github.com/sovity/edc-ui/issues/795)) +- Data Offer details now display the contract ID for each contract offer + ([#795](https://github.com/sovity/edc-ui/issues/795)) +- Warn the user when using an invalid Policy Id + ([#746](https://github.com/sovity/edc-ui/issues/746)) +- Warn the user when using an invalid Data Offer Id + ([#745](https://github.com/sovity/edc-ui/issues/745)) +- Fixed time restriction upper bound "local day to datetime" conversion issues + ([#815](https://github.com/sovity/edc-ui/issues/815)) + +### Deployment Migration Notes + +_No special deployment migration steps required_ + +## [v4.1.3] - 2024-09-03 + +### Overview + +Bug fixes + +#### Patch + +- Check the contract limits before negotiating a new one. +- Changed the title of Contract Definitions to Data Offers. +- Enhanced EDC UI terminologies for the Create Data Offer tab. +- Date and time display fixes, unified date format. + +## [v4.1.2] - 2024-08-20 + +### Overview + +Bug fixes + +### Detailed Changes + +#### Patch + +- Added description for fields in asset creation mask +- Added description for fields in Create Data Offer page +- Added proper handling of custom JSON properties in edit asset process + +## [v4.1.1] - 2024-08-09 + +### Overview + +MDS 2.2 patch release + +### Detailed Changes + +Various UI improvements and bug fixing + +#### Patch + +- Copyable contact email and subject fields on asset and data offer detail + dialogs +- Assets Page search input field is now case-insensitive +- Markdown support for Reference files description, Conditions for use fields +- Fixed wrong date format when creating a new data offer +- Temporarily re-implemented the Create Asset Dialog +- Performance improvement when fetching a single contract agreement + +## [v4.1.0] - 2024-07-24 + +### Overview + +MDS 2.2 release + +### Detailed Changes + +#### Minor + +- Reworked data offer creation page for easier data sharing +- Complex policies using AND, OR and XONE +- Both providers and consumers can now terminate contracts +- Contracts can be filtered by their termination status +- Adjusted data offer card/detail dialog UI to differentiate live and on request + assets + +#### Patch + +- Fixed an issue that caused the auth information to get lost during asset + creation. + +### Deployment Migration Notes + +_No special deployment migration steps required_ + +## [v4.0.0] - 2024-07-15 + +### Overview + +Broker UI removal refactoring, ToS dialog. + +### Detailed Changes + +#### Major + +- Removed the Broker UI, as it has been moved into the Authority Portal. + +#### Minor + +- Added Initiate Negotiation Confirm ToS Dialog +- Support for creating "On Request" Data Offers + +### Deployment Migration Notes + +- Connector UI: + - _No special deployment migration steps required_ +- Broker UI: + - Can be undeployed, the Broker no longer exists as a stand-alone component. + +## [v3.2.2] - 2024-04-20 + +### Overview + +MDS Bufix Release + +### Detailed Changes + +#### Patch + +- Connector UI & Broker UI: Fixed an issue causing pages using asset cards to + crash if object custom properties were used. +- Removed HTTP Option "HEAD" as it is not supported by the backend. + +### Deployment Migration Notes + +_No special deployment migration steps required_ + +## [v3.2.1] - 2024-04-18 + +### Overview + +NGINX Config Fix + +### Detailed Changes + +#### Patch + +- Revert NGINX header changes because they disallow E2E testing of edc-ce + +### Deployment Migration Notes + +_No special deployment migration steps required_ + +## [v3.2.0] - 2024-04-18 + +MDS Bufix Release + +### Overview + +### Detailed Changes + +#### Minor + +- Security headers added to NGINX Docker Image + +#### Patch + +- Fix `nutsLocations` field + +### Deployment Migration Notes + +_No special deployment migration steps required_ + +## [v3.1.0] - 2024-04-11 + +### Overview + +Small improvements for Connector and Broker UI + +### Detailed Changes + +#### Minor + +- Management API URL now displayed on the dashboard page + +#### Patch + +- Broker UI: "Name" column renamed to "Title" +- Fix status icon for data offers + +### Deployment Migration Notes + +- New **optional** environment variable: + - `EDC_UI_MANAGEMENT_API_URL_SHOWN_IN_DASHBOARD` as override for shown + Management API URL on the dashboard + +## [v3.0.0] - 2024-03-22 + +### Overview + +Bugfixes and improvement of Broker UI integration with AP + +### Detailed Changes + +#### Major + +- Broker UI: Catalog page no longer supports query parameter `connectorEndpoint` + to filter connectors +- Broker UI: Catalog page now supports query parameter `mdsId` to filter + connectors + +#### Patch + +- Fixed an asset select issue caused by a bad compare function +- Asset: Fix double encoding of query params by disallowing '&' and '=' chars in + form field and sending them unencoded +- Fixed an issue that prevented custom headers from being included in HTTP Data + Transfers +- Fix `Content-Type` header being ignored by backend for transfers +- Fixed Method Parameterization always showing "GET", added unselected option. + +### Deployment Migration Notes + +_No special deployment migration steps required_ + +## [v2.5.0] - 2024-02-28 + +### Overview + +Enable better integration of Broker UI and Authority Portal + +### Detailed Changes + +#### Minor + +- Broker UI: Added query params for the connector endpoints filter + +### Deployment Migration Notes + +_No special deployment migration steps required_ + +## [v2.4.0] 2024-02-14 + +### Overview + +MDS feature and bugfix release for Connector UI and Broker UI + +### Detailed Changes + +#### Minor + +- Added new MDS fields to assets + +#### Patch + +- Ensured GDPR compliance of Fonts usage +- Fixed auth proxy issues + +### Deployment Migration Notes + +_No special deployment migration steps required_ + +## [v2.3.1] 2024-01-18 + +### Overview + +Bugfix release for minor UI bugs + +### Detailed Changes + +#### Patch + +- Fixed "No Description" only showing sometimes in cards. + +### Deployment Migration Notes + +_No special deployment migration steps required_ + +## [v2.3.0] 2024-01-17 + +### Overview + +MDS feature release for Connector UI and Broker UI + +### Detailed Changes + +#### Minor + +- Asset descriptions now support Markdown +- Asset metadata is now editable +- Negotiate button is no longer shown for own connector endpoints +- Broker: Catalog now supports list view +- Broker: Connectors page now shows organization names and connector IDs + +#### Patch + +- Improved handling of asset keywords in cards +- EE: Fixed issues around logout page + +### Deployment Migration Notes + +_No special deployment migration steps required_ + +## [v2.2.0] 05.12.2023 + +### Overview + +Aligned Participant ID / Connector ID and Connector Restricted Policy. + +### Detailed Changes + +#### Minor + +- MDS Connector UI: Renamed Participant IDs to MDS Connector IDs +- Connector Restricted Policy now supports entry of multiple connectors. +- New Transfer History Page Column: Participant ID / Connector ID + +### Deployment Migration Notes + +_No special deployment migration steps required_ + +## [v2.1.0] 17.11.2023 + +### Overview + +Broker UI EDC 0 Upgrade and some Connector UI bugfixes. + +### Detailed Changes + +#### Minor + +- New optional marketing banner for MDS Basic Enterprise Edition Connectors. +- Finalize Migrate Broker UI to EDC 0 + +#### Patch + +- Improved visibility of buttons in "Create New Asset" and "Initiate Transfer" + Dialogs +- Broker Server API now also has a type-safe fake backend. +- Fix Data Category not being set correctly when creating assets. + +### Deployment Migration Notes + +- Enterprise Edition only, MDS variants only: New optional config variable + `EDC_UI_SHOW_EE_BASIC_MARKETING=true`. Default `false` + +## [v2.0.0] 10.10.2023 + +### Overview + +EDC 0 compatible version (Connector UI only). + +### Detailed Changes + +#### Major + +- Switched to semantic versioning +- Migrated transfer history page to the api wrapper +- Migrated contract definition page to the api wrapper +- Migrated policy definition page to the api wrapper +- Migrated asset page to the api wrapper +- Migrated dashboard page to the api wrapper +- The Docker Container now uses the port `8080` instead of ~~`80`~~. + +#### Minor + +- Added custom 404 pages to connector and broker ui +- New Asset Property "Participant ID" + +#### Patch + +- Fixed HTTP Parameterization Hints not showing in Asset Details. +- Removed 404-causing login polling from broker UI +- Renamed button from cancel to close in json-dialogs +- Broker: Fixed popularity not logged when clicking on a data offer +- Broker: Fixed missing name in legal notice + +### Deployment Migration Notes + +- The Docker Container now uses the port `8080` instead of ~~`80`~~. +- The following ENV Vars were changed: + - ~~`EDC_UI_DATA_MANAGEMENT_API_URL`~~ became `EDC_UI_MANAGEMENT_API_URL` + - ~~`EDC_UI_DATA_MANAGEMENT_API_KEY`~~ became `EDC_UI_MANAGEMENT_API_KEY` +- The following ENV Vars were removed and should not be specified anymore: + - `EDC_UI_CONNECTOR_ID` + - `EDC_UI_CONNECTOR_NAME` + - `EDC_UI_CURATOR_ORGANIZATION_NAME` + - `EDC_UI_CURATOR_URL` + - `EDC_UI_DAPS_OAUTH_JWKS_URL` + - `EDC_UI_DAPS_OAUTH_TOKEN_URL` + - `EDC_UI_IDS_DESCRIPTION` + - `EDC_UI_IDS_ID` + - `EDC_UI_IDS_TITLE` + - `EDC_UI_MAINTAINER_ORGANIZATION_NAME` + - `EDC_UI_MAINTAINER_URL` + - `EDC_UI_ASSET_PROP_ORIGINATOR_ORGANIZATION` + - `EDC_UI_ASSET_PROP_ORIGINATOR` +- New **optional** ENV Vars: + - `NGINX_ACCESS_LOG`, default: `/dev/stdout` + - `NGINX_ERROR_LOG`, default: `/dev/stderr` + +## [v0.0.1-milestone-8-sovity12] 12.07.2023 + +### Overview + +Broker Server Feature + Bugfix Release + +### Detailed Changes + +#### Added + +- Broker Server: Connector Online Status is now visualized. + +#### Fixed + +- Fixed Policies not being displayed properly. + +## [v0.0.1-milestone-8-sovity11] 07.07.2023 + +### Overview + +Bugfix Release + +### Detailed Changes + +#### Fixed + +- Fixed a bug causing http parameterization not being accessible due to asset + properties not being persisted on the consumer side. + +## [v0.0.1-milestone-8-sovity10] 07.07.2023 + +### Overview + +Bugfix Release + +### Detailed Changes + +#### Fixed + +- Fixed a bug causing data address dtos to be built wrongly. + +## [v0.0.1-milestone-8-sovity9] 04.07.2023 + +### Overview + +Full support for parameterized HTTP Data Sources, some Basic EE features. + +### Detailed Changes + +#### Added + +- Parameterization of Http Data Sources. +- Enteprise Edition (Basic): Added support for consuming contract agreement + limits. + +## [v0.0.1-milestone-8-sovity8] 23.06.2023 + +### Overview + +Bugfix release. + +### Detailed Changes + +#### Fixed + +- Broker UI: Fixed sorting not applied. + +## [v0.0.1-milestone-8-sovity7] 23.06.2023 + +### Overview + +Build dates, open-ended date intervals, Broker UI MvP features. + +### Detailed Changes + +#### Added + +- Added Connector Build date and Commit Information to Additional Properties + section in Dashboard +- Added Open-Ended Date Option to Time-Period-Restricted Policies +- Broker UI: Added sorting, filtering and pagination to catalog page. +- Broker UI: Added legal notice page. + +## [v0.0.1-milestone-8-sovity6] 06.06.2023 + +### Overview + +Added Broker UI and minor Connector UI improvements. + +### Detailed Changes + +#### Added + +- Added Broker PoC UI: + - Refactored Module Structure + - Added Catalog Page + - Added Connector Page + - Added Copyright Footer +- Tooltips for Asset Properties that show the asset property names. + +#### Fixed + +- Fixed Card titles exploding when containing too large words. +- Fixed missing section header for consuming contract agreements. + +## [v0.0.1-milestone-8-sovity5] 09.05.2023 + +### Overview + +Removed catalog browser timeouts. + +### Detailed Changes + +#### Changed + +- Catalog Page now has no timeouts. +- Catalog Page now displays partial results. + +## [v0.0.1-milestone-8-sovity4] 03.05.2023 + +### Overview + +Bugfixes and minor UI improvements. + +### Detailed Changes + +#### Added + +- Added "Show Details" option to each entry in Transfer History Page + +#### Fixed + +- Fixed bug in contract definition page that prevented the entire page from + showing when any contract definition used a non-array operatorRight. +- Fixed contract agreement transfer button being available for providing + contract agreements. + +## [v0.0.1-milestone-8-sovity3] 28.04.2023 + +### Overview + +Reworked Contract Agreement Page, improved stability and full contract offer +information is now displayed. + +### Detailed Changes + +#### Added + +- Reworked the Contract Agreement Page, it now uses its own dedicated API + Wrapper endpoint. +- Added login polling to prevent auto-logout when page is open. +- Showing all asset properties for assets and contract offers via an "Additional + Properties" section. +- Showing policies for contract offers in the asset details dialog. + +#### Changed + +- Marked `EDC_UI_MANAGEMENT_API_URL` as deprecated in favor of + `EDC_UI_MANAGEMENT_API_URL`. +- Marked `EDC_UI_DATA_MANAGEMENT_API_KEY` as deprecated in favor of + `EDC_UI_MANAGEMENT_API_KEY`. + +#### Removed + +- Removed Datasource Payload support, it is probably only intended for Push + +#### Fixed + +- Fixed getting started docker-compose-yaml to use the newly renamed `edc-dev` + image. +- Fixed labels of MDS categories and sub-categories. +- Fixed issue when navigating back after clicking logout. + +## [v0.0.1-milestone-8-sovity2] 24.03.2023 + +### Overview + +Bugfixes for our productive connectors. + +### Detailed Changes + +#### Fixed + +- Fixed implicit limit of 50 being applied to all views. + +## [v0.0.1-milestone-8-sovity1] 20.03.2023 + +### Overview + +Organization names are now prominent in both asset cards and contract offer +cards. + +### Detailed Changes + +#### Added + +- Made asset IDs less prominent in favor of Organization Name + +#### Fixed + +- Removed password suggestions for ID and related fields in Chrome +- Fixed handling of http error response code 401 when user logout from edc-ui +- Bumped minor dependencies + +## [v0.0.1-milestone-7-sovity8] 07.03.2023 + +### Overview + +Minor UI fixes. + +### Detailed Changes + +#### Changed + +- Updated the icon for Fetch Status option in the catalog browser + +#### Fixed + +- Fixed closing of side nav-bar on pressing escape button +- Fixed "Your Contract Definitions" being called "Your Data Offers" in the + dashboard. + +## [v0.0.1-milestone-7-sovity7] 06.03.2023 + +### Overview + +More Connector Self-Description properties in Dashboard. + +### Detailed Changes + +#### Added + +- Added Connector Self-Description property grid in Dashboard. +- Added info texts to differentiate Connector ID and Connector Endpoint. +- Added support for loading additional config from `EDC_UI_CONFIG_URL` on + startup. + +#### Changed + +- Deprecated property `EDC_UI_ASSET_PROP_ORIGINATOR` in favor of + `EDC_UI_CONNECTOR_ENDPOINT`. +- Deprecated property `EDC_UI_ASSET_PROP_ORIGINATOR_ORGANIZATION_NAME` in favor + of `EDC_UI_CURATOR_ORGANIZATION_NAME`. + +#### Fixed + +- Fixed Contract Definition successfully created message. + +## [v0.0.1-milestone-7-sovity6] 02.03.2023 + +#### Changed + +- Navigation Item Order: Switched Contract Definitions and Asset Viewer + +#### Fixed + +- Fixed Contract Definition Page Button Typo. + +## [v0.0.1-milestone-7-sovity5] 24.02.2023 + +### Overview + +New contract definition list, catalog status info and Http Datasink fields. + +### Detailed Changes + +#### Added + +- Added Additional Http Datasink properties. +- Catalog Browser now shows if individual Connector Endpoints were unreachable. +- Reworked Contract Definition cards. + +#### Changed + +- Renamed Connector ID to Connector Endpoint to emphasize differentiate: + - Connector ID: Configured in certificate, contained in DAT. + - Connector Endpoint: Configured IDS Endpoint + +#### Fixed + +- Compatibility section in README.md + +## [v0.0.1-milestone-7-sovity4] 20.02.2023 + +### Overview + +New policy list, new http data source properties, UX improvements + +### Detailed Changes + +#### Added + +- Reworked page loading, empty messages and error states +- Reworked policy list, new cards, new detail dialog. +- Additional Http Datasource Properties: method, content type, request body, + auth header/value, headers + +#### Fixed + +- Placeholder URLs missing "/control/" path +- Fixed Keyword select not adding keywords on input field blur, causing loss of + input. +- Fixed transfer dialog submitting on cancel +- Fixed transfer dialog validation not working +- Fix exception on contract definition dialog cancel + +## [v0.0.1-milestone-7-sovity3] 06.02.2023 + +### Fixed + +- Fixed dashboard splitting transfers and contract agreements in incoming / + outgoing. Contract Agreements currently cannot be distinguished as incoming / + outgoing. + +## [v0.0.1-milestone-7-sovity2] 01.02.2023 + +### Overview + +A new dashboard and other quality of life improvements. + +### Detailed Changes + +#### Added + +- Dashboard with KPIs and charts. +- Current Connector Organization & URL on Landing Page / Dashboard +- Simplified adding of data sources in Catalog Browser via Connector ID. +- Source code version of deployed edc-ui can now be accessed under + /assets/config/version.txt + +#### Changed + +- Connector URL is now called Connector ID, fixed naming and added good + placeholders. + +#### Fixed + +- Fixed additional PolicyDefinition uid vs id issues. +- Fixed E-Mail in README.MD +- Added API Endpoint to workaround extension that fixes an API problem: + - Could not start transfer processes with just contract agreement ids when + asset came from a custom catalog provider. + - It would need the asset's originator url. + - Since contract agreements don't contain the asset and catalogs are not + guaranteed to still contain said asset's details due to policies, a new + endpoint / extension was required. +- Fixed MDS Logo not working. + +## [v0.0.1-milestone-7-sovity1] 19.01.2023 + +### Overview + +- Prepared this repository for its open source release: + - Better configuration via ENV Vars, documentation and CI. + - Changed versioning system to `$EDC_VERSION-sovity$EDC_UI_MINOR_VERSION`. + - Changed release image to `ghcr.io/sovity/edc-ui`. + +### Detailed Changes + +#### Added + +- Split mds and sovity profiles each into "open source" and "hosted by sovity" + variants. + +#### Changed + +- Releasing images now as `ghcr.io/sovity/edc-ui`. +- Changed configuration: + - Removed `app.config.json`. + - Configuration via `EDC_UI_` environment variables in both local dev and + docker container. + - See `app-config-properties.ts` for available properties. + - Configuration via single environment variable `EDC_UI_CONFIG_JSON` possible. +- Added prettier as code formatter. + - Formatted all non-generated code with prettier. + +#### Fixed + +- Menu of Navigation bar hiding behind feedback widget + +## [0.0.1-ab1b6fd0-sovity5] 17.01.2023 + +### Detailed Changes + +#### Added + +- Added support for connector restricted usage policy. +- Added additional asset fields: + - General fields (e.g. standard license). + - MDS specific fields (e.g. transport mode). +- Catalog Browser: + - Reworked cards to support to support some new fields. + - Added new detail dialog showing asset details. +- Asset Viewer: + - Reworked cards to support to support some new fields. + - Added new detail dialog showing asset details. + - Added field CONNECTOR ORIGINATOR ORGANIZATION to be fetched from + app.config.json +- Asset Create Dialog: + - Divided asset creation into stages with an Angular Material Stepper. + - Added title and fixed styling. + - Added validation for required fields. + - Added validation for URL fields. + - Added validation for ID field, no whitespaces. + - Added ID generation from name. + - Added vocabulary for MDS specific fields: Data Category, Data Subcategory, + Transport Mode. + - Added new MDS field: Data Subcategory +- Policy Definition Dialog: + - Added title and fixed styling. + - Added validation for required fields. +- Contract Definition Dialog: + - Added title and fixed styling. + - Added validation for required fields. + +#### Changed + +- Changed asset properties, especially ID property. See `asset-properties.ts`. + +#### Fixed + +- Error-Message Displayed when Creating and Cancelling the Create-Policy-Dialog +- Removed dead theming code at wrong places. +- Fixed "black" accent color having black text on black background. +- Asset Create Dialog: + - Fixed validation not preventing submit. + - Fixed data flow so submit errors don't close dialog. +- Policy Viewer + - Fixed filter / pagination bar styling. +- Policy Definition Dialog: + - Fixed validation not preventing submit. + - Fixed data flow so submit errors don't close dialog. +- Contract Definition Viewer + - Fixed filter / pagination bar styling. +- Contract Definition Dialog: + - Fixed validation not preventing submit. + - Fixed data flow so submit errors don't close dialog. + +## [0.0.1-ab1b6fd0-sovity4] 04.01.2023 + +### Overview + +Supporting communication with milestone 7 EDC. Working Logout. + +### Detailed Changes + +#### Added + +- Angular 14 and Angular Material 14 +- Add Help-Box (Zammad links) + (https://github.com/sovity/edc-sovity-ui/issues/55) +- Additional `logoutUrl` Property in app.config.json + (https://github.com/sovity/edc-sovity-ui/issues/24) + - OAuth2-Proxy-Config has to be adapted + - Setting: --whitelist-domain to {{KEYCLOAK_ASE_URL}} + - Schema: {{EDC_UI_BASE_URL}}/oauth2/sign_out?rd={{KEYCLOAK_LOGOUT_URL}} + - KEYCLOAK_LOGOUT_URL + - all url encoded including {{KEYCLOAK_BASE_URL}} and {{EDC_UI_BASE_URL}} + - Schema: + {{KEYCLOAK_BASE_URL}}%2Frealms%2Fsovity%2Fprotocol%2Fopenid-connect%2Flogout%3Fclient_id%3D{{OAUTH2_PROXY_KEYCLOAK_CLIENT_ID}}%26post_logout_redirect_uri%3D{{EDC_UI_BASE_URL}} +- Support customizing the NGINX listen address + (https://github.com/sovity/edc-sovity-ui/issues/42) + - Required Environment-Variables + - NGINX_BIND + - NGINX_PORT +- Added secret scanning to repo +- Added MDS logo in the tool bar and added MDS theme +- Added focus on nav-bar item +- Added test server setup +- Added Catalog Url Field in Catalog Browser + (https://github.com/sovity/edc-sovity-ui/issues/83) + +#### Changed + +- Renamed policyDefinition `uid` field to `id` +- Removed Logout-Button from Startpage and added it to the menu + (https://github.com/sovity/edc-sovity-ui/issues/24) + +#### Removed + +- Developer text description on start page + +## [0.0.1-ab1b6fd0-sovity3] 2022-11-10 + +### Overview + +Passing multiple Catalog Urls. + +### Detailed Changes + +#### Added + +- Support for Passing multiple Catalog Urls + (https://github.com/sovity/edc-sovity-ui/issues/46) + - `catalogUrl` in `app.config.json` may be set to following pattern: + {catalog1Url},{catalog2Url},... + +## [0.0.1-ab1b6fd0-sovity2] 2022-09-28 + +### Overview + +Robustness and convenience improvements. + +### Detailed Changes + +#### Added + +- Field `originator` in `app.config.json` + (https://github.com/sovity/edc-sovity-ui/pull/36) + +#### Changed + +- CreateAssetDialog (https://github.com/sovity/edc-sovity-ui/pull/31) + - Added ComboBox for Asset Datasource-Type + - Json: Passing a Datasource using Json + - Rest-Api: Just passing a Url + - Removed Originator Field + - Originator will now bet set using the `originator` from `app.config.json` + - Changed Json Input Field to TextArea +- TransferDialog (https://github.com/sovity/edc-sovity-ui/pull/33) + - Added ComboBox for Transfer Destination-Type + - Json: Passing a Data-Destination using Json + - Rest-Api: Just passing a Url +- PolicyDialog (https://github.com/sovity/edc-sovity-ui/pull/34) + - Removed fields (Assignee, Assigner, Permissions, Prohibitions and + Obligations) + - Added ComboBox for Choosing a fixed Policy + - Added Time-Interval-Selection Component for Time-Restricted Policy + +#### Removed + +- `Deprovision` Button in Transfer History: Has been used for AzureBlob Storage + before, but is not required for Rest-Api Transfers + (https://github.com/sovity/edc-sovity-ui/pull/35) + +#### Fixed + +- User-Input will be trimmed before sending it to the EDC-Backend + (https://github.com/sovity/edc-sovity-ui/pull/39) + +## [0.0.1-ab1b6fd0-sovity1] 2022-08-31 + +### Overview + +First release of sovity EDC-Data-Dashboard. Adds support for connecting +REST-APIs. + +### Detailed Changes + +#### Added + +- sovity Theming + +#### Changed + +- Sorting Transfer History Entries by Created Date +- Create Asset Dialog + - Field for arbitrary Data Destination + - Field for Originator + - Has to be set to connectors IDS Endpoint, for instance: + http://daps-connector-a-controlplane-1:8282/api/v1/ids/data +- Transfer Dialog in Contracts + - Field for arbitrary Data Destination + +#### Removed + +- Support for Azure Storage Blobs diff --git a/connector-ui/CODE_OF_CONDUCT.md b/connector-ui/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..6fd31637b --- /dev/null +++ b/connector-ui/CODE_OF_CONDUCT.md @@ -0,0 +1,72 @@ +# Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of +experience, nationality, personal appearance, race, religion, or sexual identity +and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community +- Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +- The use of sexualized language or imagery and unwelcome sexual attention or + advances +- Trolling, insulting/derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or electronic + address, without explicit permission +- Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, or to ban temporarily or permanently any +contributor for other behaviors that they deem inappropriate, threatening, +offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team. All complaints will be reviewed and +investigated and will result in a response that is deemed necessary and +appropriate to the circumstances. The project team is obligated to maintain +confidentiality with regard to the reporter of an incident. Further details of +specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the +[Contributor Covenant](http://contributor-covenant.org), version 1.4, available +at http://contributor-covenant.org/version/1/4. diff --git a/connector-ui/CONTRIBUTING.md b/connector-ui/CONTRIBUTING.md new file mode 100644 index 000000000..0d9c09b64 --- /dev/null +++ b/connector-ui/CONTRIBUTING.md @@ -0,0 +1,195 @@ +# Contributing to the Project + +Thank you for your interest in contributing to this project + +## Table of Contents + +- [Code Of Conduct](#code-of-conduct) +- [How to Contribute](#how-to-contribute) + - [Discuss](#discuss) + - [Create an Issue](#create-an-issue) + - [Submit a Pull Request](#submit-a-pull-request) + - [Report on Flaky Tests](#report-on-flaky-tests) +- [Etiquette for pull requests](#etiquette-for-pull-requests) +- [Contact Us](#contact-us) + +## Code Of Conduct + +See the [Code Of Conduct](CODE_OF_CONDUCT.md). + +## How to Contribute + +### Discuss + +If you want to share an idea to further enhance the project or discuss potential +use cases, please feel free to create a discussion at the +`GitHub Discussions page`] If you feel there is a bug or an issue, contribute to +the discussions in `existing issues` otherwise +[create a new issue](#create-an-issue). + +### Create an Issue + +If you have identified a bug or want to formulate a working item that you want +to concentrate on, feel free to create a new issue at our project's +corresponding `GitHub Issues page`. Before doing so, please consider searching +for potentially suitable `existing issues`. + +We also use +[GitHub's default label set](https://docs.github.com/en/issues/using-labels-and-milestones-to-track-work/managing-labels) +extended by custom ones to classify issues and improve findability. + +If an issue appears to cover changes that will have a (huge) impact on the code +base and needs to first be discussed, or if you just have a question regarding +the usage of the software, please create a `discussion` before raising an issue. + +Please note that if an issue covers a topic or the response to a question that +may be interesting for other developers or contributors, or for further +discussions, it should be converted to a discussion and not be closed. + +### Adhere to Coding Style Guide + +We aim for a coherent and consistent code base, thus the coding style detailed +in the [styleguide](STYLEGUIDE.md) should be followed. + +### Submit a Pull Request + +We would appreciate if your pull request applies to the following points: + +- Conform to following + [Etiquette for pull requests](#etiquette-for-pull-requests): + +- Make sure to adjust copyright headers appropriately. + +- The git commit messages should comply to the following format: + + ``` + (): + ``` + + Use the + [imperative mood](https://github.com/git/git/blob/master/Documentation/SubmittingPatches) + as in "Fix bug" or "Add feature" rather than "Fixed bug" or "Added feature" + and + [mention the GitHub issue](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue) + e.g. `chore(transfer process): improve logging`. + +- Add meaningful tests to verify your submission acts as expected. + +- Where code is not self-explanatory, add documentation providing extra + clarification. + +- PR descriptions should use the current + [PR template](.github/PULL_REQUEST_TEMPLATE.md) + +- Submit a draft pull request at early-stage and add people previously working + on the same code as reviewer. Make sure automatic checks pass before marking + it as "ready for review": + + - _Continuous Integration_ performing various test conventions. + +### Report on Flaky Tests + +If you discover a randomly failing ("flaky") test, please take the time to check +whether an issue for that already exists and if not, create an issue yourself, +providing meaningful description and a link to the failing run. Please also +label it with `Bug` and `github`. Then assign it to whoever was the original +author of the relevant piece of code or whoever worked on it last. If assigning +the issue is not possible due to missing rights, please just comment and +@mention the author/last editor. + +Please do not just restart the run, as this would overwrite the results. If you +need to, a better way of doing this is to push an empty commit. This will +trigger another run. + +```bash +git commit --allow-empty -m "trigger CI" && git push +``` + +If an issue labeled with `Bug` and `github` is assigned to you, please +prioritize addressing this issue as other people will be affected. We are taking +the quality of our code very serious and reporting on flaky tests is an +important step toward improvement in that area. + +## Etiquette for pull requests + +### As an author + +Submitting pull requests should be done while adhering to a couple of simple +rules. + +- Familiarize yourself with [coding style](STYLEGUIDE.md), architectural + patterns and other contribution guidelines. +- No surprise PRs please. Before you submit a PR, open a discussion or an issue + outlining your planned work and give people time to comment. It may even be + advisable to contact committers using the `@mention` feature. Unsolicited PRs + may get ignored or rejected. +- Create focused PRs: your work should be focused on one particular feature or + bug. Do not create broad-scoped PRs that solve multiple issues as reviewers + may reject those PR bombs outright. +- Provide a clear description and motivation in the PR description in GitHub. + This makes the reviewer's life much easier. It is also helpful to outline the + broad changes that were made, e.g. "Changes the schema of XYZ-Entity: the + `age` field changed from `long` to `String`". +- If you introduce new 3rd party dependencies, be sure to note them in the PR + description and explain why they are necessary. +- Stick to the established code style, please refer to the + [styleguide document](STYLEGUIDE.md). +- All tests should be green, especially when your PR is in `"Ready for review"` +- Mark PRs as `"Ready for review"` only when you're prepared to defend your + work. By that time you have completed your work and shouldn't need to push any + more commits other than to incorporate review comments. +- Merge conflicts should be resolved by squashing all commits on the PR branch, + rebasing onto `main` and force-pushing. Do this when your PR is ready to + review. +- If you require a reviewer's input while it's still in draft, please contact + the designated reviewer using the `@mention` feature and let them know what + you'd like them to look at. +- Re-request reviews after all remarks have been adopted. This helps reviewers + track their work in GitHub. +- If you disagree with a committer's remarks, feel free to object and argue, but + if no agreement is reached, you'll have to either accept the decision or + withdraw your PR. +- Be civil and objective. No foul language, insulting or otherwise abusive + language will be tolerated. +- The PR titles must follow + [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/). + - The title must follow the format as + `(): `. `build`, `chore`, `ci`, `docs`, + `feat`, `fix`, `perf`, `refactor`, `revert`, `style`, `test` are allowed for + the ``. + - The length must be kept under 80 characters. + +### As a reviewer + +- Have a look at + [Pull Request Review Pyramide](https://www.morling.dev/blog/the-code-review-pyramid/) +- Please complete reviews within two business days or delegate to another + committer, removing yourself as a reviewer. +- If you have been requested as reviewer, but cannot do the review for any + reason (time, lack of knowledge in particular area, etc.) please comment that + in the PR and remove yourself as a reviewer, suggesting a stand-in. +- Don't be overly pedantic. +- Don't argue basic principles (code style, architectural decisions, etc.) +- Use the `suggestion` feature of GitHub for small/simple changes. +- The following could serve you as a review checklist: + - no unnecessary dependencies in `build.gradle.kts` + - sensible unit tests, prefer unit tests over integration tests wherever + possible (test runtime). Also check the usage of test tags. + - code style + - simplicity and "uncluttered-ness" of the code + - overall focus of the PR +- Don't just wave through any PR. Please take the time to look at them + carefully. +- Be civil and objective. No foul language, insulting or otherwise abusive + language will be tolerated. The goal is to _encourage_ contributions. + +## Contact Us + +If you have questions or suggestions, do not hesitate to contact the project +developers via https://github.com/sovity. + +## Attribution + +This file is adapted from the +[eclipse-edc](https://github.com/eclipse-dataspaceconnector/DataSpaceConnector) +project. diff --git a/connector-ui/LICENSE b/connector-ui/LICENSE new file mode 100644 index 000000000..d9a10c0d8 --- /dev/null +++ b/connector-ui/LICENSE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/connector-ui/NOTICE b/connector-ui/NOTICE new file mode 100644 index 000000000..11716f93b --- /dev/null +++ b/connector-ui/NOTICE @@ -0,0 +1,11 @@ +sovity EDC UI +Copyright (c) 2024. sovity GmbH + +This product includes software developed at sovity GmbH (https://www.sovity.de). +Copyright (c) 2024. sovity GmbH + +The initial internationalization of the EDC UI and localization to German was done +by the Fraunhofer Institute for Applied Information Technology FIT (https://www.fit.fraunhofer.de/). +This project was part of the national flagship project "Datenraum Kultur" and was funded +by the Federal Government Commissioner for Culture and the Media from 2023 to 2025. +Copyright (c) 2024. Fraunhofer Institute for Applied Information Technology FIT diff --git a/connector-ui/README.md b/connector-ui/README.md new file mode 100644 index 000000000..54c494b64 --- /dev/null +++ b/connector-ui/README.md @@ -0,0 +1,227 @@ + + + + + + +[![Contributors][contributors-shield]][contributors-url] +[![Forks][forks-shield]][forks-url] [![Stargazers][stars-shield]][stars-url] +[![Issues][issues-shield]][issues-url] +[![MIT License][license-shield]][license-url] +[![LinkedIn][linkedin-shield]][linkedin-url] + + +
+
+ + Logo + + +

sovity EDC UI

+ +

+ UI for sovity's extended EDC-Connector. +
+ Report Bug + · + Request Feature +
+
+ angular.io +

+
+ + +
+ Table of Contents +
    +
  1. About The Project
  2. +
  3. Getting Started
  4. +
  5. Configuration
  6. +
  7. Running dev mode
  8. +
  9. Build docker image
  10. +
  11. Contributing
  12. +
  13. License
  14. +
  15. Contact
  16. +
+
+ + + +## About The Project + +[Eclipse Dataspace Components](https://github.com/eclipse-edc) is a framework +for building dataspaces, exchanging data securely with ensured data +sovereignity. + +[sovity](https://sovity.de/) extends the EDC functionality to offer +enterprise-ready managed "Connector-as-a-Service" services, bringing +out-of-the-box fully configured DAPS and integrations to existing other +dataspace technologies. + +Our extension of EDC DataDashboard functionalities has been made open source and +will be kept compatible to mostly stock EDCs with minimal API extending +extensions. + +

(back to top)

+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ + + +## Getting Started + +The fastest way to get started is using our Getting Started Guide: +[sovity EDC CE Getting Started Guide](https://github.com/sovity/edc-ce#getting-started). + +

(back to top)

+ + + +## Configuration + +A list of all available configuration properties can be found +[here](src/app/core/config/app-config-properties.ts). + +In general, all ENV vars `EDC_UI_*` are written to an `assets/app-config.json`, +either before starting the angular build server or before starting the nginx to +serve static files. + +### (Optional) Pass a JSON in an ENV Var + +The ENV var `EDC_UI_CONFIG_JSON` can be used to pass a JSON that can contain all +properties that would otherwise need to be specified individually. Individually +provided ENV vars take precedence, however. + +

(back to top)

+ +### (Optional) Configuring the NGINX + +```yaml +# Customizable ENV Vars and their defaults +NGINX_BIND: '0.0.0.0' +NGINX_PORT: '8080' +NGINX_ACCESS_LOG: '/dev/stdout' +NGINX_ERROR_LOG: '/dev/stderr' +``` + + + +## Running dev mode + +Requires Node.js version `^16.10.0`. + +```shell +# Fake backend +(cd fake-backend && npm i && npm run start) + +# Run Angular Application +npm i +npm run start +``` + +### Configuring Dev Mode + +For dev mode ENV vars are read from: + +- Current Environment Variables (highest precedence) +- `.env` file (not committed, in .gitignore) +- `.env.local-dev` file (defaults for working with fake backend). + +```properties +# Example: +# Create a .env file to easily switch between profiles +EDC_UI_ACTIVE_PROFILE=mds-open-source +``` + +

(back to top)

+ + + +## Build docker image + +Requires docker. + +```shell +# Build docker image +docker build -f "docker/Dockerfile" -t "edc-ui:latest" . + +# Docker image will serve at :80 +``` + +

(back to top)

+ + + +## Contributing + +Contributions are what make the open source community such an amazing place to +learn, inspire, and create. Any contributions you make are **greatly +appreciated**. + +If you have a suggestion that would make this better, please fork the repo and +create a pull request. You can also simply open an issue with the tag +"enhancement". Don't forget to give the project a star! Thanks again! + +1. Fork the Project +2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`) +3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`) +4. Push to the Branch (`git push origin feature/AmazingFeature`) +5. Open a Pull Request + +Our contribution guide can be found in [CONTRIBUTING.md](CONTRIBUTING.md). + +

(back to top)

+ + + +## License + +Distributed under the Apache 2.0 License. See `LICENSE` for more information. + +

(back to top)

+ + + +## Contact + +contact@sovity.de + +

(back to top)

+ + + + +[contributors-shield]: + https://img.shields.io/github/contributors/sovity/edc-ui.svg?style=for-the-badge +[contributors-url]: https://github.com/sovity/edc-ui/graphs/contributors +[forks-shield]: + https://img.shields.io/github/forks/sovity/edc-ui.svg?style=for-the-badge +[forks-url]: https://github.com/sovity/edc-ui/network/members +[stars-shield]: + https://img.shields.io/github/stars/sovity/edc-ui.svg?style=for-the-badge +[stars-url]: https://github.com/sovity/edc-ui/stargazers +[issues-shield]: + https://img.shields.io/github/issues/sovity/edc-ui.svg?style=for-the-badge +[issues-url]: https://github.com/sovity/edc-ui/issues +[license-shield]: + https://img.shields.io/github/license/sovity/edc-ui.svg?style=for-the-badge +[license-url]: https://github.com/sovity/edc-ui/blob/master/LICENSE.txt +[linkedin-shield]: + https://img.shields.io/badge/-LinkedIn-black.svg?style=for-the-badge&logo=linkedin&colorB=555 +[linkedin-url]: https://www.linkedin.com/company/sovity +[Angular.io]: + https://img.shields.io/badge/Angular-DD0031?style=for-the-badge&logo=angular&logoColor=white +[Angular-url]: https://angular.io/ diff --git a/connector-ui/SECURITY.md b/connector-ui/SECURITY.md new file mode 100644 index 000000000..5b81c2e4d --- /dev/null +++ b/connector-ui/SECURITY.md @@ -0,0 +1,37 @@ +## Security + +sovity GmbH takes the security of its software products and services seriously, +which includes all source code repositories managed through our GitHub +organization: [sovity](https://github.com/sovity). + +If you believe you have found a security vulnerability in any of sovity's owned +repositories, please report it to us as described below. + +## Reporting Security Issues + +**Please do not report security vulnerabilities through public GitHub issues.** + +Instead, please report them via mail: +[security@sovity.de](mailto:security@sovity.de) + +You should receive a response within 24 hours. If for some reason you do not, +please follow up via email to ensure we received your original message. + +Please include the requested information listed below (as much as you can +provide) to help us better understand the nature and scope of the possible +issue: + +- Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, + etc.) +- Full paths of source file(s) related to the manifestation of the issue +- The location of the affected source code (tag/branch/commit or direct URL) +- Any special configuration required to reproduce the issue +- Step-by-step instructions to reproduce the issue +- Proof-of-concept or exploit code (if possible) +- Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +## Preferred Languages + +We prefer all communications to be in English. diff --git a/connector-ui/STYLEGUIDE.md b/connector-ui/STYLEGUIDE.md new file mode 100644 index 000000000..b394585ef --- /dev/null +++ b/connector-ui/STYLEGUIDE.md @@ -0,0 +1,10 @@ +# Code Style Guide + +- We use prettier as code formatter. Check for code formatting issues with + `npm run format-all`. +- Angular code follows + [Angular Style Guide](https://angular.io/guide/styleguide). +- Reviewers will review for + [Clean Code principles](https://x-team.com/blog/principles-clean-code/#clean-code-principles) +- [SOLID](https://en.wikipedia.org/wiki/SOLID) ensure consistent, composable and + re-usable intra-project APIs. diff --git a/connector-ui/angular.json b/connector-ui/angular.json new file mode 100644 index 000000000..d48360c8f --- /dev/null +++ b/connector-ui/angular.json @@ -0,0 +1,113 @@ +{ + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "version": 1, + "newProjectRoot": "projects", + "projects": { + "edc-demo-client": { + "projectType": "application", + "schematics": { + "@schematics/angular:component": { + "style": "scss" + }, + "@schematics/angular:application": { + "strict": true + } + }, + "root": "", + "sourceRoot": "src", + "prefix": "app", + "architect": { + "build": { + "builder": "ngx-build-plus:browser", + "options": { + "extraWebpackConfig": "webpack.config.js", + "outputPath": "dist/edc-demo-client", + "index": "src/index.html", + "main": "src/main.ts", + "polyfills": "src/polyfills.ts", + "tsConfig": "tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": ["src/favicon.ico", "src/assets"], + "styles": ["src/styles.scss"], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "2.0mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.prod.ts" + } + ], + "outputHashing": "all", + "sourceMap": { + "hidden": false, + "scripts": true, + "styles": true + } + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "builder": "ngx-build-plus:dev-server", + "configurations": { + "production": { + "browserTarget": "edc-demo-client:build:production" + }, + "development": { + "browserTarget": "edc-demo-client:build:development" + } + }, + "options": { + "extraWebpackConfig": "webpack.config.js", + "browserTarget": "project-name:build" + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "builder": "@angular-devkit/build-angular:extract-i18n", + "options": { + "browserTarget": "edc-demo-client:build" + } + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "main": "src/test.ts", + "polyfills": "src/polyfills.ts", + "tsConfig": "tsconfig.spec.json", + "karmaConfig": "karma.conf.js", + "inlineStyleLanguage": "scss", + "assets": ["src/favicon.ico", "src/assets"], + "styles": ["src/styles.scss"], + "scripts": [] + } + } + } + } + }, + "cli": { + "analytics": false + } +} diff --git a/connector-ui/config-generator.js b/connector-ui/config-generator.js new file mode 100644 index 000000000..8d6e1dadb --- /dev/null +++ b/connector-ui/config-generator.js @@ -0,0 +1,55 @@ +const {writeFileSync, existsSync, readFileSync} = require('fs'); +const dotenv = require('dotenv'); + +// Generate app-config.json from ENV Vars +// Priority: ENV VAR > .env > .env.local-dev +// Usage: node ./config-generator.js + +// app-config.json in production is not generated by this script + +/** + * Reads given .env file + * + * @param path path to .env file + * @return vars (Record) + */ +const readEnvFileSync = (path) => { + if (existsSync(path)) { + return dotenv.parse(readFileSync(path)); + } + return {}; +}; + +/** + * Filter object properties by applying filter fn to each key. + * + * @param obj any object + * @param fn filter fn (applied to property name) + * @return subset of obj + */ +const objFilterKeys = (obj, fn) => + Object.fromEntries(Object.entries(obj).filter(([k, _]) => fn(k))); + +// Read ENV Vars from .env files as well +const allProps = { + ...readEnvFileSync('.env.local-dev'), + ...readEnvFileSync('.env'), + ...process.env, +}; + +// Collect ENV Vars with prefix EDC_UI_ +const prefix = 'EDC_UI_'; +const filteredProps = objFilterKeys(allProps, (k) => k.startsWith(prefix)); +if (!Object.keys(filteredProps).length) { + console.warn( + `No ${prefix} configuration properties are set in ENV, application might not be configured properly.`, + ); +} + +// Write app-config.json +const output = './src/assets/config/app-configuration.json'; +const json = JSON.stringify(filteredProps); +writeFileSync(output, json); + +// It is ok to log this config as the data will be available in all client browsers +console.log(`Writing app.configuration.json to ${output}: ${json}`); diff --git a/connector-ui/docker/99-generate-app-config.sh b/connector-ui/docker/99-generate-app-config.sh new file mode 100644 index 000000000..040c256ff --- /dev/null +++ b/connector-ui/docker/99-generate-app-config.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +set -e + +jq -n 'env | with_entries( select(.key | startswith("EDC_UI_") ) )' > /tmp/app-config.json diff --git a/connector-ui/docker/Dockerfile b/connector-ui/docker/Dockerfile new file mode 100644 index 000000000..8642d6426 --- /dev/null +++ b/connector-ui/docker/Dockerfile @@ -0,0 +1,44 @@ +# Stage 1: Install node modules +FROM docker.io/library/node:lts as npm-install + +WORKDIR /app +COPY ./package*.json /app/ +RUN npm install + +# Stage 2: Build Project +FROM docker.io/library/node:lts as build + +WORKDIR /app +COPY --from=npm-install /app/node_modules /app/node_modules +COPY ./ /app/ +RUN npm run ng build --no-progress --configuration=production + +# Stage 3: Serve app with nginx +FROM docker.io/nginxinc/nginx-unprivileged:1.27-alpine3.20 + +# Temporarily switch to root to install packages and create symlink in restricted location +USER root +RUN apk add --no-cache jq curl + +COPY --from=build /app/dist/edc-demo-client /usr/share/nginx/html +COPY --from=build /app/src/assets /usr/share/nginx/html/assets +COPY docker/default.conf.template etc/nginx/templates/default.conf.template +# Before starting nginx, apply ENV vars to create app-config.json from EDC_UI_* ENV Vars +# Use an entrypoint drop-in instead of modifying the default entrypoint or command, +# so that the automatic envsubst templating is not disabled. +COPY docker/99-generate-app-config.sh /docker-entrypoint.d/99-generate-app-config.sh + +RUN ln -sf /tmp/app-config.json /usr/share/nginx/html/assets/config/app-configuration.json \ + # Nginx is configured to reject symlinks that point to a file owned by a different user, for security reasons + && chown --no-dereference nginx:root /usr/share/nginx/html/assets/config/app-configuration.json + +# Switch back to unprivileged user for runtime +USER nginx:nginx + +ENV NGINX_BIND="0.0.0.0" +ENV NGINX_PORT=8080 +ENV NGINX_ACCESS_LOG=/dev/stdout +ENV NGINX_ERROR_LOG=/dev/stderr + +HEALTHCHECK --interval=2s --timeout=5s --retries=10 \ + CMD curl -f http://$NGINX_BIND:$NGINX_PORT/ || exit 1 diff --git a/connector-ui/docker/default.conf.template b/connector-ui/docker/default.conf.template new file mode 100644 index 000000000..605cb15fe --- /dev/null +++ b/connector-ui/docker/default.conf.template @@ -0,0 +1,28 @@ +access_log ${NGINX_ACCESS_LOG}; +error_log ${NGINX_ERROR_LOG}; +disable_symlinks if_not_owner; +server_tokens off; + +server { + listen ${NGINX_BIND}:${NGINX_PORT}; + server_name localhost; + + root /usr/share/nginx/html; + + location / { + index do-not-use-me.html; + try_files $uri @index; + } + + location @index { + add_header Cache-Control no-cache; + expires 0; + index index.html; + try_files /index.html =404; + } + + location ~* ^/assets/config/app-configuration\.json$ { + add_header Cache-Control "no-store, no-cache, must-revalidate"; + expires -1; + } +} diff --git a/connector-ui/docs/dev/changelog_updates.md b/connector-ui/docs/dev/changelog_updates.md new file mode 100644 index 000000000..8892e534a --- /dev/null +++ b/connector-ui/docs/dev/changelog_updates.md @@ -0,0 +1,67 @@ +Updating the Changelog +====================== + +This project uses a [CHANGELOG.md](../../CHANGELOG.md). + +## Structure of the Changelog + +Each pull request should also update the "Unreleased" section of the changelog. +It should also update the "Deployment Migration Notes" Section of the unreleased section as preparation for the release. + +For each release there will be a separate section especially with an "Overview" section containing a summary +from a product perspective. + +Releases will especially contain a "Compatible Versions" section with the final docker +images and versions of other software components that are connected by APIs. + +## How to categorize a change + +The changelog uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +Changes are categorized as either Major, Minor or Patch Changes. + +For this project, changes are categorized as the following: + +### Major Changes + +Major changes include: + +- UX / Product overhauls. +- Breaking changes in Connector-To-Connector communication. +- Breaking changes to the required deployment units (our UI doesn't count). +- Breaking changes in our API Wrapper Use Case API. + +### Minor Changes + +Minor changes include: + +- Any changes from a product perspective to our UI or API Wrapper UI API. +- Additions to our API Wrapper Use Case API. +- New APIs with API contracts with other deployment units (our UI doesn't count). +- New Product Documentation + +### Patch Changes + +Patch changes are basically everything else, that does not add, change or remove any product or external API features. + +- Product Fixes, Bugfixes, Refactorings +- Changes to existing Product Documentation +- New or changes to Developer Documentation +- Everything else + +## Released Versions + +On releases the "Unreleased" section is emptied in favor of a new section for the release. + +Whether a release will bump the major, minor or patch version is decided by the unreleased changes in the changelog. + +The Release sections will be cleaned up on release, improved with additional information and made +useful for the customer and people deploying the application, containing both product changes and +deployment migration notes. + +More on that can be found in the [Release Issue Template](../../.github/ISSUE_TEMPLATE/release.md). + +## Linking + +The entries in the changelog should be linked to their corresponding issue. + +If for some reason the issue is not accessible for the public, link to the pull request instead. diff --git a/connector-ui/docs/screenshots/screenshot-assets.png b/connector-ui/docs/screenshots/screenshot-assets.png new file mode 100644 index 000000000..f8bee909f Binary files /dev/null and b/connector-ui/docs/screenshots/screenshot-assets.png differ diff --git a/connector-ui/docs/screenshots/screenshot-contracts.png b/connector-ui/docs/screenshots/screenshot-contracts.png new file mode 100644 index 000000000..a56d68d7a Binary files /dev/null and b/connector-ui/docs/screenshots/screenshot-contracts.png differ diff --git a/connector-ui/docs/screenshots/screenshot-dashboard.png b/connector-ui/docs/screenshots/screenshot-dashboard.png new file mode 100644 index 000000000..6956c16c7 Binary files /dev/null and b/connector-ui/docs/screenshots/screenshot-dashboard.png differ diff --git a/connector-ui/karma.conf.js b/connector-ui/karma.conf.js new file mode 100644 index 000000000..d76edf170 --- /dev/null +++ b/connector-ui/karma.conf.js @@ -0,0 +1,41 @@ +// Karma configuration file, see link for more information +// https://karma-runner.github.io/1.0/config/configuration-file.html + +module.exports = function (config) { + config.set({ + basePath: '', + frameworks: ['jasmine', '@angular-devkit/build-angular'], + plugins: [ + require('karma-jasmine'), + require('karma-chrome-launcher'), + require('karma-jasmine-html-reporter'), + require('karma-coverage'), + require('@angular-devkit/build-angular/plugins/karma'), + ], + client: { + jasmine: { + // you can add configuration options for Jasmine here + // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html + // for example, you can disable the random execution with `random: false` + // or set a specific seed with `seed: 4321` + }, + clearContext: false, // leave Jasmine Spec Runner output visible in browser + }, + jasmineHtmlReporter: { + suppressAll: true, // removes the duplicated traces + }, + coverageReporter: { + dir: require('path').join(__dirname, './coverage/edc-demo-client'), + subdir: '.', + reporters: [{type: 'html'}, {type: 'text-summary'}], + }, + reporters: ['progress', 'kjhtml'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + singleRun: false, + restartOnFileChange: true, + }); +}; diff --git a/connector-ui/openapi/openapi.yaml b/connector-ui/openapi/openapi.yaml new file mode 100644 index 000000000..c849a3ead --- /dev/null +++ b/connector-ui/openapi/openapi.yaml @@ -0,0 +1,1633 @@ +openapi: 3.0.1 +info: + title: EDC REST API + description: All files merged by open api merger + license: + name: Apache License v2.0 + url: http://apache.org/v2 + version: 1.0.0-SNAPSHOT +servers: + - url: / +paths: + /identity-hub/collections: + post: + tags: + - Identity Hub + operationId: write + requestBody: + content: + application/json: + schema: + type: string + responses: + default: + description: default response + content: + application/json: + schema: + type: string + /identity-hub/collections-commit: + post: + tags: + - Identity Hub + operationId: writeCommit + requestBody: + content: + application/json: + schema: + type: object + additionalProperties: + type: string + responses: + default: + description: default response + content: + application/json: {} + /identity-hub/query-commits: + post: + tags: + - Identity Hub + operationId: queryCommits + requestBody: + content: + application/json: + schema: + type: string + responses: + default: + description: default response + content: + application/json: + schema: + type: string + /identity-hub/query-objects: + post: + tags: + - Identity Hub + operationId: queryObjects + requestBody: + content: + application/json: + schema: + type: string + responses: + default: + description: default response + content: + application/json: + schema: + type: string + /instances: + get: + tags: + - Dataplane Selector + operationId: getAll + responses: + default: + description: default response + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/DataPlaneInstance' + post: + tags: + - Dataplane Selector + operationId: addEntry + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/DataPlaneInstance' + responses: + default: + description: default response + content: + application/json: {} + /instances/select: + post: + tags: + - Dataplane Selector + operationId: find + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/SelectionRequest' + responses: + default: + description: default response + content: + application/json: + schema: + $ref: '#/components/schemas/DataPlaneInstance' + /catalog: + get: + tags: + - Catalog + operationId: getCatalog + parameters: + - name: providerUrl + in: query + required: false + style: form + explode: true + schema: + type: string + responses: + default: + description: Gets contract offers (=catalog) of a single connector + content: + application/json: + schema: + $ref: '#/components/schemas/Catalog' + /federatedcatalog: + post: + operationId: getCachedCatalog + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/FederatedCatalogCacheQuery' + responses: + default: + description: default response + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ContractOffer' + /check/health: + get: + tags: + - Application Observability + description: Performs a liveness probe to determine whether the runtime is working + properly. + operationId: checkHealth + responses: + "200": + description: + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/HealthStatus' + /check/liveness: + get: + tags: + - Application Observability + description: Performs a liveness probe to determine whether the runtime is working + properly. + operationId: getLiveness + responses: + "200": + description: + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/HealthStatus' + /check/readiness: + get: + tags: + - Application Observability + description: Performs a readiness probe to determine whether the runtime is + able to accept requests. + operationId: getReadiness + responses: + "200": + description: + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/HealthStatus' + /check/startup: + get: + tags: + - Application Observability + description: Performs a startup probe to determine whether the runtime has completed + startup. + operationId: getStartup + responses: + "200": + description: + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/HealthStatus' + /callback/{processId}/deprovision: + post: + tags: + - HTTP Provisioner Webhook + operationId: callDeprovisionWebhook + parameters: + - name: processId + in: path + required: true + style: simple + explode: false + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/DeprovisionedResource' + responses: + default: + description: default response + content: + application/json: {} + /callback/{processId}/provision: + post: + tags: + - HTTP Provisioner Webhook + operationId: callProvisionWebhook + parameters: + - name: processId + in: path + required: true + style: simple + explode: false + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/ProvisionerWebhookRequest' + responses: + default: + description: default response + content: + application/json: {} + /contractagreements: + get: + tags: + - Contract Agreement + description: Gets all contract agreements according to a particular query + operationId: getAllAgreements + parameters: + - name: offset + in: query + required: false + style: form + explode: true + schema: + type: integer + format: int32 + - name: limit + in: query + required: false + style: form + explode: true + schema: + type: integer + format: int32 + - name: filter + in: query + required: false + style: form + explode: true + schema: + type: string + - name: sort + in: query + required: false + style: form + explode: true + schema: + type: string + enum: + - ASC + - DESC + - name: sortField + in: query + required: false + style: form + explode: true + schema: + type: string + responses: + "200": + description: + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ContractAgreementDto' + "400": + description: Request body was malformed + /contractagreements/{id}: + get: + tags: + - Contract Agreement + description: Gets an contract agreement with the given ID + operationId: getContractAgreement + parameters: + - name: id + in: path + required: true + style: simple + explode: false + schema: + type: string + responses: + "200": + description: The contract agreement + content: + application/json: + schema: + $ref: '#/components/schemas/ContractAgreementDto' + "400": + description: "Request was malformed, e.g. id was null" + "404": + description: An contract agreement with the given ID does not exist + /assets: + get: + tags: + - Asset + description: Gets all assets according to a particular query + operationId: getAllAssets + parameters: + - name: offset + in: query + required: false + style: form + explode: true + schema: + type: integer + format: int32 + - name: limit + in: query + required: false + style: form + explode: true + schema: + type: integer + format: int32 + - name: filter + in: query + required: false + style: form + explode: true + schema: + type: string + - name: sort + in: query + required: false + style: form + explode: true + schema: + type: string + enum: + - ASC + - DESC + - name: sortField + in: query + required: false + style: form + explode: true + schema: + type: string + responses: + "200": + description: + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/AssetDto' + "400": + description: Request body was malformed + post: + tags: + - Asset + description: Creates a new asset together with a data address + operationId: createAsset + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/AssetEntryDto' + responses: + "200": + description: Asset was created successfully + "400": + description: Request body was malformed + "409": + description: "Could not create asset, because an asset with that ID already\ + \ exists" + /assets/{id}: + get: + tags: + - Asset + description: Gets an asset with the given ID + operationId: getAsset + parameters: + - name: id + in: path + required: true + style: simple + explode: false + schema: + type: string + responses: + "200": + description: The asset + content: + application/json: + schema: + $ref: '#/components/schemas/AssetDto' + "400": + description: "Request was malformed, e.g. id was null" + "404": + description: An asset with the given ID does not exist + delete: + tags: + - Asset + description: "Removes an asset with the given ID if possible. Deleting an asset\ + \ is only possible if that asset is not yet referenced by a contract agreement,\ + \ in which case an error is returned. DANGER ZONE: Note that deleting assets\ + \ can have unexpected results, especially for contract offers that have been\ + \ sent out or ongoing or contract negotiations." + operationId: removeAsset + parameters: + - name: id + in: path + required: true + style: simple + explode: false + schema: + type: string + responses: + "200": + description: Asset was deleted successfully + "400": + description: "Request was malformed, e.g. id was null" + "404": + description: An asset with the given ID does not exist + "409": + description: "The asset cannot be deleted, because it is referenced by a\ + \ contract agreement" + /contractdefinitions: + get: + tags: + - Contract Definition + description: Returns all contract definitions according to a query + operationId: getAllContractDefinitions + parameters: + - name: offset + in: query + required: false + style: form + explode: true + schema: + type: integer + format: int32 + - name: limit + in: query + required: false + style: form + explode: true + schema: + type: integer + format: int32 + - name: filter + in: query + required: false + style: form + explode: true + schema: + type: string + - name: sort + in: query + required: false + style: form + explode: true + schema: + type: string + enum: + - ASC + - DESC + - name: sortField + in: query + required: false + style: form + explode: true + schema: + type: string + responses: + "200": + description: + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ContractDefinitionDto' + "400": + description: Request was malformed + post: + tags: + - Contract Definition + description: Creates a new contract definition + operationId: createContractDefinition + requestBody: + content: + '*/*': + schema: + $ref: '#/components/schemas/ContractDefinitionDto' + responses: + "200": + description: contract definition was created successfully + "400": + description: Request body was malformed + "409": + description: "Could not create contract definition, because a contract definition\ + \ with that ID already exists" + /contractdefinitions/{id}: + get: + tags: + - Contract Definition + description: Gets an contract definition with the given ID + operationId: getContractDefinition + parameters: + - name: id + in: path + required: true + style: simple + explode: false + schema: + type: string + responses: + "200": + description: The contract definition + content: + application/json: + schema: + $ref: '#/components/schemas/ContractDefinitionDto' + "400": + description: "Request was malformed, e.g. id was null" + "404": + description: An contract agreement with the given ID does not exist + delete: + tags: + - Contract Definition + description: "Removes a contract definition with the given ID if possible. DANGER\ + \ ZONE: Note that deleting contract definitions can have unexpected results,\ + \ especially for contract offers that have been sent out or ongoing or contract\ + \ negotiations." + operationId: deleteContractDefinition + parameters: + - name: id + in: path + required: true + style: simple + explode: false + schema: + type: string + responses: + "200": + description: Contract definition was deleted successfully + "400": + description: "Request was malformed, e.g. id was null" + "404": + description: A contract definition with the given ID does not exist + /contractnegotiations: + get: + tags: + - Contract Negotiation + description: Returns all contract negotiations according to a query + operationId: getNegotiations + parameters: + - name: offset + in: query + required: false + style: form + explode: true + schema: + type: integer + format: int32 + - name: limit + in: query + required: false + style: form + explode: true + schema: + type: integer + format: int32 + - name: filter + in: query + required: false + style: form + explode: true + schema: + type: string + - name: sort + in: query + required: false + style: form + explode: true + schema: + type: string + enum: + - ASC + - DESC + - name: sortField + in: query + required: false + style: form + explode: true + schema: + type: string + responses: + "200": + description: + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/ContractNegotiationDto' + "400": + description: Request was malformed + post: + tags: + - Contract Negotiation + description: "Initiates a contract negotiation for a given offer and with the\ + \ given counter part. Please note that successfully invoking this endpoint\ + \ only means that the negotiation was initiated. Clients must poll the /{id}/state\ + \ endpoint to track the state" + operationId: initiateContractNegotiation + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/NegotiationInitiateRequestDto' + responses: + "200": + description: The negotiation was successfully initiated. Returns the contract + negotiation ID + content: + application/json: + schema: + $ref: '#/components/schemas/NegotiationId' + links: + poll-state: + operationId: getNegotiationState + parameters: + id: $response.body#/id + "400": + description: Request body was malformed + /contractnegotiations/{id}: + get: + tags: + - Contract Negotiation + description: Gets an contract negotiation with the given ID + operationId: getNegotiation + parameters: + - name: id + in: path + required: true + style: simple + explode: false + schema: + type: string + responses: + "200": + description: The contract negotiation + content: + application/json: + schema: + $ref: '#/components/schemas/ContractNegotiationDto' + "400": + description: "Request was malformed, e.g. id was null" + "404": + description: An contract negotiation with the given ID does not exist + /contractnegotiations/{id}/agreement: + get: + tags: + - Contract Negotiation + description: Gets a contract agreement for a contract negotiation with the given + ID + operationId: getAgreementForNegotiation + parameters: + - name: id + in: path + required: true + style: simple + explode: false + schema: + type: string + responses: + "200": + description: "The contract agreement that is attached to the negotiation,\ + \ or null" + content: + application/json: + schema: + $ref: '#/components/schemas/ContractNegotiationDto' + "400": + description: "Request was malformed, e.g. id was null" + "404": + description: An contract negotiation with the given ID does not exist + /contractnegotiations/{id}/cancel: + post: + tags: + - Contract Negotiation + description: "Requests aborting the contract negotiation. Due to the asynchronous\ + \ nature of contract negotiations, a successful response only indicates that\ + \ the request was successfully received. Clients must poll the /{id}/state\ + \ endpoint to track the state." + operationId: cancelNegotiation + parameters: + - name: id + in: path + required: true + style: simple + explode: false + schema: + type: string + responses: + "200": + description: Request to cancel the Contract negotiation was successfully + received + links: + poll-state: + operationId: getNegotiationState + "400": + description: "Request was malformed, e.g. id was null" + "404": + description: A contract negotiation with the given ID does not exist + /contractnegotiations/{id}/decline: + post: + tags: + - Contract Negotiation + description: "Requests cancelling the contract negotiation. Due to the asynchronous\ + \ nature of contract negotiations, a successful response only indicates that\ + \ the request was successfully received. Clients must poll the /{id}/state\ + \ endpoint to track the state." + operationId: declineNegotiation + parameters: + - name: id + in: path + required: true + style: simple + explode: false + schema: + type: string + responses: + "200": + description: Request to decline the Contract negotiation was successfully + received + links: + poll-state: + operationId: getNegotiationState + "400": + description: "Request was malformed, e.g. id was null" + "404": + description: A contract negotiation with the given ID does not exist + /contractnegotiations/{id}/state: + get: + tags: + - Contract Negotiation + description: Gets the state of a contract negotiation with the given ID + operationId: getNegotiationState + parameters: + - name: id + in: path + required: true + style: simple + explode: false + schema: + type: string + responses: + "200": + description: The contract negotiation's state + content: + application/json: + schema: + $ref: '#/components/schemas/NegotiationState' + "400": + description: "Request was malformed, e.g. id was null" + "404": + description: An contract negotiation with the given ID does not exist + /policydefinitions: + get: + tags: + - Policy + description: Returns all policy definitions according to a query + operationId: getAllPolicies + parameters: + - name: offset + in: query + required: false + style: form + explode: true + schema: + type: integer + format: int32 + - name: limit + in: query + required: false + style: form + explode: true + schema: + type: integer + format: int32 + - name: filter + in: query + required: false + style: form + explode: true + schema: + type: string + - name: sort + in: query + required: false + style: form + explode: true + schema: + type: string + enum: + - ASC + - DESC + - name: sortField + in: query + required: false + style: form + explode: true + schema: + type: string + responses: + "200": + description: default description + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/PolicyDefinition' + "400": + description: Request was malformed + post: + tags: + - Policy + description: Creates a new policy definition + operationId: createPolicy + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/PolicyDefinition' + responses: + "200": + description: policy definition was created successfully + "400": + description: Request body was malformed + "409": + description: "Could not create policy definition, because a contract definition\ + \ with that ID already exists" + /policydefinitions/{id}: + get: + tags: + - Policy + description: Gets a policy definition with the given ID + operationId: getPolicy + parameters: + - name: id + in: path + required: true + style: simple + explode: false + schema: + type: string + responses: + "200": + description: The policy definition + content: + application/json: + schema: + $ref: '#/components/schemas/PolicyDefinition' + "400": + description: "Request was malformed, e.g. id was null" + "404": + description: An policy definition with the given ID does not exist + delete: + tags: + - Policy + description: "Removes a policy definition with the given ID if possible. Deleting\ + \ a policy definition is only possible if that policy definition is not yet\ + \ referenced by a contract definition, in which case an error is returned.\ + \ DANGER ZONE: Note that deleting policy definitions can have unexpected results,\ + \ do this at your own risk!" + operationId: deletePolicy + parameters: + - name: id + in: path + required: true + style: simple + explode: false + schema: + type: string + responses: + "200": + description: Policy definition was deleted successfully + "400": + description: "Request was malformed, e.g. id was null" + "404": + description: An policy definition with the given ID does not exist + "409": + description: "The policy definition cannot be deleted, because it is referenced\ + \ by a contract definition" + /transferprocess: + get: + tags: + - Transfer Process + description: Returns all transfer process according to a query + operationId: getAllTransferProcesses + parameters: + - name: offset + in: query + required: false + style: form + explode: true + schema: + type: integer + format: int32 + - name: limit + in: query + required: false + style: form + explode: true + schema: + type: integer + format: int32 + - name: filter + in: query + required: false + style: form + explode: true + schema: + type: string + - name: sort + in: query + required: false + style: form + explode: true + schema: + type: string + enum: + - ASC + - DESC + - name: sortField + in: query + required: false + style: form + explode: true + schema: + type: string + responses: + "200": + description: + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/TransferProcessDto' + "400": + description: Request was malformed + post: + tags: + - Transfer Process + description: "Initiates a data transfer with the given parameters. Please note\ + \ that successfully invoking this endpoint only means that the transfer was\ + \ initiated. Clients must poll the /{id}/state endpoint to track the state" + operationId: initiateTransfer + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/TransferRequestDto' + responses: + "200": + description: The transfer was successfully initiated. Returns the transfer + process ID + content: + application/json: + schema: + $ref: '#/components/schemas/TransferId' + links: + poll-state: + operationId: getTransferProcessState + parameters: + id: $response.body#/id + "400": + description: Request body was malformed + /transferprocess/{id}: + get: + tags: + - Transfer Process + description: Gets an transfer process with the given ID + operationId: getTransferProcess + parameters: + - name: id + in: path + required: true + style: simple + explode: false + schema: + type: string + responses: + "200": + description: The transfer process + content: + application/json: + schema: + $ref: '#/components/schemas/TransferProcessDto' + "400": + description: "Request was malformed, e.g. id was null" + "404": + description: A transfer process with the given ID does not exist + /transferprocess/{id}/cancel: + post: + tags: + - Transfer Process + description: "Requests aborting the transfer process. Due to the asynchronous\ + \ nature of transfers, a successful response only indicates that the request\ + \ was successfully received. Clients must poll the /{id}/state endpoint to\ + \ track the state." + operationId: cancelTransferProcess + parameters: + - name: id + in: path + required: true + style: simple + explode: false + schema: + type: string + responses: + "200": + description: Request to cancel the transfer process was successfully received + links: + poll-state: + operationId: getTransferProcessState + "400": + description: "Request was malformed, e.g. id was null" + "404": + description: A contract negotiation with the given ID does not exist + /transferprocess/{id}/deprovision: + post: + tags: + - Transfer Process + description: "Requests the deprovisioning of resources associated with a transfer\ + \ process. Due to the asynchronous nature of transfers, a successful response\ + \ only indicates that the request was successfully received. This may take\ + \ a long time, so clients must poll the /{id}/state endpoint to track the\ + \ state." + operationId: deprovisionTransferProcess + parameters: + - name: id + in: path + required: true + style: simple + explode: false + schema: + type: string + responses: + "200": + description: Request to deprovision the transfer process was successfully + received + links: + poll-state: + operationId: getTransferProcessState + "400": + description: "Request was malformed, e.g. id was null" + "404": + description: A contract negotiation with the given ID does not exist + /transferprocess/{id}/state: + get: + tags: + - Transfer Process + description: Gets the state of a transfer process with the given ID + operationId: getTransferProcessState + parameters: + - name: id + in: path + required: true + style: simple + explode: false + schema: + type: string + responses: + "200": + description: The transfer process's state + content: + application/json: + schema: + $ref: '#/components/schemas/TransferState' + "400": + description: "Request was malformed, e.g. id was null" + "404": + description: An transfer process with the given ID does not exist +components: + schemas: + Action: + type: object + properties: + constraint: + $ref: '#/components/schemas/Constraint' + includedIn: + type: string + type: + type: string + Asset: + type: object + properties: + properties: + type: object + additionalProperties: + type: object + AssetDto: + required: + - properties + type: object + properties: + properties: + type: object + additionalProperties: + type: object + AssetEntryDto: + required: + - asset + - dataAddress + type: object + properties: + asset: + $ref: '#/components/schemas/AssetDto' + dataAddress: + $ref: '#/components/schemas/DataAddressDto' + Catalog: + type: object + properties: + contractOffers: + type: array + items: + $ref: '#/components/schemas/ContractOffer' + id: + type: string + Constraint: + required: + - edctype + type: object + properties: + edctype: + type: string + discriminator: + propertyName: edctype + ContractAgreementDto: + required: + - assetId + - consumerAgentId + - id + - policy + - providerAgentId + type: object + properties: + assetId: + type: string + consumerAgentId: + type: string + contractEndDate: + type: integer + format: int64 + contractSigningDate: + type: integer + format: int64 + contractStartDate: + type: integer + format: int64 + id: + type: string + policy: + $ref: '#/components/schemas/Policy' + providerAgentId: + type: string + ContractDefinitionDto: + required: + - accessPolicyId + - contractPolicyId + - criteria + - id + type: object + properties: + accessPolicyId: + type: string + contractPolicyId: + type: string + criteria: + type: array + items: + $ref: '#/components/schemas/Criterion' + id: + type: string + ContractNegotiationDto: + type: object + properties: + contractAgreementId: + type: string + counterPartyAddress: + type: string + errorDetail: + type: string + id: + type: string + protocol: + type: string + state: + type: string + type: + type: string + enum: + - CONSUMER + - PROVIDER + ContractOffer: + type: object + properties: + asset: + $ref: '#/components/schemas/Asset' + assetId: + type: string + consumer: + type: string + format: uri + contractEnd: + type: string + format: date-time + contractStart: + type: string + format: date-time + id: + type: string + offerEnd: + type: string + format: date-time + offerStart: + type: string + format: date-time + policy: + $ref: '#/components/schemas/Policy' + provider: + type: string + format: uri + ContractOfferDescription: + required: + - assetId + - offerId + - policy + type: object + properties: + assetId: + type: string + offerId: + type: string + policy: + $ref: '#/components/schemas/Policy' + Criterion: + type: object + properties: + operandLeft: + type: object + operator: + type: string + operandRight: + type: object + DataAddress: + type: object + properties: + properties: + type: object + additionalProperties: + type: string + DataAddressDto: + type: object + properties: + properties: + type: object + additionalProperties: + type: string + DataAddressInformationDto: + type: object + properties: + properties: + type: object + additionalProperties: + type: string + DataPlaneInstance: + type: object + properties: + id: + type: string + lastActive: + type: integer + format: int64 + properties: + type: object + additionalProperties: + type: object + turnCount: + type: integer + format: int32 + url: + type: string + format: url + DataRequestDto: + type: object + properties: + assetId: + type: string + connectorId: + type: string + contractId: + type: string + DeprovisionedResource: + type: object + properties: + error: + type: boolean + errorMessage: + type: string + inProcess: + type: boolean + provisionedResourceId: + type: string + Duty: + type: object + properties: + action: + $ref: '#/components/schemas/Action' + assignee: + type: string + assigner: + type: string + consequence: + $ref: '#/components/schemas/Duty' + constraints: + type: array + items: + $ref: '#/components/schemas/Constraint' + parentPermission: + $ref: '#/components/schemas/Permission' + target: + type: string + uid: + type: string + Failure: + type: object + properties: + messages: + type: array + items: + type: string + FederatedCatalogCacheQuery: + type: object + properties: + criteria: + type: array + items: + $ref: '#/components/schemas/Criterion' + HealthCheckResult: + type: object + properties: + component: + type: string + failure: + $ref: '#/components/schemas/Failure' + isHealthy: + type: boolean + HealthStatus: + type: object + properties: + componentResults: + type: array + items: + $ref: '#/components/schemas/HealthCheckResult' + isSystemHealthy: + type: boolean + NegotiationId: + type: object + properties: + id: + type: string + NegotiationInitiateRequestDto: + required: + - connectorAddress + - connectorId + - offer + - protocol + type: object + properties: + connectorAddress: + type: string + connectorId: + type: string + offer: + $ref: '#/components/schemas/ContractOfferDescription' + protocol: + type: string + NegotiationState: + type: object + properties: + state: + type: string + Permission: + type: object + properties: + action: + $ref: '#/components/schemas/Action' + assignee: + type: string + assigner: + type: string + constraints: + type: array + items: + $ref: '#/components/schemas/Constraint' + duties: + type: array + items: + $ref: '#/components/schemas/Duty' + target: + type: string + uid: + type: string + Policy: + type: object + properties: + '@type': + type: string + enum: + - SET + - OFFER + - CONTRACT + assignee: + type: string + assigner: + type: string + extensibleProperties: + type: object + additionalProperties: + type: object + inheritsFrom: + type: string + obligations: + type: array + items: + $ref: '#/components/schemas/Duty' + permissions: + type: array + items: + $ref: '#/components/schemas/Permission' + prohibitions: + type: array + items: + $ref: '#/components/schemas/Prohibition' + target: + type: string + PolicyDefinition: + type: object + properties: + policy: + $ref: '#/components/schemas/Policy' + id: + type: string + Prohibition: + type: object + properties: + action: + $ref: '#/components/schemas/Action' + assignee: + type: string + assigner: + type: string + constraints: + type: array + items: + $ref: '#/components/schemas/Constraint' + target: + type: string + uid: + type: string + ProvisionerWebhookRequest: + required: + - apiKeyJwt + - assetId + - contentDataAddress + - resourceDefinitionId + - resourceName + type: object + properties: + apiKeyJwt: + type: string + assetId: + type: string + contentDataAddress: + $ref: '#/components/schemas/DataAddress' + hasToken: + type: boolean + resourceDefinitionId: + type: string + resourceName: + type: string + SelectionRequest: + type: object + properties: + destination: + $ref: '#/components/schemas/DataAddress' + source: + $ref: '#/components/schemas/DataAddress' + strategy: + type: string + TransferId: + type: object + properties: + id: + type: string + TransferProcessDto: + type: object + properties: + createdTimestamp: + type: integer + format: int64 + dataDestination: + $ref: '#/components/schemas/DataAddressInformationDto' + dataRequest: + $ref: '#/components/schemas/DataRequestDto' + errorDetail: + type: string + id: + type: string + state: + type: string + stateTimestamp: + type: integer + format: int64 + type: + type: string + TransferRequestDto: + required: + - assetId + - connectorAddress + - connectorId + - contractId + - dataDestination + - protocol + - transferType + type: object + properties: + assetId: + type: string + connectorAddress: + type: string + connectorId: + type: string + contractId: + type: string + dataDestination: + $ref: '#/components/schemas/DataAddress' + id: + type: string + managedResources: + type: boolean + properties: + type: object + additionalProperties: + type: string + protocol: + type: string + transferType: + $ref: '#/components/schemas/TransferType' + TransferState: + type: object + properties: + state: + type: string + TransferType: + type: object + properties: + contentType: + type: string + isFinite: + type: boolean diff --git a/connector-ui/package-lock.json b/connector-ui/package-lock.json new file mode 100644 index 000000000..4c553d9c6 --- /dev/null +++ b/connector-ui/package-lock.json @@ -0,0 +1,24477 @@ +{ + "name": "sovity-edc-ui", + "version": "0.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "sovity-edc-ui", + "version": "0.0.0", + "license": "Apache 2.0", + "dependencies": { + "@angular/animations": "^14.3.0", + "@angular/cdk": "^14.2.7", + "@angular/common": "^14.3.0", + "@angular/compiler": "^14.3.0", + "@angular/core": "^14.3.0", + "@angular/flex-layout": "^14.0.0-beta.41", + "@angular/forms": "^14.3.0", + "@angular/material": "^14.2.7", + "@angular/platform-browser": "^14.3.0", + "@angular/platform-browser-dynamic": "^14.3.0", + "@angular/router": "^14.3.0", + "@ng-apimock/core": "^3.11.0", + "@ngx-translate/core": "^14.0.0", + "@ngx-translate/http-loader": "^7.0.0", + "@ngxs/store": "^3.8.1", + "@sovity.de/edc-client": "10.4.0", + "clean-deep": "^3.4.0", + "date-fns": "^2.30.0", + "date-fns-tz": "^2.0.1", + "dotenv": "^16.3.1", + "isomorphic-dompurify": "^2.0.0", + "json-stable-stringify": "^1.0.2", + "marked": "^11.1.1", + "ng2-charts": "^4.1.1", + "ngx-json-viewer": "^3.2.1", + "rxjs": "7.8.1", + "tslib": "^2.6.2", + "url-join": "^5.0.0", + "zone.js": "^0.12.0" + }, + "devDependencies": { + "@angular-devkit/build-angular": "^14.2.12", + "@angular/cli": "^14.2.13", + "@angular/compiler-cli": "^14.3.0", + "@tailwindcss/typography": "^0.5.10", + "@trivago/prettier-plugin-sort-imports": "^4.2.0", + "@types/dompurify": "^3.0.5", + "@types/jasmine": "^4.3.6", + "@types/node": "^18.17.1", + "jasmine-core": "^4.6.0", + "karma": "^6.4.3", + "karma-chrome-launcher": "~3.2.0", + "karma-coverage": "^2.2.1", + "karma-jasmine": "^5.1.0", + "karma-jasmine-html-reporter": "~2.1.0", + "ngx-build-plus": "^14.0.0", + "postcss-import": "^15.1.0", + "postcss-loader": "^7.3.3", + "postcss-scss": "^4.0.9", + "prettier": "^2.8.8", + "prettier-plugin-organize-attributes": "^0.0.5", + "tailwindcss": "^3.3.3", + "typescript": "^4.8.4" + } + }, + "node_modules/@adobe/css-tools": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.2.tgz", + "integrity": "sha512-DA5a1C0gD/pLOvhv33YMrbf2FK3oUzwNl9oOJqE4XVjuEtt6XIakRcsd7eLiOSPkp1kTRQGICTA8cKra/vFbjw==", + "dev": true + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@angular-devkit/architect": { + "version": "0.1402.13", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1402.13.tgz", + "integrity": "sha512-n0ISBuvkZHoOpAzuAZql1TU9VLHUE9e/a9g4VNOPHewjMzpN02VqeGKvJfOCKtzkCs6gVssIlILm2/SXxkIFxQ==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "14.2.13", + "rxjs": "6.6.7" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular-devkit/architect/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/@angular-devkit/architect/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/@angular-devkit/build-angular": { + "version": "14.2.13", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-14.2.13.tgz", + "integrity": "sha512-FJZKQ3xYFvEJ807sxVy4bCVyGU2NMl3UUPNfLIdIdzwwDEP9tx/cc+c4VtVPEZZfU8jVenu8XOvL6L0vpjt3yg==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "2.2.0", + "@angular-devkit/architect": "0.1402.13", + "@angular-devkit/build-webpack": "0.1402.13", + "@angular-devkit/core": "14.2.13", + "@babel/core": "7.18.10", + "@babel/generator": "7.18.12", + "@babel/helper-annotate-as-pure": "7.18.6", + "@babel/plugin-proposal-async-generator-functions": "7.18.10", + "@babel/plugin-transform-async-to-generator": "7.18.6", + "@babel/plugin-transform-runtime": "7.18.10", + "@babel/preset-env": "7.18.10", + "@babel/runtime": "7.18.9", + "@babel/template": "7.18.10", + "@discoveryjs/json-ext": "0.5.7", + "@ngtools/webpack": "14.2.13", + "ansi-colors": "4.1.3", + "babel-loader": "8.2.5", + "babel-plugin-istanbul": "6.1.1", + "browserslist": "^4.9.1", + "cacache": "16.1.2", + "copy-webpack-plugin": "11.0.0", + "critters": "0.0.16", + "css-loader": "6.7.1", + "esbuild-wasm": "0.15.5", + "glob": "8.0.3", + "https-proxy-agent": "5.0.1", + "inquirer": "8.2.4", + "jsonc-parser": "3.1.0", + "karma-source-map-support": "1.4.0", + "less": "4.1.3", + "less-loader": "11.0.0", + "license-webpack-plugin": "4.0.2", + "loader-utils": "3.2.1", + "mini-css-extract-plugin": "2.6.1", + "minimatch": "5.1.0", + "open": "8.4.0", + "ora": "5.4.1", + "parse5-html-rewriting-stream": "6.0.1", + "piscina": "3.2.0", + "postcss": "8.4.31", + "postcss-import": "15.0.0", + "postcss-loader": "7.0.1", + "postcss-preset-env": "7.8.0", + "regenerator-runtime": "0.13.9", + "resolve-url-loader": "5.0.0", + "rxjs": "6.6.7", + "sass": "1.54.4", + "sass-loader": "13.0.2", + "semver": "7.5.3", + "source-map-loader": "4.0.0", + "source-map-support": "0.5.21", + "stylus": "0.59.0", + "stylus-loader": "7.0.0", + "terser": "5.14.2", + "text-table": "0.2.0", + "tree-kill": "1.2.2", + "tslib": "2.4.0", + "webpack": "5.76.1", + "webpack-dev-middleware": "5.3.3", + "webpack-dev-server": "4.11.0", + "webpack-merge": "5.8.0", + "webpack-subresource-integrity": "5.1.0" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "optionalDependencies": { + "esbuild": "0.15.5" + }, + "peerDependencies": { + "@angular/compiler-cli": "^14.0.0", + "@angular/localize": "^14.0.0", + "@angular/service-worker": "^14.0.0", + "karma": "^6.3.0", + "ng-packagr": "^14.0.0", + "protractor": "^7.0.0", + "tailwindcss": "^2.0.0 || ^3.0.0", + "typescript": ">=4.6.2 <4.9" + }, + "peerDependenciesMeta": { + "@angular/localize": { + "optional": true + }, + "@angular/service-worker": { + "optional": true + }, + "karma": { + "optional": true + }, + "ng-packagr": { + "optional": true + }, + "protractor": { + "optional": true + }, + "tailwindcss": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/@types/estree": { + "version": "0.0.51", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", + "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", + "dev": true + }, + "node_modules/@angular-devkit/build-angular/node_modules/@webassemblyjs/ast": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", + "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "dev": true, + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", + "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", + "dev": true + }, + "node_modules/@angular-devkit/build-angular/node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", + "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", + "dev": true + }, + "node_modules/@angular-devkit/build-angular/node_modules/@webassemblyjs/helper-buffer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", + "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", + "dev": true + }, + "node_modules/@angular-devkit/build-angular/node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", + "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "dev": true, + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", + "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", + "dev": true + }, + "node_modules/@angular-devkit/build-angular/node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", + "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/@webassemblyjs/ieee754": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", + "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "dev": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/@webassemblyjs/leb128": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", + "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "dev": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/@webassemblyjs/utf8": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", + "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", + "dev": true + }, + "node_modules/@angular-devkit/build-angular/node_modules/@webassemblyjs/wasm-edit": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", + "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/helper-wasm-section": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-opt": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "@webassemblyjs/wast-printer": "1.11.1" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/@webassemblyjs/wasm-gen": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", + "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/@webassemblyjs/wasm-opt": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", + "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/@webassemblyjs/wasm-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", + "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/@webassemblyjs/wast-printer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", + "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "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": { + "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/@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/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "dev": true, + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/es-module-lexer": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", + "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", + "dev": true + }, + "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-angular/node_modules/postcss-import": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.0.0.tgz", + "integrity": "sha512-Y20shPQ07RitgBGv2zvkEAu9bqvrD77C9axhj/aA1BQj4czape2MdClCExvB27EwYEJdGgKZBpKanb0t1rK2Kg==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/postcss-loader": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.0.1.tgz", + "integrity": "sha512-VRviFEyYlLjctSM93gAZtcJJ/iSkPZ79zWbN/1fSH+NisBByEiVLqpdVDrPLVSi8DX0oJo12kL/GppTBdKVXiQ==", + "dev": true, + "dependencies": { + "cosmiconfig": "^7.0.0", + "klona": "^2.0.5", + "semver": "^7.3.7" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "postcss": "^7.0.0 || ^8.0.1", + "webpack": "^5.0.0" + } + }, + "node_modules/@angular-devkit/build-angular/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/@angular-devkit/build-angular/node_modules/rxjs/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/@angular-devkit/build-angular/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "dev": true + }, + "node_modules/@angular-devkit/build-angular/node_modules/webpack": { + "version": "5.76.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.1.tgz", + "integrity": "sha512-4+YIK4Abzv8172/SGqObnUjaIHjLEuUasz9EwQj/9xmPPkYJy2Mh03Q/lJfSD3YLzbxy5FeTq5Uw0323Oh6SJQ==", + "dev": true, + "dependencies": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^0.0.51", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.10.0", + "es-module-lexer": "^0.9.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "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.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@angular-devkit/build-webpack": { + "version": "0.1402.13", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1402.13.tgz", + "integrity": "sha512-K27aJmuw86ZOdiu5PoGeGDJ2v7g2ZCK0bGwc8jzkjTLRfvd4FRKIIZumGv3hbQ3vQRLikiU6WMDRTFyCZky/EA==", + "dev": true, + "dependencies": { + "@angular-devkit/architect": "0.1402.13", + "rxjs": "6.6.7" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "webpack": "^5.30.0", + "webpack-dev-server": "^4.0.0" + } + }, + "node_modules/@angular-devkit/build-webpack/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/@angular-devkit/build-webpack/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/@angular-devkit/core": { + "version": "14.2.13", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-14.2.13.tgz", + "integrity": "sha512-aIefeZcbjghQg/V6U9CTLtyB5fXDJ63KwYqVYkWP+i0XriS5A9puFgq2u/OVsWxAfYvqpDqp5AdQ0g0bi3CAsA==", + "dev": true, + "dependencies": { + "ajv": "8.11.0", + "ajv-formats": "2.1.1", + "jsonc-parser": "3.1.0", + "rxjs": "6.6.7", + "source-map": "0.7.4" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^3.5.2" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/core/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/@angular-devkit/core/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/@angular-devkit/schematics": { + "version": "14.2.13", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-14.2.13.tgz", + "integrity": "sha512-2zczyeNzeBcrT2HOysv52X9SH3tZoHfWJvVf6H0SIa74rfDKEl7hFpKNXnh3x8sIMLj5mZn05n5RCqGxCczcIg==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "14.2.13", + "jsonc-parser": "3.1.0", + "magic-string": "0.26.2", + "ora": "5.4.1", + "rxjs": "6.6.7" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular-devkit/schematics/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/@angular-devkit/schematics/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/@angular/animations": { + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-14.3.0.tgz", + "integrity": "sha512-QoBcIKy1ZiU+4qJsAh5Ls20BupWiXiZzKb0s6L9/dntPt5Msr4Ao289XR2P6O1L+kTsCprH9Kt41zyGQ/bkRqg==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/core": "14.3.0" + } + }, + "node_modules/@angular/cdk": { + "version": "14.2.7", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-14.2.7.tgz", + "integrity": "sha512-/tEsYaUbDSnfEmKVvAMramIptmhI67O+9STjOV0i+74XR2NospeK0fkbywIANu1n3w6AHGMotvRWJrjmbCElFg==", + "dependencies": { + "tslib": "^2.3.0" + }, + "optionalDependencies": { + "parse5": "^5.0.0" + }, + "peerDependencies": { + "@angular/common": "^14.0.0 || ^15.0.0", + "@angular/core": "^14.0.0 || ^15.0.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/cli": { + "version": "14.2.13", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-14.2.13.tgz", + "integrity": "sha512-I5EepRem2CCyS3GDzQxZ2ZrqQwVqoGoLY+ZQhsK1QGWUnUyFOjbv3OlUGxRUYwcedu19V1EBAKjmQ96HzMIcVQ==", + "dev": true, + "dependencies": { + "@angular-devkit/architect": "0.1402.13", + "@angular-devkit/core": "14.2.13", + "@angular-devkit/schematics": "14.2.13", + "@schematics/angular": "14.2.13", + "@yarnpkg/lockfile": "1.1.0", + "ansi-colors": "4.1.3", + "debug": "4.3.4", + "ini": "3.0.0", + "inquirer": "8.2.4", + "jsonc-parser": "3.1.0", + "npm-package-arg": "9.1.0", + "npm-pick-manifest": "7.0.1", + "open": "8.4.0", + "ora": "5.4.1", + "pacote": "13.6.2", + "resolve": "1.22.1", + "semver": "7.5.3", + "symbol-observable": "4.0.0", + "uuid": "8.3.2", + "yargs": "17.5.1" + }, + "bin": { + "ng": "bin/ng.js" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular/common": { + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-14.3.0.tgz", + "integrity": "sha512-pV9oyG3JhGWeQ+TFB0Qub6a1VZWMNZ6/7zEopvYivdqa5yDLLDSBRWb6P80RuONXyGnM1pa7l5nYopX+r/23GQ==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/core": "14.3.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/compiler": { + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-14.3.0.tgz", + "integrity": "sha512-E15Rh0t3vA+bctbKnBCaDmLvc3ix+ZBt6yFZmhZalReQ+KpOlvOJv+L9oiFEgg+rYVl2QdvN7US1fvT0PqswLw==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/core": "14.3.0" + }, + "peerDependenciesMeta": { + "@angular/core": { + "optional": true + } + } + }, + "node_modules/@angular/compiler-cli": { + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-14.3.0.tgz", + "integrity": "sha512-eoKpKdQ2X6axMgzcPUMZVYl3bIlTMzMeTo5V29No4BzgiUB+QoOTYGNJZkGRyqTNpwD9uSBJvmT2vG9+eC4ghQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.17.2", + "chokidar": "^3.0.0", + "convert-source-map": "^1.5.1", + "dependency-graph": "^0.11.0", + "magic-string": "^0.26.0", + "reflect-metadata": "^0.1.2", + "semver": "^7.0.0", + "sourcemap-codec": "^1.4.8", + "tslib": "^2.3.0", + "yargs": "^17.2.1" + }, + "bin": { + "ng-xi18n": "bundles/src/bin/ng_xi18n.js", + "ngc": "bundles/src/bin/ngc.js", + "ngcc": "bundles/ngcc/main-ngcc.js" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/compiler": "14.3.0", + "typescript": ">=4.6.2 <4.9" + } + }, + "node_modules/@angular/core": { + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-14.3.0.tgz", + "integrity": "sha512-wYiwItc0Uyn4FWZ/OAx/Ubp2/WrD3EgUJ476y1XI7yATGPF8n9Ld5iCXT08HOvc4eBcYlDfh90kTXR6/MfhzdQ==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "rxjs": "^6.5.3 || ^7.4.0", + "zone.js": "~0.11.4 || ~0.12.0" + } + }, + "node_modules/@angular/flex-layout": { + "version": "14.0.0-beta.41", + "resolved": "https://registry.npmjs.org/@angular/flex-layout/-/flex-layout-14.0.0-beta.41.tgz", + "integrity": "sha512-x1YcxqkdFlcbVXEy9ebCgW/F+7n/MXkEkwEcVEIPf5v5qn7HZsjQxgIj35Lf0amvMyF7h35prpoxO1uX5+ntFg==", + "deprecated": "This package has been deprecated. Please see https://blog.angular.io/modern-css-in-angular-layouts-4a259dca9127", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/cdk": "^14.0.0", + "@angular/common": "^14.0.0", + "@angular/core": "^14.0.0", + "@angular/platform-browser": "^14.0.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/forms": { + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-14.3.0.tgz", + "integrity": "sha512-fBZZC2UFMom2AZPjGQzROPXFWO6kvCsPDKctjJwClVC8PuMrkm+RRyiYRdBbt2qxWHEqOZM2OCQo73xUyZOYHw==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/common": "14.3.0", + "@angular/core": "14.3.0", + "@angular/platform-browser": "14.3.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/material": { + "version": "14.2.7", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-14.2.7.tgz", + "integrity": "sha512-WXHh8pEStpgkXZJmYOg2cI8BSHkV82ET4XTJCNPdveumaCn1UYnaNzsXD13kw5z+zmy8CufhFEzdXTrv/yt7KQ==", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/animations": "^14.0.0 || ^15.0.0", + "@angular/cdk": "14.2.7", + "@angular/common": "^14.0.0 || ^15.0.0", + "@angular/core": "^14.0.0 || ^15.0.0", + "@angular/forms": "^14.0.0 || ^15.0.0", + "@angular/platform-browser": "^14.0.0 || ^15.0.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/platform-browser": { + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-14.3.0.tgz", + "integrity": "sha512-w9Y3740UmTz44T0Egvc+4QV9sEbO61L+aRHbpkLTJdlEGzHByZvxJmJyBYmdqeyTPwc/Zpy7c02frlpfAlyB7A==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/animations": "14.3.0", + "@angular/common": "14.3.0", + "@angular/core": "14.3.0" + }, + "peerDependenciesMeta": { + "@angular/animations": { + "optional": true + } + } + }, + "node_modules/@angular/platform-browser-dynamic": { + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-14.3.0.tgz", + "integrity": "sha512-rneZiMrIiYRhrkQvdL40E2ErKRn4Zdo6EtjBM9pAmWeyoM8oMnOZb9gz5vhrkNWg06kVMVg0yKqluP5How7j3A==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/common": "14.3.0", + "@angular/compiler": "14.3.0", + "@angular/core": "14.3.0", + "@angular/platform-browser": "14.3.0" + } + }, + "node_modules/@angular/router": { + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-14.3.0.tgz", + "integrity": "sha512-uip0V7w7k7xyxxpTPbr7EuMnYLj3FzJrwkLVJSEw3TMMGHt5VU5t4BBa9veGZOta2C205XFrTAHnp8mD+XYY1w==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/common": "14.3.0", + "@angular/core": "14.3.0", + "@angular/platform-browser": "14.3.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@assemblyscript/loader": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.10.1.tgz", + "integrity": "sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg==", + "dev": true + }, + "node_modules/@babel/code-frame": { + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.9.tgz", + "integrity": "sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.10.tgz", + "integrity": "sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.18.10", + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-module-transforms": "^7.18.9", + "@babel/helpers": "^7.18.9", + "@babel/parser": "^7.18.10", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.18.10", + "@babel/types": "^7.18.10", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.18.12", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.12.tgz", + "integrity": "sha512-dfQ8ebCN98SvyL7IxNMCUtZQSq5R7kxgN+r8qYTGDmmSion1hX2C0zq2yo1bsCDhXixokv1SAWTZUMYbO/V5zg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.10", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", + "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.5.tgz", + "integrity": "sha512-m1EP3lVOPptR+2DwD125gziZNcmoNSHGmJROKoy87loWUQyJaVXDgpmruWqDARZSmtYQ+Dl25okU8+qhVzuykw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.9.tgz", + "integrity": "sha512-7qYrNM6HjpnPHJbopxmb8hSPoZ0gsX8IvUS32JGVoy+pU9e5N0nLr1VjJoR6kA4d9dmGLxNYOjeB8sUDal2WMw==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.22.9", + "@babel/helper-validator-option": "^7.22.5", + "browserslist": "^4.21.9", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.9.tgz", + "integrity": "sha512-Pwyi89uO4YrGKxL/eNJ8lfEH55DnRloGPOseaA8NFNL6jAUnn+KccaISiFazCj5IolPPDjGSdzQzXVzODVRqUQ==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-member-expression-to-functions": "^7.22.5", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/@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==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "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", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.9.tgz", + "integrity": "sha512-+svjVa/tFwsNSG4NEy1h85+HQ5imbT92Q5/bgtS7P0GTQlP8WuFdqsiABmQouhiFGyV66oGxZFpeYHza1rNsKw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "regexpu-core": "^5.3.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/@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==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "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", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz", + "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.17.7", + "@babel/helper-plugin-utils": "^7.16.7", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2", + "semver": "^6.1.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0-0" + } + }, + "node_modules/@babel/helper-define-polyfill-provider/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "dev": true, + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name/node_modules/@babel/template": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.5.tgz", + "integrity": "sha512-aBiH1NKMG0H2cGZqspNvsaBe6wNGjbJjuLy29aU+eDZjSbbN53BaxlpB02xm9v34pLTZ1nIQPFYn2qMZoa5BQQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz", + "integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz", + "integrity": "sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", + "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.9.tgz", + "integrity": "sha512-8WWC4oR4Px+tr+Fp0X3RHDVfINGpF3ad1HIbrc8A77epiR6eMMc6jsgozkzT2uDiOOdoS9cLIQ+XD2XvI2WSmQ==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-wrap-function": "^7.22.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator/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==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.9.tgz", + "integrity": "sha512-LJIKvvpgPOPUThdYqcX6IXRuIcTkcAub0IaDRGCZH0p5GPUp7PhRU9QVgFcDDd51BaPkk77ZjqFwh6DZTAEmGg==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-member-expression-to-functions": "^7.22.5", + "@babel/helper-optimise-call-expression": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", + "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "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==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz", + "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.9.tgz", + "integrity": "sha512-sZ+QzfauuUEfxSEjKFmi3qDSHgLsTPK/pEpoD/qonZKOtTPTLbf59oabPQ4rKekt9lFcj/hTZaOhWwFYrgjk+Q==", + "dev": true, + "dependencies": { + "@babel/helper-function-name": "^7.22.5", + "@babel/template": "^7.22.5", + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function/node_modules/@babel/template": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", + "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.6.tgz", + "integrity": "sha512-YjDs6y/fVOYFV8hAf1rxd1QvR9wJe1pDBZ2AREKq/SDayfPzgk0PBnVuTCE5X1acEpMMNOVUqoe+OwiZGJ+OaA==", + "dev": true, + "dependencies": { + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.6", + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers/node_modules/@babel/template": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", + "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.3.tgz", + "integrity": "sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.5.tgz", + "integrity": "sha512-NP1M5Rf+u2Gw9qfSO4ihjcTGW5zXTi36ITLd4/EoAcEhIZ0yjMqmftDNl3QC19CX7olhrjpyU454g/2W7X0jvQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.5.tgz", + "integrity": "sha512-31Bb65aZaUwqCbWMnZPduIZxCBngHFlzyN6Dq6KAJjtx+lx6ohKHubc61OomYi7XwVD4Ol0XCVz4h+pYFR048g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/plugin-transform-optional-chaining": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-proposal-async-generator-functions": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.18.10.tgz", + "integrity": "sha512-1mFuY2TOsR1hxbjCo4QL+qlIjV07p4H4EUYw2J/WCqsvFV6V9X9z9YhXbWndc/4fw+hYGlDT7egYxliMp5O6Ew==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-remap-async-to-generator": "^7.18.9", + "@babel/plugin-syntax-async-generators": "^7.8.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-class-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", + "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-class-static-block": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.21.0.tgz", + "integrity": "sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.21.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-proposal-dynamic-import": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", + "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-export-namespace-from": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", + "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-json-strings": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", + "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-json-strings": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz", + "integrity": "sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", + "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-numeric-separator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", + "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-object-rest-spread": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz", + "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.20.5", + "@babel/helper-compilation-targets": "^7.20.7", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.20.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-catch-binding": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", + "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-chaining": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz", + "integrity": "sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-methods": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", + "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz", + "integrity": "sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.21.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-unicode-property-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", + "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.22.5.tgz", + "integrity": "sha512-rdV97N7KqsRzeNGoWUOK6yUsWarLjE5Su/Snk9IYPU9CwkWHs4t+rTGOvffTR8XGkJMTAdLfO0xVnXm8wugIJg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.22.5.tgz", + "integrity": "sha512-26lTNXoVRdAnsaDXPpvCNUq+OVWEVC6bx7Vvz9rC53F2bagUWW4u4ii2+h8Fejfh7RYqPxn+libeFBBck9muEw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.18.6.tgz", + "integrity": "sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-remap-async-to-generator": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.22.5.tgz", + "integrity": "sha512-tdXZ2UdknEKQWKJP1KMNmuF5Lx3MymtMN/pvA+p/VEkhK8jVcQ1fzSy8KM9qRYhAf2/lV33hoMPKI/xaI9sADA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.22.5.tgz", + "integrity": "sha512-EcACl1i5fSQ6bt+YGuU/XGCeZKStLmyVGytWkpyhCLeQVA0eu6Wtiw92V+I1T/hnezUv7j74dA/Ro69gWcU+hg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.6.tgz", + "integrity": "sha512-58EgM6nuPNG6Py4Z3zSuu0xWu2VfodiMi72Jt5Kj2FECmaYk1RrTXA45z6KBFsu9tRgwQDwIiY4FXTt+YsSFAQ==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-classes/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==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.22.5.tgz", + "integrity": "sha512-4GHWBgRf0krxPX+AaPtgBAlTgTeZmqDynokHOX7aqqAB4tHs3U2Y02zH6ETFdLZGcg9UQSD1WCmkVrE9ErHeOg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/template": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties/node_modules/@babel/template": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", + "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.22.5.tgz", + "integrity": "sha512-GfqcFuGW8vnEqTUBM7UtPd5A4q797LTvvwKxXTgRsFjoqaJiEg9deBG6kWeQYkVEL569NpnmpC0Pkr/8BLKGnQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.22.5.tgz", + "integrity": "sha512-5/Yk9QxCQCl+sOIB1WelKnVRxTJDSAIxtJLL2/pqL14ZVlbH0fUQUZa/T5/UnQtBNgghR7mfB8ERBKyKPCi7Vw==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.22.5.tgz", + "integrity": "sha512-dEnYD+9BBgld5VBXHnF/DbYGp3fqGMsyxKbtD1mDyIA7AkTSpKXFhCVuj/oQVOoALfBs77DudA0BE4d5mcpmqw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.22.5.tgz", + "integrity": "sha512-vIpJFNM/FjZ4rh1myqIya9jXwrwwgFRHPjT3DkUA9ZLHuzox8jiXkOLvwm1H+PQIP3CqfC++WPKeuDi0Sjdj1g==", + "dev": true, + "dependencies": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.22.5.tgz", + "integrity": "sha512-3kxQjX1dU9uudwSshyLeEipvrLjBCVthCgeTp6CzE/9JYrlAIaeekVxRpCWsDDfYTfRZRoCeZatCQvwo+wvK8A==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.22.5.tgz", + "integrity": "sha512-UIzQNMS0p0HHiQm3oelztj+ECwFnj+ZRV4KnguvlsD2of1whUeM6o7wGNj6oLwcDoAXQ8gEqfgC24D+VdIcevg==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.22.5.tgz", + "integrity": "sha512-fTLj4D79M+mepcw3dgFBTIDYpbcB9Sm0bpm4ppXPaO+U+PKFFyV9MGRvS0gvGw62sd10kT5lRMKXAADb9pWy8g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.22.5.tgz", + "integrity": "sha512-RZEdkNtzzYCFl9SE9ATaUMTj2hqMb4StarOJLrZRbqqU4HSBE7UlBw9WBWQiDzrJZJdUWiMTVDI6Gv/8DPvfew==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.22.5.tgz", + "integrity": "sha512-R+PTfLTcYEmb1+kK7FNkhQ1gP4KgjpSO6HfH9+f8/yfp2Nt3ggBjiVpRwmwTlfqZLafYKJACy36yDXlEmI9HjQ==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.5.tgz", + "integrity": "sha512-B4pzOXj+ONRmuaQTg05b3y/4DuFz3WcCNAXPLb2Q0GT0TrGKGxNKV4jwsXts+StaM0LQczZbOpj8o1DLPDJIiA==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-simple-access": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.5.tgz", + "integrity": "sha512-emtEpoaTMsOs6Tzz+nbmcePl6AKVtS1yC4YNAeMun9U8YCsgadPNxnOPQ8GhHFB2qdx+LZu9LgoC0Lthuu05DQ==", + "dev": true, + "dependencies": { + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.22.5.tgz", + "integrity": "sha512-+S6kzefN/E1vkSsKx8kmQuqeQsvCKCd1fraCM7zXm4SFoggI099Tr4G8U81+5gtMdUeMQ4ipdQffbKLX0/7dBQ==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz", + "integrity": "sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.22.5.tgz", + "integrity": "sha512-AsF7K0Fx/cNKVyk3a+DW0JLo+Ua598/NxMRvxDnkpCIGFh43+h/v2xyhRUYf6oD8gE4QtL83C7zZVghMjHd+iw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.22.5.tgz", + "integrity": "sha512-klXqyaT9trSjIUrcsYIfETAzmOEZL3cBYqOYLJxBHfMFFggmXOv+NYSX/Jbs9mzMVESw/WycLFPRx8ba/b2Ipw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.6.tgz", + "integrity": "sha512-Vd5HiWml0mDVtcLHIoEU5sw6HOUW/Zk0acLs/SAeuLzkGNOPc9DB4nkUajemhCmTIz3eiaKREZn2hQQqF79YTg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.5.tgz", + "integrity": "sha512-AVkFUBurORBREOmHRKo06FjHYgjrabpdqRSwq6+C7R5iTCZOsM4QbcB27St0a4U6fffyAOqh3s/qEfybAhfivg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.22.5.tgz", + "integrity": "sha512-TiOArgddK3mK/x1Qwf5hay2pxI6wCZnvQqrFSqbtg1GLl2JcNMitVH/YnqjP+M31pLUeTfzY1HAXFDnUBV30rQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.22.5.tgz", + "integrity": "sha512-rR7KePOE7gfEtNTh9Qw+iO3Q/e4DEsoQ+hdvM6QUDH7JRJ5qxq5AA52ZzBWbI5i9lfNuvySgOGP8ZN7LAmaiPw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "regenerator-transform": "^0.15.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.22.5.tgz", + "integrity": "sha512-DTtGKFRQUDm8svigJzZHzb/2xatPc6TzNvAIJ5GqOKDsGFYgAskjRulbR/vGsPKq3OPqtexnz327qYpP57RFyA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.18.10.tgz", + "integrity": "sha512-q5mMeYAdfEbpBAgzl7tBre/la3LeCxmDO1+wMXRdPWbcoMjR3GiXlCLk7JBZVVye0bqTGNMbt0yYVXX1B1jEWQ==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.9", + "babel-plugin-polyfill-corejs2": "^0.3.2", + "babel-plugin-polyfill-corejs3": "^0.5.3", + "babel-plugin-polyfill-regenerator": "^0.4.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.22.5.tgz", + "integrity": "sha512-vM4fq9IXHscXVKzDv5itkO1X52SmdFBFcMIBZ2FRn2nqVYqw6dBexUgMvAjHW+KXpPPViD/Yo3GrDEBaRC0QYA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.22.5.tgz", + "integrity": "sha512-5ZzDQIGyvN4w8+dMmpohL6MBo+l2G7tfC/O2Dg7/hjpgeWvUx8FzfeOKxGog9IimPa4YekaQ9PlDqTLOljkcxg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.22.5.tgz", + "integrity": "sha512-zf7LuNpHG0iEeiyCNwX4j3gDg1jgt1k3ZdXBKbZSoA3BbGQGvMiSvfbZRR3Dr3aeJe3ooWFZxOOG3IRStYp2Bw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.22.5.tgz", + "integrity": "sha512-5ciOehRNf+EyUeewo8NkbQiUs4d6ZxiHo6BcBcnFlgiJfu16q0bQUw9Jvo0b0gBKFG1SMhDSjeKXSYuJLeFSMA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.22.5.tgz", + "integrity": "sha512-bYkI5lMzL4kPii4HHEEChkD0rkc+nvnlR6+o/qdqR6zrm0Sv/nodmyLhlq2DO0YKLUNd2VePmPRjJXSBh9OIdA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.22.5.tgz", + "integrity": "sha512-biEmVg1IYB/raUO5wT1tgfacCef15Fbzhkx493D3urBI++6hpJ+RFG4SrWMn0NEZLfvilqKf3QDrRVZHo08FYg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.22.5.tgz", + "integrity": "sha512-028laaOKptN5vHJf9/Arr/HiJekMd41hOEZYvNsrsXqJ7YPYuX2bQxh31fkZzGmq3YqHRJzYFFAVYvKfMPKqyg==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.18.10.tgz", + "integrity": "sha512-wVxs1yjFdW3Z/XkNfXKoblxoHgbtUF7/l3PvvP4m02Qz9TZ6uZGxRVYjSQeR87oQmHco9zWitW5J82DJ7sCjvA==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.18.8", + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-async-generator-functions": "^7.18.10", + "@babel/plugin-proposal-class-properties": "^7.18.6", + "@babel/plugin-proposal-class-static-block": "^7.18.6", + "@babel/plugin-proposal-dynamic-import": "^7.18.6", + "@babel/plugin-proposal-export-namespace-from": "^7.18.9", + "@babel/plugin-proposal-json-strings": "^7.18.6", + "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", + "@babel/plugin-proposal-numeric-separator": "^7.18.6", + "@babel/plugin-proposal-object-rest-spread": "^7.18.9", + "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", + "@babel/plugin-proposal-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-private-methods": "^7.18.6", + "@babel/plugin-proposal-private-property-in-object": "^7.18.6", + "@babel/plugin-proposal-unicode-property-regex": "^7.18.6", + "@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.18.6", + "@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-transform-arrow-functions": "^7.18.6", + "@babel/plugin-transform-async-to-generator": "^7.18.6", + "@babel/plugin-transform-block-scoped-functions": "^7.18.6", + "@babel/plugin-transform-block-scoping": "^7.18.9", + "@babel/plugin-transform-classes": "^7.18.9", + "@babel/plugin-transform-computed-properties": "^7.18.9", + "@babel/plugin-transform-destructuring": "^7.18.9", + "@babel/plugin-transform-dotall-regex": "^7.18.6", + "@babel/plugin-transform-duplicate-keys": "^7.18.9", + "@babel/plugin-transform-exponentiation-operator": "^7.18.6", + "@babel/plugin-transform-for-of": "^7.18.8", + "@babel/plugin-transform-function-name": "^7.18.9", + "@babel/plugin-transform-literals": "^7.18.9", + "@babel/plugin-transform-member-expression-literals": "^7.18.6", + "@babel/plugin-transform-modules-amd": "^7.18.6", + "@babel/plugin-transform-modules-commonjs": "^7.18.6", + "@babel/plugin-transform-modules-systemjs": "^7.18.9", + "@babel/plugin-transform-modules-umd": "^7.18.6", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.18.6", + "@babel/plugin-transform-new-target": "^7.18.6", + "@babel/plugin-transform-object-super": "^7.18.6", + "@babel/plugin-transform-parameters": "^7.18.8", + "@babel/plugin-transform-property-literals": "^7.18.6", + "@babel/plugin-transform-regenerator": "^7.18.6", + "@babel/plugin-transform-reserved-words": "^7.18.6", + "@babel/plugin-transform-shorthand-properties": "^7.18.6", + "@babel/plugin-transform-spread": "^7.18.9", + "@babel/plugin-transform-sticky-regex": "^7.18.6", + "@babel/plugin-transform-template-literals": "^7.18.9", + "@babel/plugin-transform-typeof-symbol": "^7.18.9", + "@babel/plugin-transform-unicode-escapes": "^7.18.10", + "@babel/plugin-transform-unicode-regex": "^7.18.6", + "@babel/preset-modules": "^0.1.5", + "@babel/types": "^7.18.10", + "babel-plugin-polyfill-corejs2": "^0.3.2", + "babel-plugin-polyfill-corejs3": "^0.5.3", + "babel-plugin-polyfill-regenerator": "^0.4.0", + "core-js-compat": "^3.22.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6.tgz", + "integrity": "sha512-ID2yj6K/4lKfhuU3+EX4UvNbIt7eACFbHmNUjzA+ep+B5971CknnA/9DEWKbRokfbbtblxxxXFJJrH47UEAMVg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==", + "dev": true + }, + "node_modules/@babel/runtime": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz", + "integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==", + "dev": true, + "dependencies": { + "regenerator-runtime": "^0.13.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", + "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.18.10", + "@babel/types": "^7.18.10" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/@babel/generator": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.3.tgz", + "integrity": "sha512-keeZWAV4LU3tW0qRi19HRpabC/ilM0HRBBzf9/k8FFiG4KVpiv0FIy4hHfLfFQZNhziCTPTmd59zoyv6DNISzg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.23.3", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.3.tgz", + "integrity": "sha512-OZnvoH2l8PK5eUvEcUyCt/sXgr/h+UWpVuBbOljwcrAgUl6lpchoQ++PHGyQy1AtYnVA6CEq3y5xeEI10brpXw==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@csstools/postcss-cascade-layers": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-1.1.1.tgz", + "integrity": "sha512-+KdYrpKC5TgomQr2DlZF4lDEpHcoxnj5IGddYYfBWJAKfj1JtuHUIqMa+E1pJJ+z3kvDViWMqyqPlG4Ja7amQA==", + "dev": true, + "dependencies": { + "@csstools/selector-specificity": "^2.0.2", + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-color-function": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-1.1.1.tgz", + "integrity": "sha512-Bc0f62WmHdtRDjf5f3e2STwRAl89N2CLb+9iAwzrv4L2hncrbDwnQD9PCq0gtAt7pOI2leIV08HIBUd4jxD8cw==", + "dev": true, + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-font-format-keywords": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-font-format-keywords/-/postcss-font-format-keywords-1.0.1.tgz", + "integrity": "sha512-ZgrlzuUAjXIOc2JueK0X5sZDjCtgimVp/O5CEqTcs5ShWBa6smhWYbS0x5cVc/+rycTDbjjzoP0KTDnUneZGOg==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-hwb-function": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-1.0.2.tgz", + "integrity": "sha512-YHdEru4o3Rsbjmu6vHy4UKOXZD+Rn2zmkAmLRfPet6+Jz4Ojw8cbWxe1n42VaXQhD3CQUXXTooIy8OkVbUcL+w==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-ic-unit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-1.0.1.tgz", + "integrity": "sha512-Ot1rcwRAaRHNKC9tAqoqNZhjdYBzKk1POgWfhN4uCOE47ebGcLRqXjKkApVDpjifL6u2/55ekkpnFcp+s/OZUw==", + "dev": true, + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-is-pseudo-class": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-2.0.7.tgz", + "integrity": "sha512-7JPeVVZHd+jxYdULl87lvjgvWldYu+Bc62s9vD/ED6/QTGjy0jy0US/f6BG53sVMTBJ1lzKZFpYmofBN9eaRiA==", + "dev": true, + "dependencies": { + "@csstools/selector-specificity": "^2.0.0", + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-nested-calc": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-nested-calc/-/postcss-nested-calc-1.0.0.tgz", + "integrity": "sha512-JCsQsw1wjYwv1bJmgjKSoZNvf7R6+wuHDAbi5f/7MbFhl2d/+v+TvBTU4BJH3G1X1H87dHl0mh6TfYogbT/dJQ==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-normalize-display-values": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-1.0.1.tgz", + "integrity": "sha512-jcOanIbv55OFKQ3sYeFD/T0Ti7AMXc9nM1hZWu8m/2722gOTxFg7xYu4RDLJLeZmPUVQlGzo4jhzvTUq3x4ZUw==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-oklab-function": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-1.1.1.tgz", + "integrity": "sha512-nJpJgsdA3dA9y5pgyb/UfEzE7W5Ka7u0CX0/HIMVBNWzWemdcTH3XwANECU6anWv/ao4vVNLTMxhiPNZsTK6iA==", + "dev": true, + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-progressive-custom-properties": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-1.3.0.tgz", + "integrity": "sha512-ASA9W1aIy5ygskZYuWams4BzafD12ULvSypmaLJT2jvQ8G0M3I8PRQhC0h7mG0Z3LI05+agZjqSR9+K9yaQQjA==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.3" + } + }, + "node_modules/@csstools/postcss-stepped-value-functions": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-1.0.1.tgz", + "integrity": "sha512-dz0LNoo3ijpTOQqEJLY8nyaapl6umbmDcgj4AD0lgVQ572b2eqA1iGZYTTWhrcrHztWDDRAX2DGYyw2VBjvCvQ==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-text-decoration-shorthand": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-1.0.0.tgz", + "integrity": "sha512-c1XwKJ2eMIWrzQenN0XbcfzckOLLJiczqy+YvfGmzoVXd7pT9FfObiSEfzs84bpE/VqfpEuAZ9tCRbZkZxxbdw==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-trigonometric-functions": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-1.0.2.tgz", + "integrity": "sha512-woKaLO///4bb+zZC2s80l+7cm07M7268MsyG3M0ActXXEFi6SuhvriQYcb58iiKGbjwwIU7n45iRLEHypB47Og==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-unset-value": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-1.0.2.tgz", + "integrity": "sha512-c8J4roPBILnelAsdLr4XOAR/GsTm0GJi4XpcfvoWk3U6KiTCqiFYc63KhRMQQX35jYMp4Ao8Ij9+IZRgMfJp1g==", + "dev": true, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/selector-specificity": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.2.0.tgz", + "integrity": "sha512-+OJ9konv95ClSTOJCmMZqpd5+YGsB2S+x6w3E1oaM8UuR5j8nTNHYSz8c9BEPGDOCMQYIEEGlVPj/VY64iTbGw==", + "dev": true, + "engines": { + "node": "^14 || ^16 || >=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss-selector-parser": "^6.0.10" + } + }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.5.tgz", + "integrity": "sha512-UHkDFCfSGTuXq08oQltXxSZmH1TXyWsL+4QhZDWvvLl6mEJQqk3u7/wq1LjhrrAXYIllaTtRSzUXl4Olkf2J8A==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@gar/promisify": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", + "dev": true + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/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, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/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, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/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, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", + "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "node_modules/@jridgewell/source-map/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", + "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, + "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "node_modules/@kurkle/color": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz", + "integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==", + "peer": true + }, + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", + "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==", + "dev": true + }, + "node_modules/@ng-apimock/core": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/@ng-apimock/core/-/core-3.11.0.tgz", + "integrity": "sha512-Gy4b0hdwuExADS00TMQ1gcj4TciDQveIVCRD+B/EInRYfO+IODvZH58Iw/dV+mjcAXIOEsrql2MES7/tWyqScA==", + "dependencies": { + "body-parser": "1.20.2", + "chokidar": "3.5.3", + "debug": "4.3.4", + "fs-extra": "11.1.1", + "glob": "8.1.0", + "inversify": "6.0.1", + "node-fetch": "2.6.9", + "reflect-metadata": "0.1.13", + "uuid": "9.0.0" + } + }, + "node_modules/@ng-apimock/core/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@ng-apimock/core/node_modules/uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@ngtools/webpack": { + "version": "14.2.13", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-14.2.13.tgz", + "integrity": "sha512-RQx/rGX7K/+R55x1R6Ax1JzyeHi8cW11dEXpzHWipyuSpusQLUN53F02eMB4VTakXsL3mFNWWy4bX3/LSq8/9w==", + "dev": true, + "engines": { + "node": "^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "@angular/compiler-cli": "^14.0.0", + "typescript": ">=4.6.2 <4.9", + "webpack": "^5.54.0" + } + }, + "node_modules/@ngx-translate/core": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@ngx-translate/core/-/core-14.0.0.tgz", + "integrity": "sha512-UevdwNCXMRCdJv//0kC8h2eSfmi02r29xeE8E9gJ1Al4D4jEJ7eiLPdjslTMc21oJNGguqqWeEVjf64SFtvw2w==", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/core": ">=13.0.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@ngx-translate/http-loader": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@ngx-translate/http-loader/-/http-loader-7.0.0.tgz", + "integrity": "sha512-j+NpXXlcGVdyUNyY/qsJrqqeAdJdizCd+GKh3usXExSqy1aE9866jlAIL+xrfDU4w+LiMoma5pgE4emvFebZmA==", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/common": ">=13.0.0", + "@ngx-translate/core": ">=14.0.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@ngxs/store": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@ngxs/store/-/store-3.8.1.tgz", + "integrity": "sha512-nbapLdMx+mtnb57BUWXbD6qYfVICv6Rp2NdoGx1++qDbc44ALC49KbF7rSjyPltlExxharAzoNpzO3JuueCP+A==", + "dependencies": { + "tslib": "^2.2.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ngxs" + }, + "peerDependencies": { + "@angular/core": ">=12.0.0 <17.0.0", + "rxjs": ">=6.5.5" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@npmcli/fs": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz", + "integrity": "sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==", + "dev": true, + "dependencies": { + "@gar/promisify": "^1.1.3", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/git": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-3.0.2.tgz", + "integrity": "sha512-CAcd08y3DWBJqJDpfuVL0uijlq5oaXaOJEKHKc4wqrjd00gkvTZB+nFuLn+doOOKddaQS9JfqtNoFCO2LCvA3w==", + "dev": true, + "dependencies": { + "@npmcli/promise-spawn": "^3.0.0", + "lru-cache": "^7.4.4", + "mkdirp": "^1.0.4", + "npm-pick-manifest": "^7.0.0", + "proc-log": "^2.0.0", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^2.0.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/git/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@npmcli/git/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@npmcli/installed-package-contents": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-1.0.7.tgz", + "integrity": "sha512-9rufe0wnJusCQoLpV9ZPKIVP55itrM5BxOXs10DmdbRfgWtHy1LDyskbwRnBghuB0PrF7pNPOqREVtpz4HqzKw==", + "dev": true, + "dependencies": { + "npm-bundled": "^1.1.1", + "npm-normalize-package-bin": "^1.0.1" + }, + "bin": { + "installed-package-contents": "index.js" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@npmcli/move-file": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-2.0.1.tgz", + "integrity": "sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==", + "deprecated": "This functionality has been moved to @npmcli/fs", + "dev": true, + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/node-gyp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-2.0.0.tgz", + "integrity": "sha512-doNI35wIe3bBaEgrlPfdJPaCpUR89pJWep4Hq3aRdh6gKazIVWfs0jHttvSSoq47ZXgC7h73kDsUl8AoIQUB+A==", + "dev": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/promise-spawn": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-3.0.0.tgz", + "integrity": "sha512-s9SgS+p3a9Eohe68cSI3fi+hpcZUmXq5P7w0kMlAsWVtR7XbK3ptkZqKT2cK1zLDObJ3sR+8P59sJE0w/KTL1g==", + "dev": true, + "dependencies": { + "infer-owner": "^1.0.4" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/run-script": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-4.2.1.tgz", + "integrity": "sha512-7dqywvVudPSrRCW5nTHpHgeWnbBtz8cFkOuKrecm6ih+oO9ciydhWt6OF7HlqupRRmB8Q/gECVdB9LMfToJbRg==", + "dev": true, + "dependencies": { + "@npmcli/node-gyp": "^2.0.0", + "@npmcli/promise-spawn": "^3.0.0", + "node-gyp": "^9.0.0", + "read-package-json-fast": "^2.0.3", + "which": "^2.0.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/run-script/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@schematics/angular": { + "version": "14.2.13", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-14.2.13.tgz", + "integrity": "sha512-MLxTpTU3E8QACQ/5c0sENMR2gRiMXpGaKeD5IHY+3wyU2fUSJVB0QPU/l1WhoyZbX8N9ospBgf5UEG7taVF9rg==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "14.2.13", + "@angular-devkit/schematics": "14.2.13", + "jsonc-parser": "3.1.0" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==", + "dev": true + }, + "node_modules/@sovity.de/edc-client": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/@sovity.de/edc-client/-/edc-client-10.4.0.tgz", + "integrity": "sha512-CBC7bHdIWD1K5plc+tgdJOb5YT82YyyaZ1xMKCy5Ox77KgQEOKG5W/95VzzZkap91xV5zNO5j7I5KJXj18jz8g==", + "dependencies": { + "zod": "^3.22.4" + } + }, + "node_modules/@tailwindcss/typography": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.10.tgz", + "integrity": "sha512-Pe8BuPJQJd3FfRnm6H0ulKIGoMEQS+Vq01R6M5aCrFB/ccR/shT+0kXLjouGC1gFLm9hopTFN+DMP0pfwRWzPw==", + "dev": true, + "dependencies": { + "lodash.castarray": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.merge": "^4.6.2", + "postcss-selector-parser": "6.0.10" + }, + "peerDependencies": { + "tailwindcss": ">=3.0.0 || insiders" + } + }, + "node_modules/@tailwindcss/typography/node_modules/postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@trivago/prettier-plugin-sort-imports": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-4.3.0.tgz", + "integrity": "sha512-r3n0onD3BTOVUNPhR4lhVK4/pABGpbA7bW3eumZnYdKaHkf1qEC+Mag6DPbGNuuh0eG8AaYj+YqmVHSiGslaTQ==", + "dev": true, + "dependencies": { + "@babel/generator": "7.17.7", + "@babel/parser": "^7.20.5", + "@babel/traverse": "7.23.2", + "@babel/types": "7.17.0", + "javascript-natural-sort": "0.7.1", + "lodash": "^4.17.21" + }, + "peerDependencies": { + "@vue/compiler-sfc": "3.x", + "prettier": "2.x - 3.x" + }, + "peerDependenciesMeta": { + "@vue/compiler-sfc": { + "optional": true + } + } + }, + "node_modules/@trivago/prettier-plugin-sort-imports/node_modules/@babel/generator": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.7.tgz", + "integrity": "sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.17.0", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@trivago/prettier-plugin-sort-imports/node_modules/@babel/types": { + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", + "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.16.7", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@trivago/prettier-plugin-sort-imports/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/bonjour": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz", + "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect-history-api-fallback": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.3.tgz", + "integrity": "sha512-6mfQ6iNvhSKCZJoY6sIG3m0pKkdUcweVNOLuBBKvoWGzl2yRxOJcYOTRyLKt3nxXvBLJWa6QkW//tgbIwJehmA==", + "dev": true, + "dependencies": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, + "node_modules/@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", + "dev": true + }, + "node_modules/@types/cors": { + "version": "2.8.17", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", + "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/dompurify": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.0.5.tgz", + "integrity": "sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==", + "dependencies": { + "@types/trusted-types": "*" + } + }, + "node_modules/@types/eslint": { + "version": "8.44.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.1.tgz", + "integrity": "sha512-XpNDc4Z5Tb4x+SW1MriMVeIsMoONHCkWFMkR/aPJbzEsxqHy+4Glu/BqTdPrApfDeMaXbtNh6bseNgl5KaWrSg==", + "dev": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", + "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", + "dev": true, + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz", + "integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==", + "dev": true + }, + "node_modules/@types/express": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "dev": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.17.41", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.41.tgz", + "integrity": "sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", + "dev": true + }, + "node_modules/@types/http-proxy": { + "version": "1.17.14", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.14.tgz", + "integrity": "sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/jasmine": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-4.3.6.tgz", + "integrity": "sha512-3N0FpQTeiWjm+Oo1WUYWguUS7E6JLceiGTriFrG8k5PU7zRLJCzLcWURU3wjMbZGS//a2/LgjsnO3QxIlwxt9g==", + "dev": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.12", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", + "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", + "dev": true + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true + }, + "node_modules/@types/node": { + "version": "18.17.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.17.1.tgz", + "integrity": "sha512-xlR1jahfizdplZYRU59JlUx9uzF1ARa8jbhM11ccpCJya8kvos5jwdm2ZAgxSCwOl0fq21svP18EVwPBXMQudw==", + "dev": true + }, + "node_modules/@types/node-forge": { + "version": "1.3.9", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.9.tgz", + "integrity": "sha512-meK88cx/sTalPSLSoCzkiUB4VPIFHmxtXm5FaaqRDqBX2i/Sy8bJ4odsan0b20RBjPh06dAQ+OTTdnyQyhJZyQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, + "node_modules/@types/qs": { + "version": "6.9.10", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.10.tgz", + "integrity": "sha512-3Gnx08Ns1sEoCrWssEgTSJs/rsT2vhGP+Ja9cnnk9k4ALxinORlQneLXFeFKOTJMOeZUFD1s7w+w2AphTpvzZw==", + "dev": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true + }, + "node_modules/@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "dev": true + }, + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "dev": true, + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-index": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz", + "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz", + "integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==", + "dev": true, + "dependencies": { + "@types/http-errors": "*", + "@types/mime": "*", + "@types/node": "*" + } + }, + "node_modules/@types/sockjs": { + "version": "0.3.36", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", + "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==" + }, + "node_modules/@types/ws": { + "version": "8.5.9", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.9.tgz", + "integrity": "sha512-jbdrY0a8lxfdTp/+r7Z4CkycbOFN8WX+IOchLJr3juT/xzbJ8URyTVSJ/hvNdadTgM1mnedb47n+Y31GsFnQlg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", + "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", + "dev": true, + "peer": true, + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", + "dev": true, + "peer": true + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", + "dev": true, + "peer": true + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", + "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==", + "dev": true, + "peer": true + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", + "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", + "dev": true, + "peer": true, + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", + "dev": true, + "peer": true + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", + "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", + "dev": true, + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", + "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", + "dev": true, + "peer": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", + "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", + "dev": true, + "peer": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", + "dev": true, + "peer": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", + "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", + "dev": true, + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-opt": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6", + "@webassemblyjs/wast-printer": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", + "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", + "dev": true, + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", + "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", + "dev": true, + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", + "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", + "dev": true, + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", + "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", + "dev": true, + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "node_modules/@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", + "dev": true + }, + "node_modules/abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "dev": true + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", + "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "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==", + "dev": true, + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/adjust-sourcemap-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", + "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", + "dev": true, + "dependencies": { + "loader-utils": "^2.0.0", + "regex-parser": "^2.2.11" + }, + "engines": { + "node": ">=8.9" + } + }, + "node_modules/adjust-sourcemap-loader/node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/agentkeepalive": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.3.0.tgz", + "integrity": "sha512-7Epl1Blf4Sy37j4v9f9FjICCh4+KAQOyXgHEwlyBiAQLbhKdq/i2QQU3amQalS/wPhdPzDXPL5DMR5bkn+YeWg==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "depd": "^2.0.0", + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "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" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", + "dev": true, + "engines": [ + "node >= 0.8.0" + ], + "bin": { + "ansi-html": "bin/ansi-html" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "dev": true + }, + "node_modules/are-we-there-yet": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", + "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", + "dev": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", + "dev": true + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/autoprefixer": { + "version": "10.4.14", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz", + "integrity": "sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + } + ], + "dependencies": { + "browserslist": "^4.21.5", + "caniuse-lite": "^1.0.30001464", + "fraction.js": "^4.2.0", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/babel-loader": { + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.5.tgz", + "integrity": "sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==", + "dev": true, + "dependencies": { + "find-cache-dir": "^3.3.1", + "loader-utils": "^2.0.0", + "make-dir": "^3.1.0", + "schema-utils": "^2.6.5" + }, + "engines": { + "node": ">= 8.9" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "webpack": ">=2" + } + }, + "node_modules/babel-loader/node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", + "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.17.7", + "@babel/helper-define-polyfill-provider": "^0.3.3", + "semver": "^6.1.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.3.tgz", + "integrity": "sha512-zKsXDh0XjnrUEW0mxIHLfjBfnXSMr5Q/goMe/fxpQnLm07mcOZiIZHBNWCMx60HmdvjxfXcalac0tfFg0wqxyw==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.3.2", + "core-js-compat": "^3.21.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz", + "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.3.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "dev": true, + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, + "node_modules/batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", + "dev": true + }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/body-parser": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/bonjour-service": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.1.1.tgz", + "integrity": "sha512-Z/5lQRMOG9k7W+FkeGTNjh7htqn/2LMnfOvBZ8pynNZCM9MwkQkI3zeI4oz09uWdcgmgHugVvBqxGg4VQJ5PCg==", + "dev": true, + "dependencies": { + "array-flatten": "^2.1.2", + "dns-equal": "^1.0.0", + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.21.9", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.9.tgz", + "integrity": "sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001503", + "electron-to-chromium": "^1.4.431", + "node-releases": "^2.0.12", + "update-browserslist-db": "^1.0.11" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/builtins": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", + "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", + "dev": true, + "dependencies": { + "semver": "^7.0.0" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cacache": { + "version": "16.1.2", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-16.1.2.tgz", + "integrity": "sha512-Xx+xPlfCZIUHagysjjOAje9nRo8pRDczQCcXb4J2O0BLtH+xeVue6ba4y1kfJfQMAnM2mkcoMIAyOctlaRGWYA==", + "dev": true, + "dependencies": { + "@npmcli/fs": "^2.1.0", + "@npmcli/move-file": "^2.0.0", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "glob": "^8.0.1", + "infer-owner": "^1.0.4", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "mkdirp": "^1.0.4", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^9.0.0", + "tar": "^6.1.11", + "unique-filename": "^1.1.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/cacache/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001517", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001517.tgz", + "integrity": "sha512-Vdhm5S11DaFVLlyiKu4hiUTkpZu+y1KA/rZZqVQfOD5YdDT/eQKlkt7NaE0WGOFgX32diqt9MiP9CAiFeRklaA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "node_modules/chart.js": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.3.1.tgz", + "integrity": "sha512-QHuISG3hTJ0ftq0I0f5jqH9mNVO9bqG8P+zvMOVslgKajQVvFEX7QAhYNJ+QEmw+uYTwo8XpTimaB82oeTWjxw==", + "peer": true, + "dependencies": { + "@kurkle/color": "^0.3.0" + }, + "engines": { + "pnpm": ">=7" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/clean-deep": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/clean-deep/-/clean-deep-3.4.0.tgz", + "integrity": "sha512-Lo78NV5ItJL/jl+B5w0BycAisaieJGXK1qYi/9m4SjR8zbqmrUtO7Yhro40wEShGmmxs/aJLI/A+jNhdkXK8mw==", + "dependencies": { + "lodash.isempty": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.transform": "^4.6.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.0.tgz", + "integrity": "sha512-4/aL9X3Wh0yiMQlE+eeRhWP6vclO3QRtw1JHKIT0FFUs5FjpFmESqtMvYZ0+lbzBw900b95mS0hohy+qn2VK/g==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true, + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dev": true, + "dependencies": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/compression/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/connect": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", + "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", + "utils-merge": "1.0.1" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/connect-history-api-fallback": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/connect/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/connect/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "dev": true + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, + "node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "dev": true + }, + "node_modules/copy-anything": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz", + "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", + "dev": true, + "dependencies": { + "is-what": "^3.14.1" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "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==", + "dev": true, + "dependencies": { + "fast-glob": "^3.2.11", + "glob-parent": "^6.0.1", + "globby": "^13.1.1", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + } + }, + "node_modules/copy-webpack-plugin/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/copy-webpack-plugin/node_modules/schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/core-js-compat": { + "version": "3.31.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.31.1.tgz", + "integrity": "sha512-wIDWd2s5/5aJSdpOJHfSibxNODxoGoWOBHt8JSPB41NOE94M7kuTPZCYLOlTtuoXTsBPKobpJ6T+y0SSy5L9SA==", + "dev": true, + "dependencies": { + "browserslist": "^4.21.9" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dev": true, + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/cosmiconfig": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.2.0.tgz", + "integrity": "sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ==", + "dev": true, + "dependencies": { + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + } + }, + "node_modules/cosmiconfig/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/cosmiconfig/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/critters": { + "version": "0.0.16", + "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.16.tgz", + "integrity": "sha512-JwjgmO6i3y6RWtLYmXwO5jMd+maZt8Tnfu7VVISmEWyQqfLpB8soBswf8/2bu6SBXxtKA68Al3c+qIG1ApT68A==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "css-select": "^4.2.0", + "parse5": "^6.0.1", + "parse5-htmlparser2-tree-adapter": "^6.0.1", + "postcss": "^8.3.7", + "pretty-bytes": "^5.3.0" + } + }, + "node_modules/critters/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/critters/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/critters/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/critters/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/critters/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/critters/node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + }, + "node_modules/critters/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/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-blank-pseudo": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-3.0.3.tgz", + "integrity": "sha512-VS90XWtsHGqoM0t4KpH053c4ehxZ2E6HtGI7x68YFV0pTo/QmkV/YFA+NnlvK8guxZVNWGQhVNJGC39Q8XF4OQ==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "bin": { + "css-blank-pseudo": "dist/cli.cjs" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/css-has-pseudo": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-3.0.4.tgz", + "integrity": "sha512-Vse0xpR1K9MNlp2j5w1pgWIJtm1a8qS0JwS9goFYcImjlHEmywP9VUF05aGBXzGpDJF86QXk4L0ypBmwPhGArw==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "bin": { + "css-has-pseudo": "dist/cli.cjs" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/css-loader": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.1.tgz", + "integrity": "sha512-yB5CNFa14MbPJcomwNh3wLThtkZgcNyI2bNMRt8iE5Z8Vwl7f8vQXFAzn2HDOJvtDq2NTZBUGMSUNNyrv3/+cw==", + "dev": true, + "dependencies": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.7", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.3.5" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/css-prefers-color-scheme": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz", + "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==", + "dev": true, + "bin": { + "css-prefers-color-scheme": "dist/cli.cjs" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cssdb": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.7.0.tgz", + "integrity": "sha512-1hN+I3r4VqSNQ+OmMXxYexnumbOONkSil0TWMebVXHtzYW4tRRPovUNHPHj2d4nrgOuYJ8Vs3XwvywsuwwXNNA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + } + ] + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssstyle": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-3.0.0.tgz", + "integrity": "sha512-N4u2ABATi3Qplzf0hWbVCdjenim8F3ojEXpBDF5hBpjzW182MjNGLqfmQ0SkSPeQ+V86ZXgeH8aXj6kayd4jgg==", + "dependencies": { + "rrweb-cssom": "^0.6.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/custom-event": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", + "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==", + "dev": true + }, + "node_modules/data-urls": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", + "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", + "dependencies": { + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/data-urls/node_modules/tr46": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", + "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/data-urls/node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "engines": { + "node": ">=12" + } + }, + "node_modules/data-urls/node_modules/whatwg-url": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.0.0.tgz", + "integrity": "sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==", + "dependencies": { + "tr46": "^5.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, + "node_modules/date-fns-tz": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/date-fns-tz/-/date-fns-tz-2.0.1.tgz", + "integrity": "sha512-fJCG3Pwx8HUoLhkepdsP7Z5RsucUi+ZBOxyM5d0ZZ6c4SdYustq0VMmOu6Wf7bli+yS/Jwp91TOCqn9jMcVrUA==", + "peerDependencies": { + "date-fns": "2.x" + } + }, + "node_modules/date-fns/node_modules/@babel/runtime": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.6.tgz", + "integrity": "sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ==", + "dependencies": { + "regenerator-runtime": "^0.13.11" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/date-fns/node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + }, + "node_modules/date-format": { + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz", + "integrity": "sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" + }, + "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, + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "dev": true + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dependency-graph": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz", + "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true + }, + "node_modules/di": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", + "integrity": "sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==", + "dev": true + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true + }, + "node_modules/dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==", + "dev": true + }, + "node_modules/dns-packet": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", + "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", + "dev": true, + "dependencies": { + "@leichtgewicht/ip-codec": "^2.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/dom-serialize": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", + "integrity": "sha512-Yra4DbvoW7/Z6LBN560ZwXMjoNOSAN2wRsKFGc4iBeso+mpIA6qj1vfdf9HpMaKAqG6wXTy+1SYEzmNpKXOSsQ==", + "dev": true, + "dependencies": { + "custom-event": "~1.0.0", + "ent": "~2.2.0", + "extend": "^3.0.0", + "void-elements": "^2.0.0" + } + }, + "node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dev": true, + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/dompurify": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.7.tgz", + "integrity": "sha512-BViYTZoqP3ak/ULKOc101y+CtHDUvBsVgSxIF1ku0HmK6BRf+C03MC+tArMvOPtVtZp83DDh5puywKDu4sbVjQ==" + }, + "node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dotenv": { + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", + "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/motdotla/dotenv?sponsor=1" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/electron-to-chromium": { + "version": "1.4.471", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.471.tgz", + "integrity": "sha512-GpmGRC1vTl60w/k6YpQ18pSiqnmr0j3un//5TV1idPi6aheNfkT1Ye71tMEabWyNDO6sBMgAR+95Eb0eUUr1tA==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/engine.io": { + "version": "6.5.5", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.5.tgz", + "integrity": "sha512-C5Pn8Wk+1vKBoHghJODM63yk8MvrO9EWZUfkAt5HAqIgPE4/8FF0PEGHXtEd40l223+cE5ABWuPzm38PHFXfMA==", + "dev": true, + "dependencies": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.2.tgz", + "integrity": "sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", + "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/ent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", + "integrity": "sha512-GHrMyVZQWvTIdDtpiEXdHZnFQKzeO09apj8Cbl4pKWy4i0Oprcq17usfDt5aO63swf0JOeMWjWQE/LzgSRuWpA==", + "dev": true + }, + "node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "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 + }, + "node_modules/errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dev": true, + "optional": true, + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-module-lexer": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.0.tgz", + "integrity": "sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA==", + "dev": true, + "peer": true + }, + "node_modules/esbuild": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.5.tgz", + "integrity": "sha512-VSf6S1QVqvxfIsSKb3UKr3VhUCis7wgDbtF4Vd9z84UJr05/Sp2fRKmzC+CSPG/dNAPPJZ0BTBLTT1Fhd6N9Gg==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/linux-loong64": "0.15.5", + "esbuild-android-64": "0.15.5", + "esbuild-android-arm64": "0.15.5", + "esbuild-darwin-64": "0.15.5", + "esbuild-darwin-arm64": "0.15.5", + "esbuild-freebsd-64": "0.15.5", + "esbuild-freebsd-arm64": "0.15.5", + "esbuild-linux-32": "0.15.5", + "esbuild-linux-64": "0.15.5", + "esbuild-linux-arm": "0.15.5", + "esbuild-linux-arm64": "0.15.5", + "esbuild-linux-mips64le": "0.15.5", + "esbuild-linux-ppc64le": "0.15.5", + "esbuild-linux-riscv64": "0.15.5", + "esbuild-linux-s390x": "0.15.5", + "esbuild-netbsd-64": "0.15.5", + "esbuild-openbsd-64": "0.15.5", + "esbuild-sunos-64": "0.15.5", + "esbuild-windows-32": "0.15.5", + "esbuild-windows-64": "0.15.5", + "esbuild-windows-arm64": "0.15.5" + } + }, + "node_modules/esbuild-android-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.5.tgz", + "integrity": "sha512-dYPPkiGNskvZqmIK29OPxolyY3tp+c47+Fsc2WYSOVjEPWNCHNyqhtFqQadcXMJDQt8eN0NMDukbyQgFcHquXg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-android-arm64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.5.tgz", + "integrity": "sha512-YyEkaQl08ze3cBzI/4Cm1S+rVh8HMOpCdq8B78JLbNFHhzi4NixVN93xDrHZLztlocEYqi45rHHCgA8kZFidFg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-darwin-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.5.tgz", + "integrity": "sha512-Cr0iIqnWKx3ZTvDUAzG0H/u9dWjLE4c2gTtRLz4pqOBGjfjqdcZSfAObFzKTInLLSmD0ZV1I/mshhPoYSBMMCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-darwin-arm64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.5.tgz", + "integrity": "sha512-WIfQkocGtFrz7vCu44ypY5YmiFXpsxvz2xqwe688jFfSVCnUsCn2qkEVDo7gT8EpsLOz1J/OmqjExePL1dr1Kg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-freebsd-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.5.tgz", + "integrity": "sha512-M5/EfzV2RsMd/wqwR18CELcenZ8+fFxQAAEO7TJKDmP3knhWSbD72ILzrXFMMwshlPAS1ShCZ90jsxkm+8FlaA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-freebsd-arm64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.5.tgz", + "integrity": "sha512-2JQQ5Qs9J0440F/n/aUBNvY6lTo4XP/4lt1TwDfHuo0DY3w5++anw+jTjfouLzbJmFFiwmX7SmUhMnysocx96w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-32": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.5.tgz", + "integrity": "sha512-gO9vNnIN0FTUGjvTFucIXtBSr1Woymmx/aHQtuU+2OllGU6YFLs99960UD4Dib1kFovVgs59MTXwpFdVoSMZoQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.5.tgz", + "integrity": "sha512-ne0GFdNLsm4veXbTnYAWjbx3shpNKZJUd6XpNbKNUZaNllDZfYQt0/zRqOg0sc7O8GQ+PjSMv9IpIEULXVTVmg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-arm": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.5.tgz", + "integrity": "sha512-wvAoHEN+gJ/22gnvhZnS/+2H14HyAxM07m59RSLn3iXrQsdS518jnEWRBnJz3fR6BJa+VUTo0NxYjGaNt7RA7Q==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-arm64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.5.tgz", + "integrity": "sha512-7EgFyP2zjO065XTfdCxiXVEk+f83RQ1JsryN1X/VSX2li9rnHAt2swRbpoz5Vlrl6qjHrCmq5b6yxD13z6RheA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-mips64le": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.5.tgz", + "integrity": "sha512-KdnSkHxWrJ6Y40ABu+ipTZeRhFtc8dowGyFsZY5prsmMSr1ZTG9zQawguN4/tunJ0wy3+kD54GaGwdcpwWAvZQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-ppc64le": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.5.tgz", + "integrity": "sha512-QdRHGeZ2ykl5P0KRmfGBZIHmqcwIsUKWmmpZTOq573jRWwmpfRmS7xOhmDHBj9pxv+6qRMH8tLr2fe+ZKQvCYw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-riscv64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.5.tgz", + "integrity": "sha512-p+WE6RX+jNILsf+exR29DwgV6B73khEQV0qWUbzxaycxawZ8NE0wA6HnnTxbiw5f4Gx9sJDUBemh9v49lKOORA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-s390x": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.5.tgz", + "integrity": "sha512-J2ngOB4cNzmqLHh6TYMM/ips8aoZIuzxJnDdWutBw5482jGXiOzsPoEF4j2WJ2mGnm7FBCO4StGcwzOgic70JQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-netbsd-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.5.tgz", + "integrity": "sha512-MmKUYGDizYjFia0Rwt8oOgmiFH7zaYlsoQ3tIOfPxOqLssAsEgG0MUdRDm5lliqjiuoog8LyDu9srQk5YwWF3w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-openbsd-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.5.tgz", + "integrity": "sha512-2mMFfkLk3oPWfopA9Plj4hyhqHNuGyp5KQyTT9Rc8hFd8wAn5ZrbJg+gNcLMo2yzf8Uiu0RT6G9B15YN9WQyMA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-sunos-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.5.tgz", + "integrity": "sha512-2sIzhMUfLNoD+rdmV6AacilCHSxZIoGAU2oT7XmJ0lXcZWnCvCtObvO6D4puxX9YRE97GodciRGDLBaiC6x1SA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-wasm": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.15.5.tgz", + "integrity": "sha512-lTJOEKekN/4JI/eOEq0wLcx53co2N6vaT/XjBz46D1tvIVoUEyM0o2K6txW6gEotf31szFD/J1PbxmnbkGlK9A==", + "dev": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-32": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.5.tgz", + "integrity": "sha512-e+duNED9UBop7Vnlap6XKedA/53lIi12xv2ebeNS4gFmu7aKyTrok7DPIZyU5w/ftHD4MUDs5PJUkQPP9xJRzg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.5.tgz", + "integrity": "sha512-v+PjvNtSASHOjPDMIai9Yi+aP+Vwox+3WVdg2JB8N9aivJ7lyhp4NVU+J0MV2OkWFPnVO8AE/7xH+72ibUUEnw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-arm64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.5.tgz", + "integrity": "sha512-Yz8w/D8CUPYstvVQujByu6mlf48lKmXkq6bkeSZZxTA626efQOJb26aDGLzmFWx6eg/FwrXgt6SZs9V8Pwy/aA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventemitter-asyncresource": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/eventemitter-asyncresource/-/eventemitter-asyncresource-1.0.0.tgz", + "integrity": "sha512-39F7TBIV0G7gTelxwbEqnwhp90eqCPON1k0NwNfwhgKn4Co4ybUbj2pECcXT0B3ztRKZ7Pw1JujUUgmQJHcVAQ==", + "dev": true + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exponential-backoff": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz", + "integrity": "sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==", + "dev": true + }, + "node_modules/express": { + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", + "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", + "dev": true, + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.2", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.6.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express/node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dev": true + }, + "node_modules/express/node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dev": true, + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/finalhandler/node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "dependencies": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/avajs/find-cache-dir?sponsor=1" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.2.tgz", + "integrity": "sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fraction.js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", + "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", + "dev": true, + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://www.patreon.com/infusion" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-extra": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", + "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-monkey": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.5.tgz", + "integrity": "sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew==", + "dev": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "node_modules/gauge": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "dev": true, + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", + "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", + "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/globby": { + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", + "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", + "dev": true, + "dependencies": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.3.0", + "ignore": "^5.2.4", + "merge2": "^1.4.1", + "slash": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "node_modules/handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "dev": true + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "dev": true + }, + "node_modules/hdr-histogram-js": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hdr-histogram-js/-/hdr-histogram-js-2.0.3.tgz", + "integrity": "sha512-Hkn78wwzWHNCp2uarhzQ2SGFLU3JY8SBDDd3TAABK4fc30wm+MuPOrg5QVFVfkKOQd6Bfz3ukJEI+q9sXEkK1g==", + "dev": true, + "dependencies": { + "@assemblyscript/loader": "^0.10.1", + "base64-js": "^1.2.0", + "pako": "^1.0.3" + } + }, + "node_modules/hdr-histogram-percentiles-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hdr-histogram-percentiles-obj/-/hdr-histogram-percentiles-obj-3.0.0.tgz", + "integrity": "sha512-7kIufnBqdsBGcSZLPJwqHT3yhk1QTsSlFsVD3kx5ixH/AlgBs9yM1q6DPhXZ8f8gtdqgh7N7/5btRLpQsS2gHw==", + "dev": true + }, + "node_modules/hosted-git-info": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-5.2.1.tgz", + "integrity": "sha512-xIcQYMnhcx2Nr4JTjsFmwwnr9vldugPy9uVm0o87bjqqWMv9GaqsTeT+i99wTl0mk1uLxJtHxLb8kymqTENQsw==", + "dev": true, + "dependencies": { + "lru-cache": "^7.5.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/hosted-git-info/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "node_modules/hpack.js/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/hpack.js/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/hpack.js/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "dependencies": { + "whatwg-encoding": "^3.1.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/html-entities": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.4.0.tgz", + "integrity": "sha512-igBTJcNNNhvZFRtm8uA6xMY6xYleeDwn3PeBCkDz7tHttv4F2hsDI2aPgNERWzvRcNYHNT3ymRaQzllmXj4YsQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ] + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/http-cache-semantics": { + "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 + }, + "node_modules/http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", + "dev": true + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", + "dev": true + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/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, + "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/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "dev": true, + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/ignore-walk": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-5.0.1.tgz", + "integrity": "sha512-yemi4pMf51WKT7khInJqAvsIGzoqYXblnsz0ql8tM+yi1EKYTY1evX4NAbJrLL/Aanr2HyZeluqU+Oi7MGHokw==", + "dev": true, + "dependencies": { + "minimatch": "^5.0.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==", + "dev": true, + "optional": true, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/immutable": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.1.tgz", + "integrity": "sha512-lj9cnmB/kVS0QHsJnYKD1uo3o39nrbKxszjnqS9Fr6NB7bZzW45U6WSGBPKXDL/CvDKqDNPA4r3DoDQ8GTxo2A==", + "dev": true + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "dev": true + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-3.0.0.tgz", + "integrity": "sha512-TxYQaeNW/N8ymDvwAxPyRbhMBtnEwuvaTYpOQkFx1nSeusgezHniEc/l35Vo4iCq/mMiTJbpD7oYxN98hFlfmw==", + "dev": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/inquirer": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.4.tgz", + "integrity": "sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/inquirer/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/inquirer/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/inquirer/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/inquirer/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/inquirer/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/inquirer/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/inversify": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/inversify/-/inversify-6.0.1.tgz", + "integrity": "sha512-B3ex30927698TJENHR++8FfEaJGqoWOgI6ZY5Ht/nLUsFCwHn6akbwtnUAPCgUepAnTpe2qHxhDNjoKLyz6rgQ==" + }, + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "dev": true, + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/ip-address/node_modules/sprintf-js": { + "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 + }, + "node_modules/ipaddr.js": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.1.0.tgz", + "integrity": "sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", + "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", + "dev": true + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-what": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", + "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==", + "dev": true + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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/isbinaryfile": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", + "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", + "dev": true, + "engines": { + "node": ">= 8.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isomorphic-dompurify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-dompurify/-/isomorphic-dompurify-2.0.0.tgz", + "integrity": "sha512-BJvrSQzg7jleSaySaWyhzGqH9/QxYc3sflm5fvjcXWAQcHQvQPQdCN0ORyqvMqnQDbwFuZXvqh2IcuVa3dG/DA==", + "dependencies": { + "@types/dompurify": "^3.0.3", + "dompurify": "^3.0.6", + "jsdom": "^23.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument/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/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/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/istanbul-lib-report/node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/istanbul-lib-report/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/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", + "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jackspeak": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.2.2.tgz", + "integrity": "sha512-mgNtVv4vUuaKA97yxUHoA3+FkuhtxkjdXEWOyB/N76fjy0FjezEt34oy3epBtvCvS+7DyKwqCFWx/oJLV5+kCg==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jasmine-core": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-4.6.0.tgz", + "integrity": "sha512-O236+gd0ZXS8YAjFx8xKaJ94/erqUliEkJTDedyE7iHvv4ZVqi+q+8acJxu05/WJDKm512EUNn809In37nWlAQ==", + "dev": true + }, + "node_modules/javascript-natural-sort": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz", + "integrity": "sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==", + "dev": true + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/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-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jiti": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.19.1.tgz", + "integrity": "sha512-oVhqoRDaBXf7sjkll95LHVS6Myyyb1zaunVwk4Z0+WPSW4gjS0pl01zYKHScTuyEhQsFxV5L4DR5r+YqSyqyyg==", + "dev": true, + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "dev": true + }, + "node_modules/jsdom": { + "version": "23.0.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-23.0.1.tgz", + "integrity": "sha512-2i27vgvlUsGEBO9+/kJQRbtqtm+191b5zAZrU/UezVmnC2dlDAFLgDYJvAEi94T4kjsRKkezEtLQTgsNEsW2lQ==", + "dependencies": { + "cssstyle": "^3.0.0", + "data-urls": "^5.0.0", + "decimal.js": "^10.4.3", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.2", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.7", + "parse5": "^7.1.2", + "rrweb-cssom": "^0.6.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.1.3", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0", + "ws": "^8.14.2", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "canvas": "^2.11.2" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsdom/node_modules/agent-base": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/jsdom/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/jsdom/node_modules/http-proxy-agent": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz", + "integrity": "sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/jsdom/node_modules/https-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", + "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/jsdom/node_modules/parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "dependencies": { + "entities": "^4.4.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/jsdom/node_modules/tr46": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", + "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/jsdom/node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "engines": { + "node": ">=12" + } + }, + "node_modules/jsdom/node_modules/whatwg-url": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.0.0.tgz", + "integrity": "sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==", + "dependencies": { + "tr46": "^5.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/json-stable-stringify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.2.tgz", + "integrity": "sha512-eunSSaEnxV12z+Z73y/j5N37/In40GK4GmsSy+tEHJMxknvqnA7/djeYtAgW0GsWHUfg+847WJjKaEylk2y09g==", + "dependencies": { + "jsonify": "^0.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.1.0.tgz", + "integrity": "sha512-DRf0QjnNeCUds3xTjKlQQ3DpJD51GvDjJfnxUVWg6PZTo2otSm+slzNAxU/35hF8/oJIKoG9slq30JYOsF2azg==", + "dev": true + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz", + "integrity": "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "dev": true, + "engines": [ + "node >= 0.2.0" + ] + }, + "node_modules/karma": { + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/karma/-/karma-6.4.3.tgz", + "integrity": "sha512-LuucC/RE92tJ8mlCwqEoRWXP38UMAqpnq98vktmS9SznSoUPPUJQbc91dHcxcunROvfQjdORVA/YFviH+Xci9Q==", + "dev": true, + "dependencies": { + "@colors/colors": "1.5.0", + "body-parser": "^1.19.0", + "braces": "^3.0.2", + "chokidar": "^3.5.1", + "connect": "^3.7.0", + "di": "^0.0.1", + "dom-serialize": "^2.2.1", + "glob": "^7.1.7", + "graceful-fs": "^4.2.6", + "http-proxy": "^1.18.1", + "isbinaryfile": "^4.0.8", + "lodash": "^4.17.21", + "log4js": "^6.4.1", + "mime": "^2.5.2", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.5", + "qjobs": "^1.2.0", + "range-parser": "^1.2.1", + "rimraf": "^3.0.2", + "socket.io": "^4.7.2", + "source-map": "^0.6.1", + "tmp": "^0.2.1", + "ua-parser-js": "^0.7.30", + "yargs": "^16.1.1" + }, + "bin": { + "karma": "bin/karma" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/karma-chrome-launcher": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.2.0.tgz", + "integrity": "sha512-rE9RkUPI7I9mAxByQWkGJFXfFD6lE4gC5nPuZdobf/QdTEJI6EU4yIay/cfU/xV4ZxlM5JiTv7zWYgA64NpS5Q==", + "dev": true, + "dependencies": { + "which": "^1.2.1" + } + }, + "node_modules/karma-coverage": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/karma-coverage/-/karma-coverage-2.2.1.tgz", + "integrity": "sha512-yj7hbequkQP2qOSb20GuNSIyE//PgJWHwC2IydLE6XRtsnaflv+/OSGNssPjobYUlhVVagy99TQpqUt3vAUG7A==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.1", + "istanbul-reports": "^3.0.5", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/karma-coverage/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, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/karma-coverage/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, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/karma-jasmine": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-5.1.0.tgz", + "integrity": "sha512-i/zQLFrfEpRyQoJF9fsCdTMOF5c2dK7C7OmsuKg2D0YSsuZSfQDiLuaiktbuio6F2wiCsZSnSnieIQ0ant/uzQ==", + "dev": true, + "dependencies": { + "jasmine-core": "^4.1.0" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "karma": "^6.0.0" + } + }, + "node_modules/karma-jasmine-html-reporter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-2.1.0.tgz", + "integrity": "sha512-sPQE1+nlsn6Hwb5t+HHwyy0A1FNCVKuL1192b+XNauMYWThz2kweiBVW1DqloRpVvZIJkIoHVB7XRpK78n1xbQ==", + "dev": true, + "peerDependencies": { + "jasmine-core": "^4.0.0 || ^5.0.0", + "karma": "^6.0.0", + "karma-jasmine": "^5.0.0" + } + }, + "node_modules/karma-source-map-support": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz", + "integrity": "sha512-RsBECncGO17KAoJCYXjv+ckIz+Ii9NCi+9enk+rq6XC81ezYkb4/RHE6CTXdA7IOJqoF3wcaLfVG0CPmE5ca6A==", + "dev": true, + "dependencies": { + "source-map-support": "^0.5.5" + } + }, + "node_modules/karma/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, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/karma/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/karma/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, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/karma/node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/karma/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/karma/node_modules/tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "dependencies": { + "rimraf": "^3.0.0" + }, + "engines": { + "node": ">=8.17.0" + } + }, + "node_modules/karma/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/karma/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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/less": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/less/-/less-4.1.3.tgz", + "integrity": "sha512-w16Xk/Ta9Hhyei0Gpz9m7VS8F28nieJaL/VyShID7cYvP6IL5oHeL6p4TXSDJqZE/lNv0oJ2pGVjJsRkfwm5FA==", + "dev": true, + "dependencies": { + "copy-anything": "^2.0.1", + "parse-node-version": "^1.0.1", + "tslib": "^2.3.0" + }, + "bin": { + "lessc": "bin/lessc" + }, + "engines": { + "node": ">=6" + }, + "optionalDependencies": { + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "make-dir": "^2.1.0", + "mime": "^1.4.1", + "needle": "^3.1.0", + "source-map": "~0.6.0" + } + }, + "node_modules/less-loader": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-11.0.0.tgz", + "integrity": "sha512-9+LOWWjuoectIEx3zrfN83NAGxSUB5pWEabbbidVQVgZhN+wN68pOvuyirVlH1IK4VT1f3TmlyvAnCXh8O5KEw==", + "dev": true, + "dependencies": { + "klona": "^2.0.4" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "less": "^3.5.0 || ^4.0.0", + "webpack": "^5.0.0" + } + }, + "node_modules/less/node_modules/make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "optional": true, + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/less/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "optional": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/less/node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/less/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "optional": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/less/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/license-webpack-plugin": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-4.0.2.tgz", + "integrity": "sha512-771TFWFD70G1wLTC4oU2Cw4qvtmNrIw+wRvBtn+okgHl7slJVi7zfNcdmqDL72BojM30VNJ2UHylr1o77U37Jw==", + "dev": true, + "dependencies": { + "webpack-sources": "^3.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + }, + "webpack-sources": { + "optional": true + } + } + }, + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true, + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/loader-utils": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz", + "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==", + "dev": true, + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, + "node_modules/lodash.castarray": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz", + "integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==", + "dev": true + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true + }, + "node_modules/lodash.isempty": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.isempty/-/lodash.isempty-4.4.0.tgz", + "integrity": "sha512-oKMuF3xEeqDltrGMfDxAPGIVMSSRv8tbRSODbrs4KGsRRLEhrW8N8Rd4DRgB2+621hY8A8XwwrTVhXWpxFvMzg==" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lodash.transform": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.transform/-/lodash.transform-4.6.0.tgz", + "integrity": "sha512-LO37ZnhmBVx0GvOU/caQuipEh4GN82TcWv3yHlebGDgOxbxiwwzW5Pcx2AcvpIv2WmvmSMoC492yQFNhy/l/UQ==" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/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/log-symbols/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/log-symbols/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/log-symbols/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/log-symbols/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/log-symbols/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/log4js": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.9.1.tgz", + "integrity": "sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==", + "dev": true, + "dependencies": { + "date-format": "^4.0.14", + "debug": "^4.3.4", + "flatted": "^3.2.7", + "rfdc": "^1.3.0", + "streamroller": "^3.1.5" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/magic-string": { + "version": "0.26.2", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.2.tgz", + "integrity": "sha512-NzzlXpclt5zAbmo6h6jNc8zl2gNRGHvmsZW4IvZhTC4W7k4OlLP+S5YLussa/r3ixNT66KOQfNORlXHSOy/X4A==", + "dev": true, + "dependencies": { + "sourcemap-codec": "^1.4.8" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/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/make-fetch-happen": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-11.1.1.tgz", + "integrity": "sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w==", + "dev": true, + "dependencies": { + "agentkeepalive": "^4.2.1", + "cacache": "^17.0.0", + "http-cache-semantics": "^4.1.1", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^5.0.0", + "minipass-fetch": "^3.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^10.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/make-fetch-happen/node_modules/@npmcli/fs": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.0.tgz", + "integrity": "sha512-7kZUAaLscfgbwBQRbvdMYaZOWyMEcPTH/tJjnyAWJ/dvvs9Ef+CERx/qJb9GExJpl1qipaDGn7KqHnFGGixd0w==", + "dev": true, + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/make-fetch-happen/node_modules/cacache": { + "version": "17.1.3", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-17.1.3.tgz", + "integrity": "sha512-jAdjGxmPxZh0IipMdR7fK/4sDSrHMLUV0+GvVUsjwyGNKHsh79kW/otg+GkbXwl6Uzvy9wsvHOX4nUoWldeZMg==", + "dev": true, + "dependencies": { + "@npmcli/fs": "^3.1.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^7.7.1", + "minipass": "^5.0.0", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^4.0.0", + "ssri": "^10.0.0", + "tar": "^6.1.11", + "unique-filename": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/make-fetch-happen/node_modules/fs-minipass": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.2.tgz", + "integrity": "sha512-2GAfyfoaCDRrM6jaOS3UsBts8yJ55VioXdWcOL7dK9zdAuKT71+WBA4ifnNYqVjYv+4SsPxjK0JT4yIIn4cA/g==", + "dev": true, + "dependencies": { + "minipass": "^5.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/make-fetch-happen/node_modules/glob": { + "version": "10.3.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.3.tgz", + "integrity": "sha512-92vPiMb/iqpmEgsOoIDvTjc50wf9CCCvMzsi6W0JLPeUKE8TWP1a73PgqSrqy7iAZxaSD1YdzU7QZR5LF51MJw==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.0.3", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/make-fetch-happen/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/make-fetch-happen/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/make-fetch-happen/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/make-fetch-happen/node_modules/ssri": { + "version": "10.0.4", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.4.tgz", + "integrity": "sha512-12+IR2CB2C28MMAw0Ncqwj5QbTcs0nGIhgJzYWzDkb21vWmfNI83KS4f3Ci6GI98WreIfG7o9UXp3C0qbpA8nQ==", + "dev": true, + "dependencies": { + "minipass": "^5.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/make-fetch-happen/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, + "dependencies": { + "unique-slug": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/make-fetch-happen/node_modules/unique-slug": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz", + "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/marked": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/marked/-/marked-11.1.1.tgz", + "integrity": "sha512-EgxRjgK9axsQuUa/oKMx5DEY8oXpKJfk61rT5iY3aRlgU6QJtUcxU5OAymdhCvWvhYcd9FKmO5eQoX8m9VGJXg==", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memfs": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", + "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", + "dev": true, + "dependencies": { + "fs-monkey": "^1.0.4" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", + "dev": true + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/mini-css-extract-plugin": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.6.1.tgz", + "integrity": "sha512-wd+SD57/K6DiV7jIR34P+s3uckTRuQvx0tKPcvjFlrEylk6P4mQ2KSWk1hblj1Kxaqok7LogKOieygXqBczNlg==", + "dev": true, + "dependencies": { + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "node_modules/minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-fetch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.3.tgz", + "integrity": "sha512-n5ITsTkDqYkYJZjcRWzZt9qnZKCT7nKCosJhHoj7S7zD+BP4jVbWs+odsniw5TA3E0sLomhTKOKjF86wf11PuQ==", + "dev": true, + "dependencies": { + "minipass": "^5.0.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/minipass-fetch/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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-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==", + "dev": true, + "dependencies": { + "jsonparse": "^1.3.1", + "minipass": "^3.0.0" + } + }, + "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, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass/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 + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/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 + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/multicast-dns": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", + "dev": true, + "dependencies": { + "dns-packet": "^5.2.2", + "thunky": "^1.0.2" + }, + "bin": { + "multicast-dns": "cli.js" + } + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/needle": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/needle/-/needle-3.2.0.tgz", + "integrity": "sha512-oUvzXnyLiVyVGoianLijF9O/RecZUf7TkBfimjGrLM4eQhXyeJwM6GeAWccwfQ9aa4gMCZKqhAOuLaMIcQxajQ==", + "dev": true, + "optional": true, + "dependencies": { + "debug": "^3.2.6", + "iconv-lite": "^0.6.3", + "sax": "^1.2.4" + }, + "bin": { + "needle": "bin/needle" + }, + "engines": { + "node": ">= 4.4.x" + } + }, + "node_modules/needle/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "optional": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/needle/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node_modules/ng2-charts": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ng2-charts/-/ng2-charts-4.1.1.tgz", + "integrity": "sha512-iHwXDbmX86lfeH8VRcsaW2tJATsuAZo4kvvC/Yk2l35zOHjevja1qBvO6BAibiDazi9r9aS6ZRJOqWPsz1pP2w==", + "dependencies": { + "lodash-es": "^4.17.15", + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/cdk": ">=14.0.0", + "@angular/common": ">=14.0.0", + "@angular/core": ">=14.0.0", + "chart.js": "^3.4.0 || ^4.0.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/ngx-build-plus": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/ngx-build-plus/-/ngx-build-plus-14.0.0.tgz", + "integrity": "sha512-Rq23dNx9jL34+hbCrSa7LYrkopZpKN6WGAfPRaFlCeuJ/b5YIPVk8cePjj3/CDZr7LlNPedTrEtMC1dpG4AXww==", + "dev": true, + "dependencies": { + "@angular-devkit/build-angular": ">=14.0.0", + "@schematics/angular": ">=14.0.0", + "webpack-merge": "^5.0.0" + }, + "peerDependencies": { + "@angular-devkit/build-angular": ">=12.0.0", + "rxjs": ">= 6.0.0" + } + }, + "node_modules/ngx-json-viewer": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ngx-json-viewer/-/ngx-json-viewer-3.2.1.tgz", + "integrity": "sha512-TTHtXsrBX+IXPqqAIsxklHPqSNmyGeQaziFZbCDJq1PnPOQmTrEHfwNrzN3LnWGhf7UxeM1cK0njegVPChwEcg==", + "dependencies": { + "tslib": "^2.3.0" + } + }, + "node_modules/nice-napi": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz", + "integrity": "sha512-px/KnJAJZf5RuBGcfD+Sp2pAKq0ytz8j+1NehvgIGFkvtvFrDM3T8E4x/JJODXK9WZow8RRGrbA9QQ3hs+pDhA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "!win32" + ], + "dependencies": { + "node-addon-api": "^3.0.0", + "node-gyp-build": "^4.2.2" + } + }, + "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, + "optional": true + }, + "node_modules/node-fetch": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz", + "integrity": "sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "dev": true, + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/node-gyp": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-9.4.0.tgz", + "integrity": "sha512-dMXsYP6gc9rRbejLXmTbVRYjAHw7ppswsKyMxuxJxxOHzluIO1rGp9TOQgjFJ+2MCqcOcQTOPB/8Xwhr+7s4Eg==", + "dev": true, + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^11.0.3", + "nopt": "^6.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^12.13 || ^14.13 || >=16" + } + }, + "node_modules/node-gyp-build": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.0.tgz", + "integrity": "sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==", + "dev": true, + "optional": true, + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/node-gyp/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, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/node-gyp/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/node-gyp/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, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/node-gyp/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/node-releases": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", + "dev": true + }, + "node_modules/nopt": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz", + "integrity": "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==", + "dev": true, + "dependencies": { + "abbrev": "^1.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/normalize-package-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-4.0.1.tgz", + "integrity": "sha512-EBk5QKKuocMJhB3BILuKhmaPjI8vNRSpIfO9woLC6NyHVkKKdVEdAO1mrT0ZfxNR1lKwCcTkuZfmGIFdizZ8Pg==", + "dev": true, + "dependencies": { + "hosted-git-info": "^5.0.0", + "is-core-module": "^2.8.1", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-bundled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz", + "integrity": "sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==", + "dev": true, + "dependencies": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "node_modules/npm-install-checks": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-5.0.0.tgz", + "integrity": "sha512-65lUsMI8ztHCxFz5ckCEC44DRvEGdZX5usQFriauxHEwt7upv1FKaQEmAtU0YnOAdwuNWCmk64xYiQABNrEyLA==", + "dev": true, + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-normalize-package-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", + "dev": true + }, + "node_modules/npm-package-arg": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-9.1.0.tgz", + "integrity": "sha512-4J0GL+u2Nh6OnhvUKXRr2ZMG4lR8qtLp+kv7UiV00Y+nGiSxtttCyIRHCt5L5BNkXQld/RceYItau3MDOoGiBw==", + "dev": true, + "dependencies": { + "hosted-git-info": "^5.0.0", + "proc-log": "^2.0.1", + "semver": "^7.3.5", + "validate-npm-package-name": "^4.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-packlist": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-5.1.3.tgz", + "integrity": "sha512-263/0NGrn32YFYi4J533qzrQ/krmmrWwhKkzwTuM4f/07ug51odoaNjUexxO4vxlzURHcmYMH1QjvHjsNDKLVg==", + "dev": true, + "dependencies": { + "glob": "^8.0.1", + "ignore-walk": "^5.0.1", + "npm-bundled": "^2.0.0", + "npm-normalize-package-bin": "^2.0.0" + }, + "bin": { + "npm-packlist": "bin/index.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-packlist/node_modules/npm-bundled": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-2.0.1.tgz", + "integrity": "sha512-gZLxXdjEzE/+mOstGDqR6b0EkhJ+kM6fxM6vUuckuctuVPh80Q6pw/rSZj9s4Gex9GxWtIicO1pc8DB9KZWudw==", + "dev": true, + "dependencies": { + "npm-normalize-package-bin": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-packlist/node_modules/npm-normalize-package-bin": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-2.0.0.tgz", + "integrity": "sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ==", + "dev": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-pick-manifest": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-7.0.1.tgz", + "integrity": "sha512-IA8+tuv8KujbsbLQvselW2XQgmXWS47t3CB0ZrzsRZ82DbDfkcFunOaPm4X7qNuhMfq+FmV7hQT4iFVpHqV7mg==", + "dev": true, + "dependencies": { + "npm-install-checks": "^5.0.0", + "npm-normalize-package-bin": "^1.0.1", + "npm-package-arg": "^9.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-registry-fetch": { + "version": "13.3.1", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-13.3.1.tgz", + "integrity": "sha512-eukJPi++DKRTjSBRcDZSDDsGqRK3ehbxfFUcgaRd0Yp6kRwOwh2WVn0r+8rMB4nnuzvAk6rQVzl6K5CkYOmnvw==", + "dev": true, + "dependencies": { + "make-fetch-happen": "^10.0.6", + "minipass": "^3.1.6", + "minipass-fetch": "^2.0.3", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.1.2", + "npm-package-arg": "^9.0.1", + "proc-log": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-registry-fetch/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/npm-registry-fetch/node_modules/make-fetch-happen": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz", + "integrity": "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==", + "dev": true, + "dependencies": { + "agentkeepalive": "^4.2.1", + "cacache": "^16.1.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^2.0.3", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^9.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-registry-fetch/node_modules/minipass-fetch": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-2.1.2.tgz", + "integrity": "sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==", + "dev": true, + "dependencies": { + "minipass": "^3.1.6", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npmlog": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", + "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "dev": true, + "dependencies": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/nwsapi": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz", + "integrity": "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "dev": true, + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/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/ora/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/ora/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/ora/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/ora/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/ora/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/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "dev": true, + "dependencies": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-retry/node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pacote": { + "version": "13.6.2", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-13.6.2.tgz", + "integrity": "sha512-Gu8fU3GsvOPkak2CkbojR7vjs3k3P9cA6uazKTHdsdV0gpCEQq2opelnEv30KRQWgVzP5Vd/5umjcedma3MKtg==", + "dev": true, + "dependencies": { + "@npmcli/git": "^3.0.0", + "@npmcli/installed-package-contents": "^1.0.7", + "@npmcli/promise-spawn": "^3.0.0", + "@npmcli/run-script": "^4.1.0", + "cacache": "^16.0.0", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "infer-owner": "^1.0.4", + "minipass": "^3.1.6", + "mkdirp": "^1.0.4", + "npm-package-arg": "^9.0.0", + "npm-packlist": "^5.1.0", + "npm-pick-manifest": "^7.0.0", + "npm-registry-fetch": "^13.0.1", + "proc-log": "^2.0.0", + "promise-retry": "^2.0.1", + "read-package-json": "^5.0.0", + "read-package-json-fast": "^2.0.3", + "rimraf": "^3.0.2", + "ssri": "^9.0.0", + "tar": "^6.1.11" + }, + "bin": { + "pacote": "lib/bin.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "optional": true + }, + "node_modules/parse5-html-rewriting-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-6.0.1.tgz", + "integrity": "sha512-vwLQzynJVEfUlURxgnf51yAJDQTtVpNyGD8tKi2Za7m+akukNHxCcUQMAa/mUGLhCeicFdpy7Tlvj8ZNKadprg==", + "dev": true, + "dependencies": { + "parse5": "^6.0.1", + "parse5-sax-parser": "^6.0.1" + } + }, + "node_modules/parse5-html-rewriting-stream/node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", + "dev": true, + "dependencies": { + "parse5": "^6.0.1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + }, + "node_modules/parse5-sax-parser": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-6.0.1.tgz", + "integrity": "sha512-kXX+5S81lgESA0LsDuGjAlBybImAChYRMT+/uKCEXFBFOeEhS52qUCydGhU3qLRD8D9DVjaUo821WK7DM4iCeg==", + "dev": true, + "dependencies": { + "parse5": "^6.0.1" + } + }, + "node_modules/parse5-sax-parser/node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-scurry": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", + "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "dev": true, + "dependencies": { + "lru-cache": "^9.1.1 || ^10.0.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.0.tgz", + "integrity": "sha512-svTf/fzsKHffP42sujkO/Rjs37BCIsQVRCeNYIm9WN8rgT7ffoUnRtZCqU+6BqcSBdv8gwJeTz8knJpgACeQMw==", + "dev": true, + "engines": { + "node": "14 || >=16.14" + } + }, + "node_modules/path-scurry/node_modules/minipass": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.2.tgz", + "integrity": "sha512-eL79dXrE1q9dBbDCLg7xfn/vl7MS4F1gvJAgjJrQli/jbQWdUttuVawphqpffoIYfRdq78LHx6GP4bU/EQ2ATA==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", + "dev": true + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/piscina": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/piscina/-/piscina-3.2.0.tgz", + "integrity": "sha512-yn/jMdHRw+q2ZJhFhyqsmANcbF6V2QwmD84c6xRau+QpQOmtrBCoRGdvTfeuFDYXB5W2m6MfLkjkvQa9lUSmIA==", + "dev": true, + "dependencies": { + "eventemitter-asyncresource": "^1.0.0", + "hdr-histogram-js": "^2.0.1", + "hdr-histogram-percentiles-obj": "^3.0.0" + }, + "optionalDependencies": { + "nice-napi": "^1.0.2" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-attribute-case-insensitive": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.2.tgz", + "integrity": "sha512-XIidXV8fDr0kKt28vqki84fRK8VW8eTuIa4PChv2MqKuT6C9UjmSKzen6KaWhWEoYvwxFCa7n/tC1SZ3tyq4SQ==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-clamp": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-clamp/-/postcss-clamp-4.1.0.tgz", + "integrity": "sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": ">=7.6.0" + }, + "peerDependencies": { + "postcss": "^8.4.6" + } + }, + "node_modules/postcss-color-functional-notation": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-4.2.4.tgz", + "integrity": "sha512-2yrTAUZUab9s6CpxkxC4rVgFEVaR6/2Pipvi6qcgvnYiVqZcbDHEoBDhrXzyb7Efh2CCfHQNtcqWcIruDTIUeg==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-color-hex-alpha": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-8.0.4.tgz", + "integrity": "sha512-nLo2DCRC9eE4w2JmuKgVA3fGL3d01kGq752pVALF68qpGLmx2Qrk91QTKkdUqqp45T1K1XV8IhQpcu1hoAQflQ==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-color-rebeccapurple": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-7.1.1.tgz", + "integrity": "sha512-pGxkuVEInwLHgkNxUc4sdg4g3py7zUeCQ9sMfwyHAT+Ezk8a4OaaVZ8lIY5+oNqA/BXXgLyXv0+5wHP68R79hg==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-custom-media": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.2.tgz", + "integrity": "sha512-7yi25vDAoHAkbhAzX9dHx2yc6ntS4jQvejrNcC+csQJAXjj15e7VcWfMgLqBNAbOvqi5uIa9huOVwdHbf+sKqg==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.3" + } + }, + "node_modules/postcss-custom-properties": { + "version": "12.1.11", + "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.11.tgz", + "integrity": "sha512-0IDJYhgU8xDv1KY6+VgUwuQkVtmYzRwu+dMjnmdMafXYv86SWqfxkc7qdDvWS38vsjaEtv8e0vGOUQrAiMBLpQ==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-custom-selectors": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-6.0.3.tgz", + "integrity": "sha512-fgVkmyiWDwmD3JbpCmB45SvvlCD6z9CG6Ie6Iere22W5aHea6oWa7EM2bpnv2Fj3I94L3VbtvX9KqwSi5aFzSg==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.3" + } + }, + "node_modules/postcss-dir-pseudo-class": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-6.0.5.tgz", + "integrity": "sha512-eqn4m70P031PF7ZQIvSgy9RSJ5uI2171O/OO/zcRNYpJbvaeKFUlar1aJ7rmgiQtbm0FSPsRewjpdS0Oew7MPA==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-double-position-gradients": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-3.1.2.tgz", + "integrity": "sha512-GX+FuE/uBR6eskOK+4vkXgT6pDkexLokPaz/AbJna9s5Kzp/yl488pKPjhy0obB475ovfT1Wv8ho7U/cHNaRgQ==", + "dev": true, + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-env-function": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-4.0.6.tgz", + "integrity": "sha512-kpA6FsLra+NqcFnL81TnsU+Z7orGtDTxcOhl6pwXeEq1yFPpRMkCDpHhrz8CFQDr/Wfm0jLiNQ1OsGGPjlqPwA==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-focus-visible": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-6.0.4.tgz", + "integrity": "sha512-QcKuUU/dgNsstIK6HELFRT5Y3lbrMLEOwG+A4s5cA+fx3A3y/JTq3X9LaOj3OC3ALH0XqyrgQIgey/MIZ8Wczw==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-focus-within": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-5.0.4.tgz", + "integrity": "sha512-vvjDN++C0mu8jz4af5d52CB184ogg/sSxAFS+oUJQq2SuCe7T5U2iIsVJtsCp2d6R4j0jr5+q3rPkBVZkXD9fQ==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-font-variant": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", + "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", + "dev": true, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-gap-properties": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.5.tgz", + "integrity": "sha512-IuE6gKSdoUNcvkGIqdtjtcMtZIFyXZhmFd5RUlg97iVEvp1BZKV5ngsAjCjrVy+14uhGBQl9tzmi1Qwq4kqVOg==", + "dev": true, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-image-set-function": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-4.0.7.tgz", + "integrity": "sha512-9T2r9rsvYzm5ndsBE8WgtrMlIT7VbtTfE7b3BQnudUqnBcBo7L758oc+o+pdj/dUV0l5wjwSdjeOH2DZtfv8qw==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-initial": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz", + "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==", + "dev": true, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "dev": true, + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-lab-function": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-4.2.1.tgz", + "integrity": "sha512-xuXll4isR03CrQsmxyz92LJB2xX9n+pZJ5jE9JgcnmsCammLyKdlzrBin+25dy6wIjfhJpKBAN80gsTlCgRk2w==", + "dev": true, + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-load-config": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz", + "integrity": "sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==", + "dev": true, + "dependencies": { + "lilconfig": "^2.0.5", + "yaml": "^2.1.1" + }, + "engines": { + "node": ">= 14" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-loader": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.3.3.tgz", + "integrity": "sha512-YgO/yhtevGO/vJePCQmTxiaEwER94LABZN0ZMT4A0vsak9TpO+RvKRs7EmJ8peIlB9xfXCsS7M8LjqncsUZ5HA==", + "dev": true, + "dependencies": { + "cosmiconfig": "^8.2.0", + "jiti": "^1.18.2", + "semver": "^7.3.8" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "postcss": "^7.0.0 || ^8.0.1", + "webpack": "^5.0.0" + } + }, + "node_modules/postcss-logical": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz", + "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==", + "dev": true, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-media-minmax": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz", + "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.3.tgz", + "integrity": "sha512-2/u2zraspoACtrbFRnTijMiQtb4GW4BvatjaG/bCjYQo8kLTdevCUlwuBHx2sCnSyrI3x3qj4ZK1j5LQBgzmwA==", + "dev": true, + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", + "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dev": true, + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-nested": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", + "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.11" + }, + "engines": { + "node": ">=12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-nesting": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-10.2.0.tgz", + "integrity": "sha512-EwMkYchxiDiKUhlJGzWsD9b2zvq/r2SSubcRrgP+jujMXFzqvANLt16lJANC+5uZ6hjI7lpRmI6O8JIl+8l1KA==", + "dev": true, + "dependencies": { + "@csstools/selector-specificity": "^2.0.0", + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-opacity-percentage": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/postcss-opacity-percentage/-/postcss-opacity-percentage-1.1.3.tgz", + "integrity": "sha512-An6Ba4pHBiDtyVpSLymUUERMo2cU7s+Obz6BTrS+gxkbnSBNKSuD0AVUc+CpBMrpVPKKfoVz0WQCX+Tnst0i4A==", + "dev": true, + "funding": [ + { + "type": "kofi", + "url": "https://ko-fi.com/mrcgrtz" + }, + { + "type": "liberapay", + "url": "https://liberapay.com/mrcgrtz" + } + ], + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-overflow-shorthand": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.4.tgz", + "integrity": "sha512-otYl/ylHK8Y9bcBnPLo3foYFLL6a6Ak+3EQBPOTR7luMYCOsiVTUk1iLvNf6tVPNGXcoL9Hoz37kpfriRIFb4A==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-page-break": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", + "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", + "dev": true, + "peerDependencies": { + "postcss": "^8" + } + }, + "node_modules/postcss-place": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-7.0.5.tgz", + "integrity": "sha512-wR8igaZROA6Z4pv0d+bvVrvGY4GVHihBCBQieXFY3kuSuMyOmEnnfFzHl/tQuqHZkfkIVBEbDvYcFfHmpSet9g==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-preset-env": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-7.8.0.tgz", + "integrity": "sha512-leqiqLOellpLKfbHkD06E04P6d9ZQ24mat6hu4NSqun7WG0UhspHR5Myiv/510qouCjoo4+YJtNOqg5xHaFnCA==", + "dev": true, + "dependencies": { + "@csstools/postcss-cascade-layers": "^1.0.5", + "@csstools/postcss-color-function": "^1.1.1", + "@csstools/postcss-font-format-keywords": "^1.0.1", + "@csstools/postcss-hwb-function": "^1.0.2", + "@csstools/postcss-ic-unit": "^1.0.1", + "@csstools/postcss-is-pseudo-class": "^2.0.7", + "@csstools/postcss-nested-calc": "^1.0.0", + "@csstools/postcss-normalize-display-values": "^1.0.1", + "@csstools/postcss-oklab-function": "^1.1.1", + "@csstools/postcss-progressive-custom-properties": "^1.3.0", + "@csstools/postcss-stepped-value-functions": "^1.0.1", + "@csstools/postcss-text-decoration-shorthand": "^1.0.0", + "@csstools/postcss-trigonometric-functions": "^1.0.2", + "@csstools/postcss-unset-value": "^1.0.2", + "autoprefixer": "^10.4.8", + "browserslist": "^4.21.3", + "css-blank-pseudo": "^3.0.3", + "css-has-pseudo": "^3.0.4", + "css-prefers-color-scheme": "^6.0.3", + "cssdb": "^7.0.0", + "postcss-attribute-case-insensitive": "^5.0.2", + "postcss-clamp": "^4.1.0", + "postcss-color-functional-notation": "^4.2.4", + "postcss-color-hex-alpha": "^8.0.4", + "postcss-color-rebeccapurple": "^7.1.1", + "postcss-custom-media": "^8.0.2", + "postcss-custom-properties": "^12.1.8", + "postcss-custom-selectors": "^6.0.3", + "postcss-dir-pseudo-class": "^6.0.5", + "postcss-double-position-gradients": "^3.1.2", + "postcss-env-function": "^4.0.6", + "postcss-focus-visible": "^6.0.4", + "postcss-focus-within": "^5.0.4", + "postcss-font-variant": "^5.0.0", + "postcss-gap-properties": "^3.0.5", + "postcss-image-set-function": "^4.0.7", + "postcss-initial": "^4.0.1", + "postcss-lab-function": "^4.2.1", + "postcss-logical": "^5.0.4", + "postcss-media-minmax": "^5.0.0", + "postcss-nesting": "^10.1.10", + "postcss-opacity-percentage": "^1.1.2", + "postcss-overflow-shorthand": "^3.0.4", + "postcss-page-break": "^3.0.4", + "postcss-place": "^7.0.5", + "postcss-pseudo-class-any-link": "^7.1.6", + "postcss-replace-overflow-wrap": "^4.0.0", + "postcss-selector-not": "^6.0.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-pseudo-class-any-link": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-7.1.6.tgz", + "integrity": "sha512-9sCtZkO6f/5ML9WcTLcIyV1yz9D1rf0tWc+ulKcvV30s0iZKS/ONyETvoWsr6vnrmW+X+KmuK3gV/w5EWnT37w==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-replace-overflow-wrap": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", + "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", + "dev": true, + "peerDependencies": { + "postcss": "^8.0.3" + } + }, + "node_modules/postcss-scss": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-4.0.9.tgz", + "integrity": "sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss-scss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.4.29" + } + }, + "node_modules/postcss-selector-not": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-6.0.1.tgz", + "integrity": "sha512-1i9affjAe9xu/y9uqWH+tD4r6/hDaXJruk8xn2x1vzxC2U3J3LKO3zJW4CyxlNhA56pADJ/djpEwpH1RClI2rQ==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.13", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz", + "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-plugin-organize-attributes": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/prettier-plugin-organize-attributes/-/prettier-plugin-organize-attributes-0.0.5.tgz", + "integrity": "sha512-dSts16q8wd+oq8Zwk5mwmYXo1aN3B+ZkEJqx/ar5fedNHdOvx7S4XDMH/pNK7rmBW0bPXkp/kJX5gAANsWzh3A==", + "dev": true, + "engines": { + "node": ">=11.0.0" + }, + "peerDependencies": { + "prettier": "^2.0.0" + } + }, + "node_modules/pretty-bytes": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/proc-log": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-2.0.1.tgz", + "integrity": "sha512-Kcmo2FhfDTXdcbfDH76N7uBYHINxc/8GW7UAVuVP9I+Va3uHSerrnKV6dLooga/gh7GlgzuCCr/eoldnL1muGw==", + "dev": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "dev": true + }, + "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, + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-addr/node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", + "dev": true, + "optional": true + }, + "node_modules/psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/qjobs": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", + "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", + "dev": true, + "engines": { + "node": ">=0.9" + } + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/read-package-json": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-5.0.2.tgz", + "integrity": "sha512-BSzugrt4kQ/Z0krro8zhTwV1Kd79ue25IhNN/VtHFy1mG/6Tluyi+msc0UpwaoQzxSHa28mntAjIZY6kEgfR9Q==", + "dev": true, + "dependencies": { + "glob": "^8.0.1", + "json-parse-even-better-errors": "^2.3.1", + "normalize-package-data": "^4.0.0", + "npm-normalize-package-bin": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/read-package-json-fast": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-2.0.3.tgz", + "integrity": "sha512-W/BKtbL+dUjTuRL2vziuYhp76s5HZ9qQhd/dKfWIZveD0O40453QNyZhC0e63lqZrAQ4jiOapVoeJ7JrszenQQ==", + "dev": true, + "dependencies": { + "json-parse-even-better-errors": "^2.3.0", + "npm-normalize-package-bin": "^1.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/read-package-json/node_modules/npm-normalize-package-bin": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-2.0.0.tgz", + "integrity": "sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ==", + "dev": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/reflect-metadata": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", + "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", + "dev": true, + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", + "dev": true + }, + "node_modules/regenerator-transform": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz", + "integrity": "sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.8.4" + } + }, + "node_modules/regex-parser": { + "version": "2.2.11", + "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz", + "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==", + "dev": true + }, + "node_modules/regexpu-core": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", + "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", + "dev": true, + "dependencies": { + "@babel/regjsgen": "^0.8.0", + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.1.0", + "regjsparser": "^0.9.1", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsparser": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", + "dev": true, + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" + }, + "node_modules/resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-url-loader": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-5.0.0.tgz", + "integrity": "sha512-uZtduh8/8srhBoMx//5bwqjQ+rfYOUq8zC9NrMUGtjBiGTtFJM42s58/36+hTqeqINcnYe08Nj3LkK9lW4N8Xg==", + "dev": true, + "dependencies": { + "adjust-sourcemap-loader": "^4.0.0", + "convert-source-map": "^1.7.0", + "loader-utils": "^2.0.0", + "postcss": "^8.2.14", + "source-map": "0.6.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/resolve-url-loader/node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/resolve-url-loader/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", + "dev": true + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/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, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/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, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/rrweb-cssom": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz", + "integrity": "sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==" + }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/sass": { + "version": "1.54.4", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.54.4.tgz", + "integrity": "sha512-3tmF16yvnBwtlPrNBHw/H907j8MlOX8aTBnlNX1yrKx24RKcJGPyLhFUwkoKBKesR3unP93/2z14Ll8NicwQUA==", + "dev": true, + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/sass-loader": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.0.2.tgz", + "integrity": "sha512-BbiqbVmbfJaWVeOOAu2o7DhYWtcNmTfvroVgFXa6k2hHheMxNAeDHLNoDy/Q5aoaVlz0LH+MbMktKwm9vN/j8Q==", + "dev": true, + "dependencies": { + "klona": "^2.0.4", + "neo-async": "^2.6.2" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "fibers": ">= 3.1.0", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", + "sass": "^1.3.0", + "sass-embedded": "*", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "fibers": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + } + } + }, + "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 + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "node_modules/schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/schema-utils/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/schema-utils/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/schema-utils/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/select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", + "dev": true + }, + "node_modules/selfsigned": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", + "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", + "dev": true, + "dependencies": { + "@types/node-forge": "^1.3.0", + "node-forge": "^1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", + "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "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==", + "dev": true, + "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==", + "dev": true + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/send/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/serialize-javascript": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", + "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", + "dev": true, + "dependencies": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-index/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/serve-index/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "dev": true, + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "dev": true + }, + "node_modules/serve-index/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/serve-index/node_modules/setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + }, + "node_modules/serve-index/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dev": true, + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socket.io": { + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.4.tgz", + "integrity": "sha512-DcotgfP1Zg9iP/dH9zvAQcWrE0TtbMVwXmlV4T4mqsvY+gw+LqUGPfx2AoVyRk0FLME+GQhufDMyacFmw7ksqw==", + "dev": true, + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.5.2", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", + "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", + "dev": true, + "dependencies": { + "debug": "~4.3.4", + "ws": "~8.17.1" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "dev": true, + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "dev": true, + "dependencies": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "node_modules/socks": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.3.tgz", + "integrity": "sha512-vfuYK48HXCTFD03G/1/zkIls3Ebr2YNa4qU9gHDZdblHLiqhJrJGkY3+0Nx0JpN9qBhJbVObc1CNciT1bIZJxw==", + "dev": true, + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", + "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", + "dev": true, + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-4.0.0.tgz", + "integrity": "sha512-i3KVgM3+QPAHNbGavK+VBq03YoJl24m9JWNbLgsjTj8aJzXG9M61bantBTNBt7CNwY2FYf+RJRYJ3pzalKjIrw==", + "dev": true, + "dependencies": { + "abab": "^2.0.6", + "iconv-lite": "^0.6.3", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.72.1" + } + }, + "node_modules/source-map-loader/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "deprecated": "Please use @jridgewell/sourcemap-codec instead", + "dev": true + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz", + "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==", + "dev": true + }, + "node_modules/spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/ssri": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz", + "integrity": "sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==", + "dev": true, + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/streamroller": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.5.tgz", + "integrity": "sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==", + "dev": true, + "dependencies": { + "date-format": "^4.0.14", + "debug": "^4.3.4", + "fs-extra": "^8.1.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/streamroller/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/streamroller/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/streamroller/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/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, + "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-loader": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-7.0.0.tgz", + "integrity": "sha512-WTbtLrNfOfLgzTaR9Lj/BPhQroKk/LC1hfTXSUbrxmxgfUo3Y3LpmKRVA2R1XbjvTAvOfaian9vOyfv1z99E+A==", + "dev": true, + "dependencies": { + "fast-glob": "^3.2.11", + "klona": "^2.0.5", + "normalize-path": "^3.0.0" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "stylus": ">=0.52.4", + "webpack": "^5.0.0" + } + }, + "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, + "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==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/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, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/sucrase": { + "version": "3.34.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.34.0.tgz", + "integrity": "sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "7.1.6", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/sucrase/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/sucrase/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, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/sucrase/node_modules/glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sucrase/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, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/symbol-observable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", + "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" + }, + "node_modules/tailwindcss": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.3.tgz", + "integrity": "sha512-A0KgSkef7eE4Mf+nKJ83i75TMyq8HqY3qmFIJSWy8bNt0v1lG7jUcpGpoTFxAwYcWOphcTBLPPJg+bDfhDf52w==", + "dev": true, + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.5.3", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.2.12", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.18.2", + "lilconfig": "^2.1.0", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.23", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.1", + "postcss-nested": "^6.0.1", + "postcss-selector-parser": "^6.0.11", + "resolve": "^1.22.2", + "sucrase": "^3.32.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tailwindcss/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/tailwindcss/node_modules/resolve": { + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", + "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", + "dev": true, + "dependencies": { + "is-core-module": "^2.11.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "dev": true, + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar/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 + }, + "node_modules/terser": { + "version": "5.14.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz", + "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", + "dev": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.9", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz", + "integrity": "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.17", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.1", + "terser": "^5.16.8" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin/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/terser-webpack-plugin/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/terser-webpack-plugin/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/terser-webpack-plugin/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/terser-webpack-plugin/node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/terser-webpack-plugin/node_modules/terser": { + "version": "5.19.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.19.2.tgz", + "integrity": "sha512-qC5+dmecKJA4cpYxRa5aVkKehYsQKc+AHeKl0Oe62aYjBL8ZA33tTljktDHJSaxxMnbI5ZYw+o/S2DxxLu8OfA==", + "dev": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser/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/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude/node_modules/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, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/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, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, + "node_modules/thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tough-cookie": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", + "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie/node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true + }, + "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/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typed-assert": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/typed-assert/-/typed-assert-1.0.9.tgz", + "integrity": "sha512-KNNZtayBCtmnNmbo5mG47p1XsCyrx6iVqomjcZnec/1Y5GGARaxPs6r49RnSPeUP3YjNYiU9sQHAtY4BBvnZwg==", + "dev": true + }, + "node_modules/typescript": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", + "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/ua-parser-js": { + "version": "0.7.35", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.35.tgz", + "integrity": "sha512-veRf7dawaj9xaWEu9HoTVn5Pggtc/qj+kqTOFvNiN1l0YdxwC1kvel57UCjThjGa3BHBihE8/UJAHI+uQHmd/g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + } + ], + "engines": { + "node": "*" + } + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dev": true, + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", + "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "dev": true, + "dependencies": { + "unique-slug": "^2.0.0" + } + }, + "node_modules/unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4" + } + }, + "node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", + "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/url-join": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-5.0.0.tgz", + "integrity": "sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/validate-npm-package-name": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-4.0.0.tgz", + "integrity": "sha512-mzR0L8ZDktZjpX4OB46KT+56MAhl4EIazWP/+G/HPGuvfdaqg4YsCdtOm6U9+LOFyYDoh4dpnpxZRB9MQQns5Q==", + "dev": true, + "dependencies": { + "builtins": "^5.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/void-elements": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", + "integrity": "sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "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==", + "dev": true, + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "dependencies": { + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/webpack": { + "version": "5.88.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.2.tgz", + "integrity": "sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ==", + "dev": true, + "peer": true, + "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", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.9.0", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.15.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.9", + "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", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-middleware": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz", + "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==", + "dev": true, + "dependencies": { + "colorette": "^2.0.10", + "memfs": "^3.4.3", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/webpack-dev-middleware/node_modules/schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-dev-server": { + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.11.0.tgz", + "integrity": "sha512-L5S4Q2zT57SK7tazgzjMiSMBdsw+rGYIX27MgPgx7LDhWO0lViPrHKoLS7jo5In06PWYAhlYu3PbyoC6yAThbw==", + "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.1", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.0.11", + "chokidar": "^3.5.3", + "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", + "http-proxy-middleware": "^2.0.3", + "ipaddr.js": "^2.0.1", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "rimraf": "^3.0.2", + "schema-utils": "^4.0.0", + "selfsigned": "^2.0.1", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^5.3.1", + "ws": "^8.4.2" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.37.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server/node_modules/schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-merge": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", + "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", + "dev": true, + "dependencies": { + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack-subresource-integrity": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-5.1.0.tgz", + "integrity": "sha512-sacXoX+xd8r4WKsy9MvH/q/vBtEHr86cpImXwyg74pFIpERKt6FmB8cXpeuh0ZLgclOlHI4Wcll7+R5L02xk9Q==", + "dev": true, + "dependencies": { + "typed-assert": "^1.0.8" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "html-webpack-plugin": ">= 5.0.0-beta.1 < 6", + "webpack": "^5.12.0" + }, + "peerDependenciesMeta": { + "html-webpack-plugin": { + "optional": true + } + } + }, + "node_modules/webpack/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, + "peer": 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/webpack/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, + "peer": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/webpack/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, + "peer": true + }, + "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, + "peer": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dev": true, + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dev": true, + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/wildcard": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", + "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/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/wrap-ansi-cjs/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/wrap-ansi/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/wrap-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "engines": { + "node": ">=18" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yaml": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz", + "integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, + "node_modules/yargs": { + "version": "17.5.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", + "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/zod": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz", + "integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zone.js": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.12.0.tgz", + "integrity": "sha512-XtC+I5dXU14HrzidAKBNMqneIVUykLEAA1x+v4KVrd6AUPWlwYORF8KgsVqvgdHiKZ4BkxxjvYi/ksEixTPR0Q==", + "dependencies": { + "tslib": "^2.3.0" + } + } + }, + "dependencies": { + "@adobe/css-tools": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.3.2.tgz", + "integrity": "sha512-DA5a1C0gD/pLOvhv33YMrbf2FK3oUzwNl9oOJqE4XVjuEtt6XIakRcsd7eLiOSPkp1kTRQGICTA8cKra/vFbjw==", + "dev": true + }, + "@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true + }, + "@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "dev": true, + "requires": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@angular-devkit/architect": { + "version": "0.1402.13", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1402.13.tgz", + "integrity": "sha512-n0ISBuvkZHoOpAzuAZql1TU9VLHUE9e/a9g4VNOPHewjMzpN02VqeGKvJfOCKtzkCs6gVssIlILm2/SXxkIFxQ==", + "dev": true, + "requires": { + "@angular-devkit/core": "14.2.13", + "rxjs": "6.6.7" + }, + "dependencies": { + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "@angular-devkit/build-angular": { + "version": "14.2.13", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-14.2.13.tgz", + "integrity": "sha512-FJZKQ3xYFvEJ807sxVy4bCVyGU2NMl3UUPNfLIdIdzwwDEP9tx/cc+c4VtVPEZZfU8jVenu8XOvL6L0vpjt3yg==", + "dev": true, + "requires": { + "@ampproject/remapping": "2.2.0", + "@angular-devkit/architect": "0.1402.13", + "@angular-devkit/build-webpack": "0.1402.13", + "@angular-devkit/core": "14.2.13", + "@babel/core": "7.18.10", + "@babel/generator": "7.18.12", + "@babel/helper-annotate-as-pure": "7.18.6", + "@babel/plugin-proposal-async-generator-functions": "7.18.10", + "@babel/plugin-transform-async-to-generator": "7.18.6", + "@babel/plugin-transform-runtime": "7.18.10", + "@babel/preset-env": "7.18.10", + "@babel/runtime": "7.18.9", + "@babel/template": "7.18.10", + "@discoveryjs/json-ext": "0.5.7", + "@ngtools/webpack": "14.2.13", + "ansi-colors": "4.1.3", + "babel-loader": "8.2.5", + "babel-plugin-istanbul": "6.1.1", + "browserslist": "^4.9.1", + "cacache": "16.1.2", + "copy-webpack-plugin": "11.0.0", + "critters": "0.0.16", + "css-loader": "6.7.1", + "esbuild": "0.15.5", + "esbuild-wasm": "0.15.5", + "glob": "8.0.3", + "https-proxy-agent": "5.0.1", + "inquirer": "8.2.4", + "jsonc-parser": "3.1.0", + "karma-source-map-support": "1.4.0", + "less": "4.1.3", + "less-loader": "11.0.0", + "license-webpack-plugin": "4.0.2", + "loader-utils": "3.2.1", + "mini-css-extract-plugin": "2.6.1", + "minimatch": "5.1.0", + "open": "8.4.0", + "ora": "5.4.1", + "parse5-html-rewriting-stream": "6.0.1", + "piscina": "3.2.0", + "postcss": "8.4.31", + "postcss-import": "15.0.0", + "postcss-loader": "7.0.1", + "postcss-preset-env": "7.8.0", + "regenerator-runtime": "0.13.9", + "resolve-url-loader": "5.0.0", + "rxjs": "6.6.7", + "sass": "1.54.4", + "sass-loader": "13.0.2", + "semver": "7.5.3", + "source-map-loader": "4.0.0", + "source-map-support": "0.5.21", + "stylus": "0.59.0", + "stylus-loader": "7.0.0", + "terser": "5.14.2", + "text-table": "0.2.0", + "tree-kill": "1.2.2", + "tslib": "2.4.0", + "webpack": "5.76.1", + "webpack-dev-middleware": "5.3.3", + "webpack-dev-server": "4.11.0", + "webpack-merge": "5.8.0", + "webpack-subresource-integrity": "5.1.0" + }, + "dependencies": { + "@types/estree": { + "version": "0.0.51", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", + "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", + "dev": true + }, + "@webassemblyjs/ast": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", + "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "dev": true, + "requires": { + "@webassemblyjs/helper-numbers": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", + "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", + "dev": true + }, + "@webassemblyjs/helper-api-error": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", + "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", + "dev": true + }, + "@webassemblyjs/helper-buffer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", + "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", + "dev": true + }, + "@webassemblyjs/helper-numbers": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", + "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "dev": true, + "requires": { + "@webassemblyjs/floating-point-hex-parser": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", + "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", + "dev": true + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", + "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", + "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "dev": true, + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", + "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "dev": true, + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", + "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", + "dev": true + }, + "@webassemblyjs/wasm-edit": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", + "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/helper-wasm-section": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-opt": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "@webassemblyjs/wast-printer": "1.11.1" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", + "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", + "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", + "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", + "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "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, + "requires": {} + }, + "cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "dev": true, + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + } + }, + "es-module-lexer": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", + "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "postcss-import": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.0.0.tgz", + "integrity": "sha512-Y20shPQ07RitgBGv2zvkEAu9bqvrD77C9axhj/aA1BQj4czape2MdClCExvB27EwYEJdGgKZBpKanb0t1rK2Kg==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + } + }, + "postcss-loader": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.0.1.tgz", + "integrity": "sha512-VRviFEyYlLjctSM93gAZtcJJ/iSkPZ79zWbN/1fSH+NisBByEiVLqpdVDrPLVSi8DX0oJo12kL/GppTBdKVXiQ==", + "dev": true, + "requires": { + "cosmiconfig": "^7.0.0", + "klona": "^2.0.5", + "semver": "^7.3.7" + } + }, + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "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, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "dev": true + }, + "webpack": { + "version": "5.76.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.1.tgz", + "integrity": "sha512-4+YIK4Abzv8172/SGqObnUjaIHjLEuUasz9EwQj/9xmPPkYJy2Mh03Q/lJfSD3YLzbxy5FeTq5Uw0323Oh6SJQ==", + "dev": true, + "requires": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^0.0.51", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.10.0", + "es-module-lexer": "^0.9.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "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.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" + } + }, + "yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true + } + } + }, + "@angular-devkit/build-webpack": { + "version": "0.1402.13", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1402.13.tgz", + "integrity": "sha512-K27aJmuw86ZOdiu5PoGeGDJ2v7g2ZCK0bGwc8jzkjTLRfvd4FRKIIZumGv3hbQ3vQRLikiU6WMDRTFyCZky/EA==", + "dev": true, + "requires": { + "@angular-devkit/architect": "0.1402.13", + "rxjs": "6.6.7" + }, + "dependencies": { + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "@angular-devkit/core": { + "version": "14.2.13", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-14.2.13.tgz", + "integrity": "sha512-aIefeZcbjghQg/V6U9CTLtyB5fXDJ63KwYqVYkWP+i0XriS5A9puFgq2u/OVsWxAfYvqpDqp5AdQ0g0bi3CAsA==", + "dev": true, + "requires": { + "ajv": "8.11.0", + "ajv-formats": "2.1.1", + "jsonc-parser": "3.1.0", + "rxjs": "6.6.7", + "source-map": "0.7.4" + }, + "dependencies": { + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "@angular-devkit/schematics": { + "version": "14.2.13", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-14.2.13.tgz", + "integrity": "sha512-2zczyeNzeBcrT2HOysv52X9SH3tZoHfWJvVf6H0SIa74rfDKEl7hFpKNXnh3x8sIMLj5mZn05n5RCqGxCczcIg==", + "dev": true, + "requires": { + "@angular-devkit/core": "14.2.13", + "jsonc-parser": "3.1.0", + "magic-string": "0.26.2", + "ora": "5.4.1", + "rxjs": "6.6.7" + }, + "dependencies": { + "rxjs": { + "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "@angular/animations": { + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-14.3.0.tgz", + "integrity": "sha512-QoBcIKy1ZiU+4qJsAh5Ls20BupWiXiZzKb0s6L9/dntPt5Msr4Ao289XR2P6O1L+kTsCprH9Kt41zyGQ/bkRqg==", + "requires": { + "tslib": "^2.3.0" + } + }, + "@angular/cdk": { + "version": "14.2.7", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-14.2.7.tgz", + "integrity": "sha512-/tEsYaUbDSnfEmKVvAMramIptmhI67O+9STjOV0i+74XR2NospeK0fkbywIANu1n3w6AHGMotvRWJrjmbCElFg==", + "requires": { + "parse5": "^5.0.0", + "tslib": "^2.3.0" + } + }, + "@angular/cli": { + "version": "14.2.13", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-14.2.13.tgz", + "integrity": "sha512-I5EepRem2CCyS3GDzQxZ2ZrqQwVqoGoLY+ZQhsK1QGWUnUyFOjbv3OlUGxRUYwcedu19V1EBAKjmQ96HzMIcVQ==", + "dev": true, + "requires": { + "@angular-devkit/architect": "0.1402.13", + "@angular-devkit/core": "14.2.13", + "@angular-devkit/schematics": "14.2.13", + "@schematics/angular": "14.2.13", + "@yarnpkg/lockfile": "1.1.0", + "ansi-colors": "4.1.3", + "debug": "4.3.4", + "ini": "3.0.0", + "inquirer": "8.2.4", + "jsonc-parser": "3.1.0", + "npm-package-arg": "9.1.0", + "npm-pick-manifest": "7.0.1", + "open": "8.4.0", + "ora": "5.4.1", + "pacote": "13.6.2", + "resolve": "1.22.1", + "semver": "7.5.3", + "symbol-observable": "4.0.0", + "uuid": "8.3.2", + "yargs": "17.5.1" + } + }, + "@angular/common": { + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-14.3.0.tgz", + "integrity": "sha512-pV9oyG3JhGWeQ+TFB0Qub6a1VZWMNZ6/7zEopvYivdqa5yDLLDSBRWb6P80RuONXyGnM1pa7l5nYopX+r/23GQ==", + "requires": { + "tslib": "^2.3.0" + } + }, + "@angular/compiler": { + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-14.3.0.tgz", + "integrity": "sha512-E15Rh0t3vA+bctbKnBCaDmLvc3ix+ZBt6yFZmhZalReQ+KpOlvOJv+L9oiFEgg+rYVl2QdvN7US1fvT0PqswLw==", + "requires": { + "tslib": "^2.3.0" + } + }, + "@angular/compiler-cli": { + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-14.3.0.tgz", + "integrity": "sha512-eoKpKdQ2X6axMgzcPUMZVYl3bIlTMzMeTo5V29No4BzgiUB+QoOTYGNJZkGRyqTNpwD9uSBJvmT2vG9+eC4ghQ==", + "dev": true, + "requires": { + "@babel/core": "^7.17.2", + "chokidar": "^3.0.0", + "convert-source-map": "^1.5.1", + "dependency-graph": "^0.11.0", + "magic-string": "^0.26.0", + "reflect-metadata": "^0.1.2", + "semver": "^7.0.0", + "sourcemap-codec": "^1.4.8", + "tslib": "^2.3.0", + "yargs": "^17.2.1" + } + }, + "@angular/core": { + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-14.3.0.tgz", + "integrity": "sha512-wYiwItc0Uyn4FWZ/OAx/Ubp2/WrD3EgUJ476y1XI7yATGPF8n9Ld5iCXT08HOvc4eBcYlDfh90kTXR6/MfhzdQ==", + "requires": { + "tslib": "^2.3.0" + } + }, + "@angular/flex-layout": { + "version": "14.0.0-beta.41", + "resolved": "https://registry.npmjs.org/@angular/flex-layout/-/flex-layout-14.0.0-beta.41.tgz", + "integrity": "sha512-x1YcxqkdFlcbVXEy9ebCgW/F+7n/MXkEkwEcVEIPf5v5qn7HZsjQxgIj35Lf0amvMyF7h35prpoxO1uX5+ntFg==", + "requires": { + "tslib": "^2.3.0" + } + }, + "@angular/forms": { + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-14.3.0.tgz", + "integrity": "sha512-fBZZC2UFMom2AZPjGQzROPXFWO6kvCsPDKctjJwClVC8PuMrkm+RRyiYRdBbt2qxWHEqOZM2OCQo73xUyZOYHw==", + "requires": { + "tslib": "^2.3.0" + } + }, + "@angular/material": { + "version": "14.2.7", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-14.2.7.tgz", + "integrity": "sha512-WXHh8pEStpgkXZJmYOg2cI8BSHkV82ET4XTJCNPdveumaCn1UYnaNzsXD13kw5z+zmy8CufhFEzdXTrv/yt7KQ==", + "requires": { + "tslib": "^2.3.0" + } + }, + "@angular/platform-browser": { + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-14.3.0.tgz", + "integrity": "sha512-w9Y3740UmTz44T0Egvc+4QV9sEbO61L+aRHbpkLTJdlEGzHByZvxJmJyBYmdqeyTPwc/Zpy7c02frlpfAlyB7A==", + "requires": { + "tslib": "^2.3.0" + } + }, + "@angular/platform-browser-dynamic": { + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-14.3.0.tgz", + "integrity": "sha512-rneZiMrIiYRhrkQvdL40E2ErKRn4Zdo6EtjBM9pAmWeyoM8oMnOZb9gz5vhrkNWg06kVMVg0yKqluP5How7j3A==", + "requires": { + "tslib": "^2.3.0" + } + }, + "@angular/router": { + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-14.3.0.tgz", + "integrity": "sha512-uip0V7w7k7xyxxpTPbr7EuMnYLj3FzJrwkLVJSEw3TMMGHt5VU5t4BBa9veGZOta2C205XFrTAHnp8mD+XYY1w==", + "requires": { + "tslib": "^2.3.0" + } + }, + "@assemblyscript/loader": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.10.1.tgz", + "integrity": "sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg==", + "dev": true + }, + "@babel/code-frame": { + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "dev": true, + "requires": { + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" + } + }, + "@babel/compat-data": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.9.tgz", + "integrity": "sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==", + "dev": true + }, + "@babel/core": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.10.tgz", + "integrity": "sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw==", + "dev": true, + "requires": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.18.10", + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-module-transforms": "^7.18.9", + "@babel/helpers": "^7.18.9", + "@babel/parser": "^7.18.10", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.18.10", + "@babel/types": "^7.18.10", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.1", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true + } + } + }, + "@babel/generator": { + "version": "7.18.12", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.12.tgz", + "integrity": "sha512-dfQ8ebCN98SvyL7IxNMCUtZQSq5R7kxgN+r8qYTGDmmSion1hX2C0zq2yo1bsCDhXixokv1SAWTZUMYbO/V5zg==", + "dev": true, + "requires": { + "@babel/types": "^7.18.10", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + }, + "dependencies": { + "@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + } + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", + "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.5.tgz", + "integrity": "sha512-m1EP3lVOPptR+2DwD125gziZNcmoNSHGmJROKoy87loWUQyJaVXDgpmruWqDARZSmtYQ+Dl25okU8+qhVzuykw==", + "dev": true, + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.9.tgz", + "integrity": "sha512-7qYrNM6HjpnPHJbopxmb8hSPoZ0gsX8IvUS32JGVoy+pU9e5N0nLr1VjJoR6kA4d9dmGLxNYOjeB8sUDal2WMw==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.22.9", + "@babel/helper-validator-option": "^7.22.5", + "browserslist": "^4.21.9", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true + } + } + }, + "@babel/helper-create-class-features-plugin": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.9.tgz", + "integrity": "sha512-Pwyi89uO4YrGKxL/eNJ8lfEH55DnRloGPOseaA8NFNL6jAUnn+KccaISiFazCj5IolPPDjGSdzQzXVzODVRqUQ==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-member-expression-to-functions": "^7.22.5", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "semver": "^6.3.1" + }, + "dependencies": { + "@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==", + "dev": true, + "requires": { + "@babel/types": "^7.22.5" + } + }, + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true + } + } + }, + "@babel/helper-create-regexp-features-plugin": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.9.tgz", + "integrity": "sha512-+svjVa/tFwsNSG4NEy1h85+HQ5imbT92Q5/bgtS7P0GTQlP8WuFdqsiABmQouhiFGyV66oGxZFpeYHza1rNsKw==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "regexpu-core": "^5.3.1", + "semver": "^6.3.1" + }, + "dependencies": { + "@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==", + "dev": true, + "requires": { + "@babel/types": "^7.22.5" + } + }, + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true + } + } + }, + "@babel/helper-define-polyfill-provider": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz", + "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==", + "dev": true, + "requires": { + "@babel/helper-compilation-targets": "^7.17.7", + "@babel/helper-plugin-utils": "^7.16.7", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2", + "semver": "^6.1.2" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true + } + } + }, + "@babel/helper-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "dev": true + }, + "@babel/helper-function-name": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "dev": true, + "requires": { + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + }, + "dependencies": { + "@babel/template": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" + } + } + } + }, + "@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "dev": true, + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.5.tgz", + "integrity": "sha512-aBiH1NKMG0H2cGZqspNvsaBe6wNGjbJjuLy29aU+eDZjSbbN53BaxlpB02xm9v34pLTZ1nIQPFYn2qMZoa5BQQ==", + "dev": true, + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-module-imports": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz", + "integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==", + "dev": true, + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-module-transforms": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz", + "integrity": "sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.5" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", + "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", + "dev": true, + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", + "dev": true + }, + "@babel/helper-remap-async-to-generator": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.9.tgz", + "integrity": "sha512-8WWC4oR4Px+tr+Fp0X3RHDVfINGpF3ad1HIbrc8A77epiR6eMMc6jsgozkzT2uDiOOdoS9cLIQ+XD2XvI2WSmQ==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-wrap-function": "^7.22.9" + }, + "dependencies": { + "@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==", + "dev": true, + "requires": { + "@babel/types": "^7.22.5" + } + } + } + }, + "@babel/helper-replace-supers": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.9.tgz", + "integrity": "sha512-LJIKvvpgPOPUThdYqcX6IXRuIcTkcAub0IaDRGCZH0p5GPUp7PhRU9QVgFcDDd51BaPkk77ZjqFwh6DZTAEmGg==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-member-expression-to-functions": "^7.22.5", + "@babel/helper-optimise-call-expression": "^7.22.5" + } + }, + "@babel/helper-simple-access": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "dev": true, + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", + "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", + "dev": true, + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@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==", + "dev": true, + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-string-parser": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "dev": true + }, + "@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz", + "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==", + "dev": true + }, + "@babel/helper-wrap-function": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.9.tgz", + "integrity": "sha512-sZ+QzfauuUEfxSEjKFmi3qDSHgLsTPK/pEpoD/qonZKOtTPTLbf59oabPQ4rKekt9lFcj/hTZaOhWwFYrgjk+Q==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.22.5", + "@babel/template": "^7.22.5", + "@babel/types": "^7.22.5" + }, + "dependencies": { + "@babel/template": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", + "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/types": "^7.22.5" + } + } + } + }, + "@babel/helpers": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.6.tgz", + "integrity": "sha512-YjDs6y/fVOYFV8hAf1rxd1QvR9wJe1pDBZ2AREKq/SDayfPzgk0PBnVuTCE5X1acEpMMNOVUqoe+OwiZGJ+OaA==", + "dev": true, + "requires": { + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.6", + "@babel/types": "^7.22.5" + }, + "dependencies": { + "@babel/template": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", + "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/types": "^7.22.5" + } + } + } + }, + "@babel/highlight": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.3.tgz", + "integrity": "sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw==", + "dev": true + }, + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.5.tgz", + "integrity": "sha512-NP1M5Rf+u2Gw9qfSO4ihjcTGW5zXTi36ITLd4/EoAcEhIZ0yjMqmftDNl3QC19CX7olhrjpyU454g/2W7X0jvQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.5.tgz", + "integrity": "sha512-31Bb65aZaUwqCbWMnZPduIZxCBngHFlzyN6Dq6KAJjtx+lx6ohKHubc61OomYi7XwVD4Ol0XCVz4h+pYFR048g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/plugin-transform-optional-chaining": "^7.22.5" + } + }, + "@babel/plugin-proposal-async-generator-functions": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.18.10.tgz", + "integrity": "sha512-1mFuY2TOsR1hxbjCo4QL+qlIjV07p4H4EUYw2J/WCqsvFV6V9X9z9YhXbWndc/4fw+hYGlDT7egYxliMp5O6Ew==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-remap-async-to-generator": "^7.18.9", + "@babel/plugin-syntax-async-generators": "^7.8.4" + } + }, + "@babel/plugin-proposal-class-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", + "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-proposal-class-static-block": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.21.0.tgz", + "integrity": "sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.21.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + } + }, + "@babel/plugin-proposal-dynamic-import": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", + "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + } + }, + "@babel/plugin-proposal-export-namespace-from": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", + "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + } + }, + "@babel/plugin-proposal-json-strings": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", + "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-json-strings": "^7.8.3" + } + }, + "@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz", + "integrity": "sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + } + }, + "@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", + "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + } + }, + "@babel/plugin-proposal-numeric-separator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", + "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + } + }, + "@babel/plugin-proposal-object-rest-spread": { + "version": "7.20.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz", + "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.20.5", + "@babel/helper-compilation-targets": "^7.20.7", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.20.7" + } + }, + "@babel/plugin-proposal-optional-catch-binding": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", + "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + } + }, + "@babel/plugin-proposal-optional-chaining": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz", + "integrity": "sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + } + }, + "@babel/plugin-proposal-private-methods": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", + "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.11", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz", + "integrity": "sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.21.0", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + } + }, + "@babel/plugin-proposal-unicode-property-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", + "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-syntax-import-assertions": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.22.5.tgz", + "integrity": "sha512-rdV97N7KqsRzeNGoWUOK6yUsWarLjE5Su/Snk9IYPU9CwkWHs4t+rTGOvffTR8XGkJMTAdLfO0xVnXm8wugIJg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-transform-arrow-functions": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.22.5.tgz", + "integrity": "sha512-26lTNXoVRdAnsaDXPpvCNUq+OVWEVC6bx7Vvz9rC53F2bagUWW4u4ii2+h8Fejfh7RYqPxn+libeFBBck9muEw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-async-to-generator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.18.6.tgz", + "integrity": "sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-remap-async-to-generator": "^7.18.6" + } + }, + "@babel/plugin-transform-block-scoped-functions": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.22.5.tgz", + "integrity": "sha512-tdXZ2UdknEKQWKJP1KMNmuF5Lx3MymtMN/pvA+p/VEkhK8jVcQ1fzSy8KM9qRYhAf2/lV33hoMPKI/xaI9sADA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-block-scoping": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.22.5.tgz", + "integrity": "sha512-EcACl1i5fSQ6bt+YGuU/XGCeZKStLmyVGytWkpyhCLeQVA0eu6Wtiw92V+I1T/hnezUv7j74dA/Ro69gWcU+hg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-classes": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.6.tgz", + "integrity": "sha512-58EgM6nuPNG6Py4Z3zSuu0xWu2VfodiMi72Jt5Kj2FECmaYk1RrTXA45z6KBFsu9tRgwQDwIiY4FXTt+YsSFAQ==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "globals": "^11.1.0" + }, + "dependencies": { + "@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==", + "dev": true, + "requires": { + "@babel/types": "^7.22.5" + } + } + } + }, + "@babel/plugin-transform-computed-properties": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.22.5.tgz", + "integrity": "sha512-4GHWBgRf0krxPX+AaPtgBAlTgTeZmqDynokHOX7aqqAB4tHs3U2Y02zH6ETFdLZGcg9UQSD1WCmkVrE9ErHeOg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/template": "^7.22.5" + }, + "dependencies": { + "@babel/template": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", + "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/types": "^7.22.5" + } + } + } + }, + "@babel/plugin-transform-destructuring": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.22.5.tgz", + "integrity": "sha512-GfqcFuGW8vnEqTUBM7UtPd5A4q797LTvvwKxXTgRsFjoqaJiEg9deBG6kWeQYkVEL569NpnmpC0Pkr/8BLKGnQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-dotall-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.22.5.tgz", + "integrity": "sha512-5/Yk9QxCQCl+sOIB1WelKnVRxTJDSAIxtJLL2/pqL14ZVlbH0fUQUZa/T5/UnQtBNgghR7mfB8ERBKyKPCi7Vw==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-duplicate-keys": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.22.5.tgz", + "integrity": "sha512-dEnYD+9BBgld5VBXHnF/DbYGp3fqGMsyxKbtD1mDyIA7AkTSpKXFhCVuj/oQVOoALfBs77DudA0BE4d5mcpmqw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-exponentiation-operator": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.22.5.tgz", + "integrity": "sha512-vIpJFNM/FjZ4rh1myqIya9jXwrwwgFRHPjT3DkUA9ZLHuzox8jiXkOLvwm1H+PQIP3CqfC++WPKeuDi0Sjdj1g==", + "dev": true, + "requires": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-for-of": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.22.5.tgz", + "integrity": "sha512-3kxQjX1dU9uudwSshyLeEipvrLjBCVthCgeTp6CzE/9JYrlAIaeekVxRpCWsDDfYTfRZRoCeZatCQvwo+wvK8A==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-function-name": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.22.5.tgz", + "integrity": "sha512-UIzQNMS0p0HHiQm3oelztj+ECwFnj+ZRV4KnguvlsD2of1whUeM6o7wGNj6oLwcDoAXQ8gEqfgC24D+VdIcevg==", + "dev": true, + "requires": { + "@babel/helper-compilation-targets": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-literals": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.22.5.tgz", + "integrity": "sha512-fTLj4D79M+mepcw3dgFBTIDYpbcB9Sm0bpm4ppXPaO+U+PKFFyV9MGRvS0gvGw62sd10kT5lRMKXAADb9pWy8g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-member-expression-literals": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.22.5.tgz", + "integrity": "sha512-RZEdkNtzzYCFl9SE9ATaUMTj2hqMb4StarOJLrZRbqqU4HSBE7UlBw9WBWQiDzrJZJdUWiMTVDI6Gv/8DPvfew==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-modules-amd": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.22.5.tgz", + "integrity": "sha512-R+PTfLTcYEmb1+kK7FNkhQ1gP4KgjpSO6HfH9+f8/yfp2Nt3ggBjiVpRwmwTlfqZLafYKJACy36yDXlEmI9HjQ==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-modules-commonjs": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.5.tgz", + "integrity": "sha512-B4pzOXj+ONRmuaQTg05b3y/4DuFz3WcCNAXPLb2Q0GT0TrGKGxNKV4jwsXts+StaM0LQczZbOpj8o1DLPDJIiA==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-simple-access": "^7.22.5" + } + }, + "@babel/plugin-transform-modules-systemjs": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.5.tgz", + "integrity": "sha512-emtEpoaTMsOs6Tzz+nbmcePl6AKVtS1yC4YNAeMun9U8YCsgadPNxnOPQ8GhHFB2qdx+LZu9LgoC0Lthuu05DQ==", + "dev": true, + "requires": { + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.5" + } + }, + "@babel/plugin-transform-modules-umd": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.22.5.tgz", + "integrity": "sha512-+S6kzefN/E1vkSsKx8kmQuqeQsvCKCd1fraCM7zXm4SFoggI099Tr4G8U81+5gtMdUeMQ4ipdQffbKLX0/7dBQ==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz", + "integrity": "sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-new-target": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.22.5.tgz", + "integrity": "sha512-AsF7K0Fx/cNKVyk3a+DW0JLo+Ua598/NxMRvxDnkpCIGFh43+h/v2xyhRUYf6oD8gE4QtL83C7zZVghMjHd+iw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-object-super": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.22.5.tgz", + "integrity": "sha512-klXqyaT9trSjIUrcsYIfETAzmOEZL3cBYqOYLJxBHfMFFggmXOv+NYSX/Jbs9mzMVESw/WycLFPRx8ba/b2Ipw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-replace-supers": "^7.22.5" + } + }, + "@babel/plugin-transform-optional-chaining": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.6.tgz", + "integrity": "sha512-Vd5HiWml0mDVtcLHIoEU5sw6HOUW/Zk0acLs/SAeuLzkGNOPc9DB4nkUajemhCmTIz3eiaKREZn2hQQqF79YTg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + } + }, + "@babel/plugin-transform-parameters": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.5.tgz", + "integrity": "sha512-AVkFUBurORBREOmHRKo06FjHYgjrabpdqRSwq6+C7R5iTCZOsM4QbcB27St0a4U6fffyAOqh3s/qEfybAhfivg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-property-literals": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.22.5.tgz", + "integrity": "sha512-TiOArgddK3mK/x1Qwf5hay2pxI6wCZnvQqrFSqbtg1GLl2JcNMitVH/YnqjP+M31pLUeTfzY1HAXFDnUBV30rQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-regenerator": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.22.5.tgz", + "integrity": "sha512-rR7KePOE7gfEtNTh9Qw+iO3Q/e4DEsoQ+hdvM6QUDH7JRJ5qxq5AA52ZzBWbI5i9lfNuvySgOGP8ZN7LAmaiPw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "regenerator-transform": "^0.15.1" + } + }, + "@babel/plugin-transform-reserved-words": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.22.5.tgz", + "integrity": "sha512-DTtGKFRQUDm8svigJzZHzb/2xatPc6TzNvAIJ5GqOKDsGFYgAskjRulbR/vGsPKq3OPqtexnz327qYpP57RFyA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-runtime": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.18.10.tgz", + "integrity": "sha512-q5mMeYAdfEbpBAgzl7tBre/la3LeCxmDO1+wMXRdPWbcoMjR3GiXlCLk7JBZVVye0bqTGNMbt0yYVXX1B1jEWQ==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.9", + "babel-plugin-polyfill-corejs2": "^0.3.2", + "babel-plugin-polyfill-corejs3": "^0.5.3", + "babel-plugin-polyfill-regenerator": "^0.4.0", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true + } + } + }, + "@babel/plugin-transform-shorthand-properties": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.22.5.tgz", + "integrity": "sha512-vM4fq9IXHscXVKzDv5itkO1X52SmdFBFcMIBZ2FRn2nqVYqw6dBexUgMvAjHW+KXpPPViD/Yo3GrDEBaRC0QYA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-spread": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.22.5.tgz", + "integrity": "sha512-5ZzDQIGyvN4w8+dMmpohL6MBo+l2G7tfC/O2Dg7/hjpgeWvUx8FzfeOKxGog9IimPa4YekaQ9PlDqTLOljkcxg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" + } + }, + "@babel/plugin-transform-sticky-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.22.5.tgz", + "integrity": "sha512-zf7LuNpHG0iEeiyCNwX4j3gDg1jgt1k3ZdXBKbZSoA3BbGQGvMiSvfbZRR3Dr3aeJe3ooWFZxOOG3IRStYp2Bw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-template-literals": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.22.5.tgz", + "integrity": "sha512-5ciOehRNf+EyUeewo8NkbQiUs4d6ZxiHo6BcBcnFlgiJfu16q0bQUw9Jvo0b0gBKFG1SMhDSjeKXSYuJLeFSMA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-typeof-symbol": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.22.5.tgz", + "integrity": "sha512-bYkI5lMzL4kPii4HHEEChkD0rkc+nvnlR6+o/qdqR6zrm0Sv/nodmyLhlq2DO0YKLUNd2VePmPRjJXSBh9OIdA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-unicode-escapes": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.22.5.tgz", + "integrity": "sha512-biEmVg1IYB/raUO5wT1tgfacCef15Fbzhkx493D3urBI++6hpJ+RFG4SrWMn0NEZLfvilqKf3QDrRVZHo08FYg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/plugin-transform-unicode-regex": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.22.5.tgz", + "integrity": "sha512-028laaOKptN5vHJf9/Arr/HiJekMd41hOEZYvNsrsXqJ7YPYuX2bQxh31fkZzGmq3YqHRJzYFFAVYvKfMPKqyg==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + } + }, + "@babel/preset-env": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.18.10.tgz", + "integrity": "sha512-wVxs1yjFdW3Z/XkNfXKoblxoHgbtUF7/l3PvvP4m02Qz9TZ6uZGxRVYjSQeR87oQmHco9zWitW5J82DJ7sCjvA==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.18.8", + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-async-generator-functions": "^7.18.10", + "@babel/plugin-proposal-class-properties": "^7.18.6", + "@babel/plugin-proposal-class-static-block": "^7.18.6", + "@babel/plugin-proposal-dynamic-import": "^7.18.6", + "@babel/plugin-proposal-export-namespace-from": "^7.18.9", + "@babel/plugin-proposal-json-strings": "^7.18.6", + "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", + "@babel/plugin-proposal-numeric-separator": "^7.18.6", + "@babel/plugin-proposal-object-rest-spread": "^7.18.9", + "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", + "@babel/plugin-proposal-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-private-methods": "^7.18.6", + "@babel/plugin-proposal-private-property-in-object": "^7.18.6", + "@babel/plugin-proposal-unicode-property-regex": "^7.18.6", + "@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.18.6", + "@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-transform-arrow-functions": "^7.18.6", + "@babel/plugin-transform-async-to-generator": "^7.18.6", + "@babel/plugin-transform-block-scoped-functions": "^7.18.6", + "@babel/plugin-transform-block-scoping": "^7.18.9", + "@babel/plugin-transform-classes": "^7.18.9", + "@babel/plugin-transform-computed-properties": "^7.18.9", + "@babel/plugin-transform-destructuring": "^7.18.9", + "@babel/plugin-transform-dotall-regex": "^7.18.6", + "@babel/plugin-transform-duplicate-keys": "^7.18.9", + "@babel/plugin-transform-exponentiation-operator": "^7.18.6", + "@babel/plugin-transform-for-of": "^7.18.8", + "@babel/plugin-transform-function-name": "^7.18.9", + "@babel/plugin-transform-literals": "^7.18.9", + "@babel/plugin-transform-member-expression-literals": "^7.18.6", + "@babel/plugin-transform-modules-amd": "^7.18.6", + "@babel/plugin-transform-modules-commonjs": "^7.18.6", + "@babel/plugin-transform-modules-systemjs": "^7.18.9", + "@babel/plugin-transform-modules-umd": "^7.18.6", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.18.6", + "@babel/plugin-transform-new-target": "^7.18.6", + "@babel/plugin-transform-object-super": "^7.18.6", + "@babel/plugin-transform-parameters": "^7.18.8", + "@babel/plugin-transform-property-literals": "^7.18.6", + "@babel/plugin-transform-regenerator": "^7.18.6", + "@babel/plugin-transform-reserved-words": "^7.18.6", + "@babel/plugin-transform-shorthand-properties": "^7.18.6", + "@babel/plugin-transform-spread": "^7.18.9", + "@babel/plugin-transform-sticky-regex": "^7.18.6", + "@babel/plugin-transform-template-literals": "^7.18.9", + "@babel/plugin-transform-typeof-symbol": "^7.18.9", + "@babel/plugin-transform-unicode-escapes": "^7.18.10", + "@babel/plugin-transform-unicode-regex": "^7.18.6", + "@babel/preset-modules": "^0.1.5", + "@babel/types": "^7.18.10", + "babel-plugin-polyfill-corejs2": "^0.3.2", + "babel-plugin-polyfill-corejs3": "^0.5.3", + "babel-plugin-polyfill-regenerator": "^0.4.0", + "core-js-compat": "^3.22.1", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true + } + } + }, + "@babel/preset-modules": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6.tgz", + "integrity": "sha512-ID2yj6K/4lKfhuU3+EX4UvNbIt7eACFbHmNUjzA+ep+B5971CknnA/9DEWKbRokfbbtblxxxXFJJrH47UEAMVg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + } + }, + "@babel/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==", + "dev": true + }, + "@babel/runtime": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz", + "integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@babel/template": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", + "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.18.10", + "@babel/types": "^7.18.10" + } + }, + "@babel/traverse": { + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "dependencies": { + "@babel/generator": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.3.tgz", + "integrity": "sha512-keeZWAV4LU3tW0qRi19HRpabC/ilM0HRBBzf9/k8FFiG4KVpiv0FIy4hHfLfFQZNhziCTPTmd59zoyv6DNISzg==", + "dev": true, + "requires": { + "@babel/types": "^7.23.3", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + } + }, + "@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + } + } + }, + "@babel/types": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.3.tgz", + "integrity": "sha512-OZnvoH2l8PK5eUvEcUyCt/sXgr/h+UWpVuBbOljwcrAgUl6lpchoQ++PHGyQy1AtYnVA6CEq3y5xeEI10brpXw==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + } + }, + "@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true + }, + "@csstools/postcss-cascade-layers": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-1.1.1.tgz", + "integrity": "sha512-+KdYrpKC5TgomQr2DlZF4lDEpHcoxnj5IGddYYfBWJAKfj1JtuHUIqMa+E1pJJ+z3kvDViWMqyqPlG4Ja7amQA==", + "dev": true, + "requires": { + "@csstools/selector-specificity": "^2.0.2", + "postcss-selector-parser": "^6.0.10" + } + }, + "@csstools/postcss-color-function": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-1.1.1.tgz", + "integrity": "sha512-Bc0f62WmHdtRDjf5f3e2STwRAl89N2CLb+9iAwzrv4L2hncrbDwnQD9PCq0gtAt7pOI2leIV08HIBUd4jxD8cw==", + "dev": true, + "requires": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-font-format-keywords": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-font-format-keywords/-/postcss-font-format-keywords-1.0.1.tgz", + "integrity": "sha512-ZgrlzuUAjXIOc2JueK0X5sZDjCtgimVp/O5CEqTcs5ShWBa6smhWYbS0x5cVc/+rycTDbjjzoP0KTDnUneZGOg==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-hwb-function": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-1.0.2.tgz", + "integrity": "sha512-YHdEru4o3Rsbjmu6vHy4UKOXZD+Rn2zmkAmLRfPet6+Jz4Ojw8cbWxe1n42VaXQhD3CQUXXTooIy8OkVbUcL+w==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-ic-unit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-1.0.1.tgz", + "integrity": "sha512-Ot1rcwRAaRHNKC9tAqoqNZhjdYBzKk1POgWfhN4uCOE47ebGcLRqXjKkApVDpjifL6u2/55ekkpnFcp+s/OZUw==", + "dev": true, + "requires": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-is-pseudo-class": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-2.0.7.tgz", + "integrity": "sha512-7JPeVVZHd+jxYdULl87lvjgvWldYu+Bc62s9vD/ED6/QTGjy0jy0US/f6BG53sVMTBJ1lzKZFpYmofBN9eaRiA==", + "dev": true, + "requires": { + "@csstools/selector-specificity": "^2.0.0", + "postcss-selector-parser": "^6.0.10" + } + }, + "@csstools/postcss-nested-calc": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-nested-calc/-/postcss-nested-calc-1.0.0.tgz", + "integrity": "sha512-JCsQsw1wjYwv1bJmgjKSoZNvf7R6+wuHDAbi5f/7MbFhl2d/+v+TvBTU4BJH3G1X1H87dHl0mh6TfYogbT/dJQ==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-normalize-display-values": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-1.0.1.tgz", + "integrity": "sha512-jcOanIbv55OFKQ3sYeFD/T0Ti7AMXc9nM1hZWu8m/2722gOTxFg7xYu4RDLJLeZmPUVQlGzo4jhzvTUq3x4ZUw==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-oklab-function": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-1.1.1.tgz", + "integrity": "sha512-nJpJgsdA3dA9y5pgyb/UfEzE7W5Ka7u0CX0/HIMVBNWzWemdcTH3XwANECU6anWv/ao4vVNLTMxhiPNZsTK6iA==", + "dev": true, + "requires": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-progressive-custom-properties": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-1.3.0.tgz", + "integrity": "sha512-ASA9W1aIy5ygskZYuWams4BzafD12ULvSypmaLJT2jvQ8G0M3I8PRQhC0h7mG0Z3LI05+agZjqSR9+K9yaQQjA==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-stepped-value-functions": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-1.0.1.tgz", + "integrity": "sha512-dz0LNoo3ijpTOQqEJLY8nyaapl6umbmDcgj4AD0lgVQ572b2eqA1iGZYTTWhrcrHztWDDRAX2DGYyw2VBjvCvQ==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-text-decoration-shorthand": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-1.0.0.tgz", + "integrity": "sha512-c1XwKJ2eMIWrzQenN0XbcfzckOLLJiczqy+YvfGmzoVXd7pT9FfObiSEfzs84bpE/VqfpEuAZ9tCRbZkZxxbdw==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-trigonometric-functions": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-1.0.2.tgz", + "integrity": "sha512-woKaLO///4bb+zZC2s80l+7cm07M7268MsyG3M0ActXXEFi6SuhvriQYcb58iiKGbjwwIU7n45iRLEHypB47Og==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-unset-value": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-1.0.2.tgz", + "integrity": "sha512-c8J4roPBILnelAsdLr4XOAR/GsTm0GJi4XpcfvoWk3U6KiTCqiFYc63KhRMQQX35jYMp4Ao8Ij9+IZRgMfJp1g==", + "dev": true, + "requires": {} + }, + "@csstools/selector-specificity": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.2.0.tgz", + "integrity": "sha512-+OJ9konv95ClSTOJCmMZqpd5+YGsB2S+x6w3E1oaM8UuR5j8nTNHYSz8c9BEPGDOCMQYIEEGlVPj/VY64iTbGw==", + "dev": true, + "requires": {} + }, + "@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "dev": true + }, + "@esbuild/linux-loong64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.5.tgz", + "integrity": "sha512-UHkDFCfSGTuXq08oQltXxSZmH1TXyWsL+4QhZDWvvLl6mEJQqk3u7/wq1LjhrrAXYIllaTtRSzUXl4Olkf2J8A==", + "dev": true, + "optional": true + }, + "@gar/promisify": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", + "dev": true + }, + "@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "requires": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, + "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 + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + } + }, + "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, + "requires": { + "ansi-regex": "^6.0.1" + } + }, + "wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "requires": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + } + } + } + }, + "@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + } + }, + "@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true + }, + "@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true + }, + "@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true + }, + "@jridgewell/source-map": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", + "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", + "dev": true, + "requires": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "dependencies": { + "@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + } + } + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", + "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + }, + "dependencies": { + "@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + } + } + }, + "@kurkle/color": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz", + "integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==", + "peer": true + }, + "@leichtgewicht/ip-codec": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", + "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==", + "dev": true + }, + "@ng-apimock/core": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/@ng-apimock/core/-/core-3.11.0.tgz", + "integrity": "sha512-Gy4b0hdwuExADS00TMQ1gcj4TciDQveIVCRD+B/EInRYfO+IODvZH58Iw/dV+mjcAXIOEsrql2MES7/tWyqScA==", + "requires": { + "body-parser": "1.20.2", + "chokidar": "3.5.3", + "debug": "4.3.4", + "fs-extra": "11.1.1", + "glob": "8.1.0", + "inversify": "6.0.1", + "node-fetch": "2.6.9", + "reflect-metadata": "0.1.13", + "uuid": "9.0.0" + }, + "dependencies": { + "glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + } + }, + "uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==" + } + } + }, + "@ngtools/webpack": { + "version": "14.2.13", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-14.2.13.tgz", + "integrity": "sha512-RQx/rGX7K/+R55x1R6Ax1JzyeHi8cW11dEXpzHWipyuSpusQLUN53F02eMB4VTakXsL3mFNWWy4bX3/LSq8/9w==", + "dev": true, + "requires": {} + }, + "@ngx-translate/core": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@ngx-translate/core/-/core-14.0.0.tgz", + "integrity": "sha512-UevdwNCXMRCdJv//0kC8h2eSfmi02r29xeE8E9gJ1Al4D4jEJ7eiLPdjslTMc21oJNGguqqWeEVjf64SFtvw2w==", + "requires": { + "tslib": "^2.3.0" + } + }, + "@ngx-translate/http-loader": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@ngx-translate/http-loader/-/http-loader-7.0.0.tgz", + "integrity": "sha512-j+NpXXlcGVdyUNyY/qsJrqqeAdJdizCd+GKh3usXExSqy1aE9866jlAIL+xrfDU4w+LiMoma5pgE4emvFebZmA==", + "requires": { + "tslib": "^2.3.0" + } + }, + "@ngxs/store": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@ngxs/store/-/store-3.8.1.tgz", + "integrity": "sha512-nbapLdMx+mtnb57BUWXbD6qYfVICv6Rp2NdoGx1++qDbc44ALC49KbF7rSjyPltlExxharAzoNpzO3JuueCP+A==", + "requires": { + "tslib": "^2.2.0" + } + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@npmcli/fs": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz", + "integrity": "sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==", + "dev": true, + "requires": { + "@gar/promisify": "^1.1.3", + "semver": "^7.3.5" + } + }, + "@npmcli/git": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-3.0.2.tgz", + "integrity": "sha512-CAcd08y3DWBJqJDpfuVL0uijlq5oaXaOJEKHKc4wqrjd00gkvTZB+nFuLn+doOOKddaQS9JfqtNoFCO2LCvA3w==", + "dev": true, + "requires": { + "@npmcli/promise-spawn": "^3.0.0", + "lru-cache": "^7.4.4", + "mkdirp": "^1.0.4", + "npm-pick-manifest": "^7.0.0", + "proc-log": "^2.0.0", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^2.0.2" + }, + "dependencies": { + "lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "@npmcli/installed-package-contents": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-1.0.7.tgz", + "integrity": "sha512-9rufe0wnJusCQoLpV9ZPKIVP55itrM5BxOXs10DmdbRfgWtHy1LDyskbwRnBghuB0PrF7pNPOqREVtpz4HqzKw==", + "dev": true, + "requires": { + "npm-bundled": "^1.1.1", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "@npmcli/move-file": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-2.0.1.tgz", + "integrity": "sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==", + "dev": true, + "requires": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + } + }, + "@npmcli/node-gyp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-2.0.0.tgz", + "integrity": "sha512-doNI35wIe3bBaEgrlPfdJPaCpUR89pJWep4Hq3aRdh6gKazIVWfs0jHttvSSoq47ZXgC7h73kDsUl8AoIQUB+A==", + "dev": true + }, + "@npmcli/promise-spawn": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-3.0.0.tgz", + "integrity": "sha512-s9SgS+p3a9Eohe68cSI3fi+hpcZUmXq5P7w0kMlAsWVtR7XbK3ptkZqKT2cK1zLDObJ3sR+8P59sJE0w/KTL1g==", + "dev": true, + "requires": { + "infer-owner": "^1.0.4" + } + }, + "@npmcli/run-script": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-4.2.1.tgz", + "integrity": "sha512-7dqywvVudPSrRCW5nTHpHgeWnbBtz8cFkOuKrecm6ih+oO9ciydhWt6OF7HlqupRRmB8Q/gECVdB9LMfToJbRg==", + "dev": true, + "requires": { + "@npmcli/node-gyp": "^2.0.0", + "@npmcli/promise-spawn": "^3.0.0", + "node-gyp": "^9.0.0", + "read-package-json-fast": "^2.0.3", + "which": "^2.0.2" + }, + "dependencies": { + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true + }, + "@schematics/angular": { + "version": "14.2.13", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-14.2.13.tgz", + "integrity": "sha512-MLxTpTU3E8QACQ/5c0sENMR2gRiMXpGaKeD5IHY+3wyU2fUSJVB0QPU/l1WhoyZbX8N9ospBgf5UEG7taVF9rg==", + "dev": true, + "requires": { + "@angular-devkit/core": "14.2.13", + "@angular-devkit/schematics": "14.2.13", + "jsonc-parser": "3.1.0" + } + }, + "@socket.io/component-emitter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==", + "dev": true + }, + "@sovity.de/edc-client": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/@sovity.de/edc-client/-/edc-client-10.4.0.tgz", + "integrity": "sha512-CBC7bHdIWD1K5plc+tgdJOb5YT82YyyaZ1xMKCy5Ox77KgQEOKG5W/95VzzZkap91xV5zNO5j7I5KJXj18jz8g==", + "requires": { + "zod": "^3.22.4" + } + }, + "@tailwindcss/typography": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.10.tgz", + "integrity": "sha512-Pe8BuPJQJd3FfRnm6H0ulKIGoMEQS+Vq01R6M5aCrFB/ccR/shT+0kXLjouGC1gFLm9hopTFN+DMP0pfwRWzPw==", + "dev": true, + "requires": { + "lodash.castarray": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.merge": "^4.6.2", + "postcss-selector-parser": "6.0.10" + }, + "dependencies": { + "postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + } + } + } + }, + "@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true + }, + "@trivago/prettier-plugin-sort-imports": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@trivago/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-4.3.0.tgz", + "integrity": "sha512-r3n0onD3BTOVUNPhR4lhVK4/pABGpbA7bW3eumZnYdKaHkf1qEC+Mag6DPbGNuuh0eG8AaYj+YqmVHSiGslaTQ==", + "dev": true, + "requires": { + "@babel/generator": "7.17.7", + "@babel/parser": "^7.20.5", + "@babel/traverse": "7.23.2", + "@babel/types": "7.17.0", + "javascript-natural-sort": "0.7.1", + "lodash": "^4.17.21" + }, + "dependencies": { + "@babel/generator": { + "version": "7.17.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.7.tgz", + "integrity": "sha512-oLcVCTeIFadUoArDTwpluncplrYBmTCCZZgXCbgNGvOBBiSDDK3eWO4b/+eOTli5tKv1lg+a5/NAXg+nTcei1w==", + "dev": true, + "requires": { + "@babel/types": "^7.17.0", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "@babel/types": { + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.17.0.tgz", + "integrity": "sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.16.7", + "to-fast-properties": "^2.0.0" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "dev": true + } + } + }, + "@types/body-parser": { + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "dev": true, + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "@types/bonjour": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz", + "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/connect-history-api-fallback": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.3.tgz", + "integrity": "sha512-6mfQ6iNvhSKCZJoY6sIG3m0pKkdUcweVNOLuBBKvoWGzl2yRxOJcYOTRyLKt3nxXvBLJWa6QkW//tgbIwJehmA==", + "dev": true, + "requires": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, + "@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", + "dev": true + }, + "@types/cors": { + "version": "2.8.17", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", + "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/dompurify": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.0.5.tgz", + "integrity": "sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==", + "requires": { + "@types/trusted-types": "*" + } + }, + "@types/eslint": { + "version": "8.44.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.1.tgz", + "integrity": "sha512-XpNDc4Z5Tb4x+SW1MriMVeIsMoONHCkWFMkR/aPJbzEsxqHy+4Glu/BqTdPrApfDeMaXbtNh6bseNgl5KaWrSg==", + "dev": true, + "requires": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "@types/eslint-scope": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", + "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", + "dev": true, + "requires": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "@types/estree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz", + "integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==", + "dev": true + }, + "@types/express": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "dev": true, + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.17.41", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.41.tgz", + "integrity": "sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", + "dev": true + }, + "@types/http-proxy": { + "version": "1.17.14", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.14.tgz", + "integrity": "sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/jasmine": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-4.3.6.tgz", + "integrity": "sha512-3N0FpQTeiWjm+Oo1WUYWguUS7E6JLceiGTriFrG8k5PU7zRLJCzLcWURU3wjMbZGS//a2/LgjsnO3QxIlwxt9g==", + "dev": true + }, + "@types/json-schema": { + "version": "7.0.12", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", + "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", + "dev": true + }, + "@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true + }, + "@types/node": { + "version": "18.17.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.17.1.tgz", + "integrity": "sha512-xlR1jahfizdplZYRU59JlUx9uzF1ARa8jbhM11ccpCJya8kvos5jwdm2ZAgxSCwOl0fq21svP18EVwPBXMQudw==", + "dev": true + }, + "@types/node-forge": { + "version": "1.3.9", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.9.tgz", + "integrity": "sha512-meK88cx/sTalPSLSoCzkiUB4VPIFHmxtXm5FaaqRDqBX2i/Sy8bJ4odsan0b20RBjPh06dAQ+OTTdnyQyhJZyQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, + "@types/qs": { + "version": "6.9.10", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.10.tgz", + "integrity": "sha512-3Gnx08Ns1sEoCrWssEgTSJs/rsT2vhGP+Ja9cnnk9k4ALxinORlQneLXFeFKOTJMOeZUFD1s7w+w2AphTpvzZw==", + "dev": true + }, + "@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true + }, + "@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 + }, + "@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "dev": true, + "requires": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "@types/serve-index": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz", + "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==", + "dev": true, + "requires": { + "@types/express": "*" + } + }, + "@types/serve-static": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz", + "integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==", + "dev": true, + "requires": { + "@types/http-errors": "*", + "@types/mime": "*", + "@types/node": "*" + } + }, + "@types/sockjs": { + "version": "0.3.36", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", + "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==" + }, + "@types/ws": { + "version": "8.5.9", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.9.tgz", + "integrity": "sha512-jbdrY0a8lxfdTp/+r7Z4CkycbOFN8WX+IOchLJr3juT/xzbJ8URyTVSJ/hvNdadTgM1mnedb47n+Y31GsFnQlg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@webassemblyjs/ast": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", + "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", + "dev": true, + "peer": true, + "requires": { + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", + "dev": true, + "peer": true + }, + "@webassemblyjs/helper-api-error": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", + "dev": true, + "peer": true + }, + "@webassemblyjs/helper-buffer": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", + "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==", + "dev": true, + "peer": true + }, + "@webassemblyjs/helper-numbers": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", + "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", + "dev": true, + "peer": true, + "requires": { + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", + "dev": true, + "peer": true + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", + "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", + "dev": true, + "peer": true, + "requires": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", + "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", + "dev": true, + "peer": true, + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", + "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", + "dev": true, + "peer": true, + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", + "dev": true, + "peer": true + }, + "@webassemblyjs/wasm-edit": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", + "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", + "dev": true, + "peer": true, + "requires": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-opt": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6", + "@webassemblyjs/wast-printer": "1.11.6" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", + "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", + "dev": true, + "peer": true, + "requires": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", + "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", + "dev": true, + "peer": true, + "requires": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", + "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", + "dev": true, + "peer": true, + "requires": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", + "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", + "dev": true, + "peer": true, + "requires": { + "@webassemblyjs/ast": "1.11.6", + "@xtuc/long": "4.2.2" + } + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", + "dev": true + }, + "abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "dev": true + }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dev": true, + "requires": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + } + }, + "acorn": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", + "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", + "dev": true + }, + "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==", + "dev": true, + "requires": {} + }, + "adjust-sourcemap-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", + "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", + "dev": true, + "requires": { + "loader-utils": "^2.0.0", + "regex-parser": "^2.2.11" + }, + "dependencies": { + "loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + } + } + }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "requires": { + "debug": "4" + } + }, + "agentkeepalive": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.3.0.tgz", + "integrity": "sha512-7Epl1Blf4Sy37j4v9f9FjICCh4+KAQOyXgHEwlyBiAQLbhKdq/i2QQU3amQalS/wPhdPzDXPL5DMR5bkn+YeWg==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "depd": "^2.0.0", + "humanize-ms": "^1.2.1" + } + }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "requires": { + "ajv": "^8.0.0" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true + }, + "ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "requires": { + "type-fest": "^0.21.3" + } + }, + "ansi-html-community": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", + "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", + "dev": true + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true + }, + "anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "dev": true + }, + "are-we-there-yet": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", + "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", + "dev": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + } + }, + "arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "autoprefixer": { + "version": "10.4.14", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz", + "integrity": "sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==", + "dev": true, + "requires": { + "browserslist": "^4.21.5", + "caniuse-lite": "^1.0.30001464", + "fraction.js": "^4.2.0", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + } + }, + "babel-loader": { + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.5.tgz", + "integrity": "sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==", + "dev": true, + "requires": { + "find-cache-dir": "^3.3.1", + "loader-utils": "^2.0.0", + "make-dir": "^3.1.0", + "schema-utils": "^2.6.5" + }, + "dependencies": { + "loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + } + } + }, + "babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + } + }, + "babel-plugin-polyfill-corejs2": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", + "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.17.7", + "@babel/helper-define-polyfill-provider": "^0.3.3", + "semver": "^6.1.1" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true + } + } + }, + "babel-plugin-polyfill-corejs3": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.3.tgz", + "integrity": "sha512-zKsXDh0XjnrUEW0mxIHLfjBfnXSMr5Q/goMe/fxpQnLm07mcOZiIZHBNWCMx60HmdvjxfXcalac0tfFg0wqxyw==", + "dev": true, + "requires": { + "@babel/helper-define-polyfill-provider": "^0.3.2", + "core-js-compat": "^3.21.0" + } + }, + "babel-plugin-polyfill-regenerator": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz", + "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==", + "dev": true, + "requires": { + "@babel/helper-define-polyfill-provider": "^0.3.3" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true + }, + "base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "dev": true + }, + "batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", + "dev": true + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" + }, + "bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "body-parser": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", + "requires": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + } + } + }, + "bonjour-service": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.1.1.tgz", + "integrity": "sha512-Z/5lQRMOG9k7W+FkeGTNjh7htqn/2LMnfOvBZ8pynNZCM9MwkQkI3zeI4oz09uWdcgmgHugVvBqxGg4VQJ5PCg==", + "dev": true, + "requires": { + "array-flatten": "^2.1.2", + "dns-equal": "^1.0.0", + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" + } + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true + }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "requires": { + "balanced-match": "^1.0.0" + } + }, + "braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "requires": { + "fill-range": "^7.1.1" + } + }, + "browserslist": { + "version": "4.21.9", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.9.tgz", + "integrity": "sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001503", + "electron-to-chromium": "^1.4.431", + "node-releases": "^2.0.12", + "update-browserslist-db": "^1.0.11" + } + }, + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "builtins": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", + "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", + "dev": true, + "requires": { + "semver": "^7.0.0" + } + }, + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" + }, + "cacache": { + "version": "16.1.2", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-16.1.2.tgz", + "integrity": "sha512-Xx+xPlfCZIUHagysjjOAje9nRo8pRDczQCcXb4J2O0BLtH+xeVue6ba4y1kfJfQMAnM2mkcoMIAyOctlaRGWYA==", + "dev": true, + "requires": { + "@npmcli/fs": "^2.1.0", + "@npmcli/move-file": "^2.0.0", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "glob": "^8.0.1", + "infer-owner": "^1.0.4", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "mkdirp": "^1.0.4", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^9.0.0", + "tar": "^6.1.11", + "unique-filename": "^1.1.1" + }, + "dependencies": { + "lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true + } + } + }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true + }, + "caniuse-lite": { + "version": "1.0.30001517", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001517.tgz", + "integrity": "sha512-Vdhm5S11DaFVLlyiKu4hiUTkpZu+y1KA/rZZqVQfOD5YdDT/eQKlkt7NaE0WGOFgX32diqt9MiP9CAiFeRklaA==", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "chart.js": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.3.1.tgz", + "integrity": "sha512-QHuISG3hTJ0ftq0I0f5jqH9mNVO9bqG8P+zvMOVslgKajQVvFEX7QAhYNJ+QEmw+uYTwo8XpTimaB82oeTWjxw==", + "peer": true, + "requires": { + "@kurkle/color": "^0.3.0" + } + }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true + }, + "chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true + }, + "clean-deep": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/clean-deep/-/clean-deep-3.4.0.tgz", + "integrity": "sha512-Lo78NV5ItJL/jl+B5w0BycAisaieJGXK1qYi/9m4SjR8zbqmrUtO7Yhro40wEShGmmxs/aJLI/A+jNhdkXK8mw==", + "requires": { + "lodash.isempty": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.transform": "^4.6.0" + } + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-spinners": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.0.tgz", + "integrity": "sha512-4/aL9X3Wh0yiMQlE+eeRhWP6vclO3QRtw1JHKIT0FFUs5FjpFmESqtMvYZ0+lbzBw900b95mS0hohy+qn2VK/g==", + "dev": true + }, + "cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "dev": true + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true + }, + "clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true + }, + "colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "dev": true + }, + "compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "dev": true, + "requires": { + "mime-db": ">= 1.43.0 < 2" + } + }, + "compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "dev": true, + "requires": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "dependencies": { + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + } + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "connect": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", + "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "dev": true, + "requires": { + "debug": "2.6.9", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", + "utils-merge": "1.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + } + } + }, + "connect-history-api-fallback": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "dev": true + }, + "content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "requires": { + "safe-buffer": "5.2.1" + } + }, + "content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" + }, + "convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, + "cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "dev": true + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "dev": true + }, + "copy-anything": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz", + "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", + "dev": true, + "requires": { + "is-what": "^3.14.1" + } + }, + "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==", + "dev": true, + "requires": { + "fast-glob": "^3.2.11", + "glob-parent": "^6.0.1", + "globby": "^13.1.1", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0" + }, + "dependencies": { + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + }, + "schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + } + } + } + }, + "core-js-compat": { + "version": "3.31.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.31.1.tgz", + "integrity": "sha512-wIDWd2s5/5aJSdpOJHfSibxNODxoGoWOBHt8JSPB41NOE94M7kuTPZCYLOlTtuoXTsBPKobpJ6T+y0SSy5L9SA==", + "dev": true, + "requires": { + "browserslist": "^4.21.9" + } + }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dev": true, + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, + "cosmiconfig": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.2.0.tgz", + "integrity": "sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ==", + "dev": true, + "requires": { + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0" + }, + "dependencies": { + "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 + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + } + } + }, + "critters": { + "version": "0.0.16", + "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.16.tgz", + "integrity": "sha512-JwjgmO6i3y6RWtLYmXwO5jMd+maZt8Tnfu7VVISmEWyQqfLpB8soBswf8/2bu6SBXxtKA68Al3c+qIG1ApT68A==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "css-select": "^4.2.0", + "parse5": "^6.0.1", + "parse5-htmlparser2-tree-adapter": "^6.0.1", + "postcss": "^8.3.7", + "pretty-bytes": "^5.3.0" + }, + "dependencies": { + "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, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "dependencies": { + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "css-blank-pseudo": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-3.0.3.tgz", + "integrity": "sha512-VS90XWtsHGqoM0t4KpH053c4ehxZ2E6HtGI7x68YFV0pTo/QmkV/YFA+NnlvK8guxZVNWGQhVNJGC39Q8XF4OQ==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.9" + } + }, + "css-has-pseudo": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-3.0.4.tgz", + "integrity": "sha512-Vse0xpR1K9MNlp2j5w1pgWIJtm1a8qS0JwS9goFYcImjlHEmywP9VUF05aGBXzGpDJF86QXk4L0ypBmwPhGArw==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.9" + } + }, + "css-loader": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.1.tgz", + "integrity": "sha512-yB5CNFa14MbPJcomwNh3wLThtkZgcNyI2bNMRt8iE5Z8Vwl7f8vQXFAzn2HDOJvtDq2NTZBUGMSUNNyrv3/+cw==", + "dev": true, + "requires": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.7", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.3.5" + } + }, + "css-prefers-color-scheme": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz", + "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==", + "dev": true, + "requires": {} + }, + "css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dev": true, + "requires": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + } + }, + "css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true + }, + "cssdb": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.7.0.tgz", + "integrity": "sha512-1hN+I3r4VqSNQ+OmMXxYexnumbOONkSil0TWMebVXHtzYW4tRRPovUNHPHj2d4nrgOuYJ8Vs3XwvywsuwwXNNA==", + "dev": true + }, + "cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true + }, + "cssstyle": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-3.0.0.tgz", + "integrity": "sha512-N4u2ABATi3Qplzf0hWbVCdjenim8F3ojEXpBDF5hBpjzW182MjNGLqfmQ0SkSPeQ+V86ZXgeH8aXj6kayd4jgg==", + "requires": { + "rrweb-cssom": "^0.6.0" + } + }, + "custom-event": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", + "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==", + "dev": true + }, + "data-urls": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", + "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", + "requires": { + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0" + }, + "dependencies": { + "tr46": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", + "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", + "requires": { + "punycode": "^2.3.1" + } + }, + "webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==" + }, + "whatwg-url": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.0.0.tgz", + "integrity": "sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==", + "requires": { + "tr46": "^5.0.0", + "webidl-conversions": "^7.0.0" + } + } + } + }, + "date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "requires": { + "@babel/runtime": "^7.21.0" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.6.tgz", + "integrity": "sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ==", + "requires": { + "regenerator-runtime": "^0.13.11" + } + }, + "regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" + } + } + }, + "date-fns-tz": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/date-fns-tz/-/date-fns-tz-2.0.1.tgz", + "integrity": "sha512-fJCG3Pwx8HUoLhkepdsP7Z5RsucUi+ZBOxyM5d0ZZ6c4SdYustq0VMmOu6Wf7bli+yS/Jwp91TOCqn9jMcVrUA==", + "requires": {} + }, + "date-format": { + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz", + "integrity": "sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==", + "dev": true + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "decimal.js": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==" + }, + "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, + "requires": { + "execa": "^5.0.0" + } + }, + "defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "requires": { + "clone": "^1.0.2" + } + }, + "define-lazy-prop": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "dev": true + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "dev": true + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "dependency-graph": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz", + "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==", + "dev": true + }, + "destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" + }, + "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 + }, + "di": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", + "integrity": "sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==", + "dev": true + }, + "didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true + }, + "dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==", + "dev": true + }, + "dns-packet": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", + "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", + "dev": true, + "requires": { + "@leichtgewicht/ip-codec": "^2.0.1" + } + }, + "dom-serialize": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", + "integrity": "sha512-Yra4DbvoW7/Z6LBN560ZwXMjoNOSAN2wRsKFGc4iBeso+mpIA6qj1vfdf9HpMaKAqG6wXTy+1SYEzmNpKXOSsQ==", + "dev": true, + "requires": { + "custom-event": "~1.0.0", + "ent": "~2.2.0", + "extend": "^3.0.0", + "void-elements": "^2.0.0" + } + }, + "dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + } + }, + "domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true + }, + "domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, + "requires": { + "domelementtype": "^2.2.0" + } + }, + "dompurify": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.7.tgz", + "integrity": "sha512-BViYTZoqP3ak/ULKOc101y+CtHDUvBsVgSxIF1ku0HmK6BRf+C03MC+tArMvOPtVtZp83DDh5puywKDu4sbVjQ==" + }, + "domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "requires": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + } + }, + "dotenv": { + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", + "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==" + }, + "eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "electron-to-chromium": { + "version": "1.4.471", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.471.tgz", + "integrity": "sha512-GpmGRC1vTl60w/k6YpQ18pSiqnmr0j3un//5TV1idPi6aheNfkT1Ye71tMEabWyNDO6sBMgAR+95Eb0eUUr1tA==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true + }, + "encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "optional": true, + "requires": { + "iconv-lite": "^0.6.2" + }, + "dependencies": { + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + } + } + }, + "engine.io": { + "version": "6.5.5", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.5.tgz", + "integrity": "sha512-C5Pn8Wk+1vKBoHghJODM63yk8MvrO9EWZUfkAt5HAqIgPE4/8FF0PEGHXtEd40l223+cE5ABWuPzm38PHFXfMA==", + "dev": true, + "requires": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1" + } + }, + "engine.io-parser": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.2.tgz", + "integrity": "sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==", + "dev": true + }, + "enhanced-resolve": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", + "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + } + }, + "ent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", + "integrity": "sha512-GHrMyVZQWvTIdDtpiEXdHZnFQKzeO09apj8Cbl4pKWy4i0Oprcq17usfDt5aO63swf0JOeMWjWQE/LzgSRuWpA==", + "dev": true + }, + "entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true + }, + "env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true + }, + "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 + }, + "errno": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", + "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", + "dev": true, + "optional": true, + "requires": { + "prr": "~1.0.1" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-module-lexer": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.3.0.tgz", + "integrity": "sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA==", + "dev": true, + "peer": true + }, + "esbuild": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.5.tgz", + "integrity": "sha512-VSf6S1QVqvxfIsSKb3UKr3VhUCis7wgDbtF4Vd9z84UJr05/Sp2fRKmzC+CSPG/dNAPPJZ0BTBLTT1Fhd6N9Gg==", + "dev": true, + "optional": true, + "requires": { + "@esbuild/linux-loong64": "0.15.5", + "esbuild-android-64": "0.15.5", + "esbuild-android-arm64": "0.15.5", + "esbuild-darwin-64": "0.15.5", + "esbuild-darwin-arm64": "0.15.5", + "esbuild-freebsd-64": "0.15.5", + "esbuild-freebsd-arm64": "0.15.5", + "esbuild-linux-32": "0.15.5", + "esbuild-linux-64": "0.15.5", + "esbuild-linux-arm": "0.15.5", + "esbuild-linux-arm64": "0.15.5", + "esbuild-linux-mips64le": "0.15.5", + "esbuild-linux-ppc64le": "0.15.5", + "esbuild-linux-riscv64": "0.15.5", + "esbuild-linux-s390x": "0.15.5", + "esbuild-netbsd-64": "0.15.5", + "esbuild-openbsd-64": "0.15.5", + "esbuild-sunos-64": "0.15.5", + "esbuild-windows-32": "0.15.5", + "esbuild-windows-64": "0.15.5", + "esbuild-windows-arm64": "0.15.5" + } + }, + "esbuild-android-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.5.tgz", + "integrity": "sha512-dYPPkiGNskvZqmIK29OPxolyY3tp+c47+Fsc2WYSOVjEPWNCHNyqhtFqQadcXMJDQt8eN0NMDukbyQgFcHquXg==", + "dev": true, + "optional": true + }, + "esbuild-android-arm64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.5.tgz", + "integrity": "sha512-YyEkaQl08ze3cBzI/4Cm1S+rVh8HMOpCdq8B78JLbNFHhzi4NixVN93xDrHZLztlocEYqi45rHHCgA8kZFidFg==", + "dev": true, + "optional": true + }, + "esbuild-darwin-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.5.tgz", + "integrity": "sha512-Cr0iIqnWKx3ZTvDUAzG0H/u9dWjLE4c2gTtRLz4pqOBGjfjqdcZSfAObFzKTInLLSmD0ZV1I/mshhPoYSBMMCQ==", + "dev": true, + "optional": true + }, + "esbuild-darwin-arm64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.5.tgz", + "integrity": "sha512-WIfQkocGtFrz7vCu44ypY5YmiFXpsxvz2xqwe688jFfSVCnUsCn2qkEVDo7gT8EpsLOz1J/OmqjExePL1dr1Kg==", + "dev": true, + "optional": true + }, + "esbuild-freebsd-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.5.tgz", + "integrity": "sha512-M5/EfzV2RsMd/wqwR18CELcenZ8+fFxQAAEO7TJKDmP3knhWSbD72ILzrXFMMwshlPAS1ShCZ90jsxkm+8FlaA==", + "dev": true, + "optional": true + }, + "esbuild-freebsd-arm64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.5.tgz", + "integrity": "sha512-2JQQ5Qs9J0440F/n/aUBNvY6lTo4XP/4lt1TwDfHuo0DY3w5++anw+jTjfouLzbJmFFiwmX7SmUhMnysocx96w==", + "dev": true, + "optional": true + }, + "esbuild-linux-32": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.5.tgz", + "integrity": "sha512-gO9vNnIN0FTUGjvTFucIXtBSr1Woymmx/aHQtuU+2OllGU6YFLs99960UD4Dib1kFovVgs59MTXwpFdVoSMZoQ==", + "dev": true, + "optional": true + }, + "esbuild-linux-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.5.tgz", + "integrity": "sha512-ne0GFdNLsm4veXbTnYAWjbx3shpNKZJUd6XpNbKNUZaNllDZfYQt0/zRqOg0sc7O8GQ+PjSMv9IpIEULXVTVmg==", + "dev": true, + "optional": true + }, + "esbuild-linux-arm": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.5.tgz", + "integrity": "sha512-wvAoHEN+gJ/22gnvhZnS/+2H14HyAxM07m59RSLn3iXrQsdS518jnEWRBnJz3fR6BJa+VUTo0NxYjGaNt7RA7Q==", + "dev": true, + "optional": true + }, + "esbuild-linux-arm64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.5.tgz", + "integrity": "sha512-7EgFyP2zjO065XTfdCxiXVEk+f83RQ1JsryN1X/VSX2li9rnHAt2swRbpoz5Vlrl6qjHrCmq5b6yxD13z6RheA==", + "dev": true, + "optional": true + }, + "esbuild-linux-mips64le": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.5.tgz", + "integrity": "sha512-KdnSkHxWrJ6Y40ABu+ipTZeRhFtc8dowGyFsZY5prsmMSr1ZTG9zQawguN4/tunJ0wy3+kD54GaGwdcpwWAvZQ==", + "dev": true, + "optional": true + }, + "esbuild-linux-ppc64le": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.5.tgz", + "integrity": "sha512-QdRHGeZ2ykl5P0KRmfGBZIHmqcwIsUKWmmpZTOq573jRWwmpfRmS7xOhmDHBj9pxv+6qRMH8tLr2fe+ZKQvCYw==", + "dev": true, + "optional": true + }, + "esbuild-linux-riscv64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.5.tgz", + "integrity": "sha512-p+WE6RX+jNILsf+exR29DwgV6B73khEQV0qWUbzxaycxawZ8NE0wA6HnnTxbiw5f4Gx9sJDUBemh9v49lKOORA==", + "dev": true, + "optional": true + }, + "esbuild-linux-s390x": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.5.tgz", + "integrity": "sha512-J2ngOB4cNzmqLHh6TYMM/ips8aoZIuzxJnDdWutBw5482jGXiOzsPoEF4j2WJ2mGnm7FBCO4StGcwzOgic70JQ==", + "dev": true, + "optional": true + }, + "esbuild-netbsd-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.5.tgz", + "integrity": "sha512-MmKUYGDizYjFia0Rwt8oOgmiFH7zaYlsoQ3tIOfPxOqLssAsEgG0MUdRDm5lliqjiuoog8LyDu9srQk5YwWF3w==", + "dev": true, + "optional": true + }, + "esbuild-openbsd-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.5.tgz", + "integrity": "sha512-2mMFfkLk3oPWfopA9Plj4hyhqHNuGyp5KQyTT9Rc8hFd8wAn5ZrbJg+gNcLMo2yzf8Uiu0RT6G9B15YN9WQyMA==", + "dev": true, + "optional": true + }, + "esbuild-sunos-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.5.tgz", + "integrity": "sha512-2sIzhMUfLNoD+rdmV6AacilCHSxZIoGAU2oT7XmJ0lXcZWnCvCtObvO6D4puxX9YRE97GodciRGDLBaiC6x1SA==", + "dev": true, + "optional": true + }, + "esbuild-wasm": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.15.5.tgz", + "integrity": "sha512-lTJOEKekN/4JI/eOEq0wLcx53co2N6vaT/XjBz46D1tvIVoUEyM0o2K6txW6gEotf31szFD/J1PbxmnbkGlK9A==", + "dev": true + }, + "esbuild-windows-32": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.5.tgz", + "integrity": "sha512-e+duNED9UBop7Vnlap6XKedA/53lIi12xv2ebeNS4gFmu7aKyTrok7DPIZyU5w/ftHD4MUDs5PJUkQPP9xJRzg==", + "dev": true, + "optional": true + }, + "esbuild-windows-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.5.tgz", + "integrity": "sha512-v+PjvNtSASHOjPDMIai9Yi+aP+Vwox+3WVdg2JB8N9aivJ7lyhp4NVU+J0MV2OkWFPnVO8AE/7xH+72ibUUEnw==", + "dev": true, + "optional": true + }, + "esbuild-windows-arm64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.5.tgz", + "integrity": "sha512-Yz8w/D8CUPYstvVQujByu6mlf48lKmXkq6bkeSZZxTA626efQOJb26aDGLzmFWx6eg/FwrXgt6SZs9V8Pwy/aA==", + "dev": true, + "optional": true + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, + "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, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true + }, + "eventemitter-asyncresource": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/eventemitter-asyncresource/-/eventemitter-asyncresource-1.0.0.tgz", + "integrity": "sha512-39F7TBIV0G7gTelxwbEqnwhp90eqCPON1k0NwNfwhgKn4Co4ybUbj2pECcXT0B3ztRKZ7Pw1JujUUgmQJHcVAQ==", + "dev": true + }, + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true + }, + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "exponential-backoff": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz", + "integrity": "sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==", + "dev": true + }, + "express": { + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", + "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", + "dev": true, + "requires": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.2", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.6.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dev": true + }, + "cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-glob": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", + "dev": true, + "requires": { + "websocket-driver": ">=0.5.1" + } + }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true + } + } + }, + "find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true + }, + "follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "dev": true + }, + "foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "dependencies": { + "signal-exit": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.0.2.tgz", + "integrity": "sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q==", + "dev": true + } + } + }, + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true + }, + "fraction.js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", + "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", + "dev": true + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true + }, + "fs-extra": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", + "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==", + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "fs-monkey": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.5.tgz", + "integrity": "sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "gauge": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "dev": true, + "requires": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + } + }, + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-intrinsic": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz", + "integrity": "sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3" + } + }, + "get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true + }, + "glob": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", + "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "requires": { + "is-glob": "^4.0.1" + } + }, + "glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "globby": { + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", + "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", + "dev": true, + "requires": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.3.0", + "ignore": "^5.2.4", + "merge2": "^1.4.1", + "slash": "^4.0.0" + } + }, + "graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==" + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "dev": true + }, + "hdr-histogram-js": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hdr-histogram-js/-/hdr-histogram-js-2.0.3.tgz", + "integrity": "sha512-Hkn78wwzWHNCp2uarhzQ2SGFLU3JY8SBDDd3TAABK4fc30wm+MuPOrg5QVFVfkKOQd6Bfz3ukJEI+q9sXEkK1g==", + "dev": true, + "requires": { + "@assemblyscript/loader": "^0.10.1", + "base64-js": "^1.2.0", + "pako": "^1.0.3" + } + }, + "hdr-histogram-percentiles-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hdr-histogram-percentiles-obj/-/hdr-histogram-percentiles-obj-3.0.0.tgz", + "integrity": "sha512-7kIufnBqdsBGcSZLPJwqHT3yhk1QTsSlFsVD3kx5ixH/AlgBs9yM1q6DPhXZ8f8gtdqgh7N7/5btRLpQsS2gHw==", + "dev": true + }, + "hosted-git-info": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-5.2.1.tgz", + "integrity": "sha512-xIcQYMnhcx2Nr4JTjsFmwwnr9vldugPy9uVm0o87bjqqWMv9GaqsTeT+i99wTl0mk1uLxJtHxLb8kymqTENQsw==", + "dev": true, + "requires": { + "lru-cache": "^7.5.1" + }, + "dependencies": { + "lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true + } + } + }, + "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, + "requires": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + }, + "dependencies": { + "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, + "requires": { + "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" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "html-encoding-sniffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "requires": { + "whatwg-encoding": "^3.1.1" + } + }, + "html-entities": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.4.0.tgz", + "integrity": "sha512-igBTJcNNNhvZFRtm8uA6xMY6xYleeDwn3PeBCkDz7tHttv4F2hsDI2aPgNERWzvRcNYHNT3ymRaQzllmXj4YsQ==", + "dev": true + }, + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "http-cache-semantics": { + "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 + }, + "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 + }, + "http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "requires": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + } + }, + "http-parser-js": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", + "dev": true + }, + "http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "dev": true, + "requires": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "requires": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + } + }, + "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, + "requires": { + "@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" + } + }, + "https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true + }, + "humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "dev": true, + "requires": { + "ms": "^2.0.0" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "dev": true, + "requires": {} + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true + }, + "ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true + }, + "ignore-walk": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-5.0.1.tgz", + "integrity": "sha512-yemi4pMf51WKT7khInJqAvsIGzoqYXblnsz0ql8tM+yi1EKYTY1evX4NAbJrLL/Aanr2HyZeluqU+Oi7MGHokw==", + "dev": true, + "requires": { + "minimatch": "^5.0.1" + } + }, + "image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==", + "dev": true, + "optional": true + }, + "immutable": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.1.tgz", + "integrity": "sha512-lj9cnmB/kVS0QHsJnYKD1uo3o39nrbKxszjnqS9Fr6NB7bZzW45U6WSGBPKXDL/CvDKqDNPA4r3DoDQ8GTxo2A==", + "dev": true + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + } + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + }, + "infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ini": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-3.0.0.tgz", + "integrity": "sha512-TxYQaeNW/N8ymDvwAxPyRbhMBtnEwuvaTYpOQkFx1nSeusgezHniEc/l35Vo4iCq/mMiTJbpD7oYxN98hFlfmw==", + "dev": true + }, + "inquirer": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.4.tgz", + "integrity": "sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^7.0.0" + }, + "dependencies": { + "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, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "inversify": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/inversify/-/inversify-6.0.1.tgz", + "integrity": "sha512-B3ex30927698TJENHR++8FfEaJGqoWOgI6ZY5Ht/nLUsFCwHn6akbwtnUAPCgUepAnTpe2qHxhDNjoKLyz6rgQ==" + }, + "ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "dev": true, + "requires": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "dependencies": { + "sprintf-js": { + "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 + } + } + }, + "ipaddr.js": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.1.0.tgz", + "integrity": "sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ==", + "dev": true + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-core-module": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", + "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true + }, + "is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + }, + "is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "dev": true + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==" + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true + }, + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true + }, + "is-what": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", + "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==", + "dev": true + }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "requires": { + "is-docker": "^2.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "isbinaryfile": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", + "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true + }, + "isomorphic-dompurify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-dompurify/-/isomorphic-dompurify-2.0.0.tgz", + "integrity": "sha512-BJvrSQzg7jleSaySaWyhzGqH9/QxYc3sflm5fvjcXWAQcHQvQPQdCN0ORyqvMqnQDbwFuZXvqh2IcuVa3dG/DA==", + "requires": { + "@types/dompurify": "^3.0.3", + "dompurify": "^3.0.6", + "jsdom": "^23.0.0" + } + }, + "istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "requires": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true + } + } + }, + "istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "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 + }, + "make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "requires": { + "semver": "^7.5.3" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "istanbul-reports": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", + "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", + "dev": true, + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, + "jackspeak": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.2.2.tgz", + "integrity": "sha512-mgNtVv4vUuaKA97yxUHoA3+FkuhtxkjdXEWOyB/N76fjy0FjezEt34oy3epBtvCvS+7DyKwqCFWx/oJLV5+kCg==", + "dev": true, + "requires": { + "@isaacs/cliui": "^8.0.2", + "@pkgjs/parseargs": "^0.11.0" + } + }, + "jasmine-core": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-4.6.0.tgz", + "integrity": "sha512-O236+gd0ZXS8YAjFx8xKaJ94/erqUliEkJTDedyE7iHvv4ZVqi+q+8acJxu05/WJDKm512EUNn809In37nWlAQ==", + "dev": true + }, + "javascript-natural-sort": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz", + "integrity": "sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==", + "dev": true + }, + "jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "dependencies": { + "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 + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jiti": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.19.1.tgz", + "integrity": "sha512-oVhqoRDaBXf7sjkll95LHVS6Myyyb1zaunVwk4Z0+WPSW4gjS0pl01zYKHScTuyEhQsFxV5L4DR5r+YqSyqyyg==", + "dev": true + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "dev": true + }, + "jsdom": { + "version": "23.0.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-23.0.1.tgz", + "integrity": "sha512-2i27vgvlUsGEBO9+/kJQRbtqtm+191b5zAZrU/UezVmnC2dlDAFLgDYJvAEi94T4kjsRKkezEtLQTgsNEsW2lQ==", + "requires": { + "cssstyle": "^3.0.0", + "data-urls": "^5.0.0", + "decimal.js": "^10.4.3", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.2", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.7", + "parse5": "^7.1.2", + "rrweb-cssom": "^0.6.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.1.3", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0", + "ws": "^8.14.2", + "xml-name-validator": "^5.0.0" + }, + "dependencies": { + "agent-base": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "requires": { + "debug": "^4.3.4" + } + }, + "entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==" + }, + "http-proxy-agent": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz", + "integrity": "sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==", + "requires": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + } + }, + "https-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", + "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", + "requires": { + "agent-base": "^7.0.2", + "debug": "4" + } + }, + "parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "requires": { + "entities": "^4.4.0" + } + }, + "tr46": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", + "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", + "requires": { + "punycode": "^2.3.1" + } + }, + "webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==" + }, + "whatwg-url": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.0.0.tgz", + "integrity": "sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==", + "requires": { + "tr46": "^5.0.0", + "webidl-conversions": "^7.0.0" + } + } + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "json-stable-stringify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.2.tgz", + "integrity": "sha512-eunSSaEnxV12z+Z73y/j5N37/In40GK4GmsSy+tEHJMxknvqnA7/djeYtAgW0GsWHUfg+847WJjKaEylk2y09g==", + "requires": { + "jsonify": "^0.0.1" + } + }, + "json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true + }, + "jsonc-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.1.0.tgz", + "integrity": "sha512-DRf0QjnNeCUds3xTjKlQQ3DpJD51GvDjJfnxUVWg6PZTo2otSm+slzNAxU/35hF8/oJIKoG9slq30JYOsF2azg==", + "dev": true + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "jsonify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz", + "integrity": "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==" + }, + "jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "dev": true + }, + "karma": { + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/karma/-/karma-6.4.3.tgz", + "integrity": "sha512-LuucC/RE92tJ8mlCwqEoRWXP38UMAqpnq98vktmS9SznSoUPPUJQbc91dHcxcunROvfQjdORVA/YFviH+Xci9Q==", + "dev": true, + "requires": { + "@colors/colors": "1.5.0", + "body-parser": "^1.19.0", + "braces": "^3.0.2", + "chokidar": "^3.5.1", + "connect": "^3.7.0", + "di": "^0.0.1", + "dom-serialize": "^2.2.1", + "glob": "^7.1.7", + "graceful-fs": "^4.2.6", + "http-proxy": "^1.18.1", + "isbinaryfile": "^4.0.8", + "lodash": "^4.17.21", + "log4js": "^6.4.1", + "mime": "^2.5.2", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.5", + "qjobs": "^1.2.0", + "range-parser": "^1.2.1", + "rimraf": "^3.0.2", + "socket.io": "^4.7.2", + "source-map": "^0.6.1", + "tmp": "^0.2.1", + "ua-parser-js": "^0.7.30", + "yargs": "^16.1.1" + }, + "dependencies": { + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "requires": { + "minimist": "^1.2.6" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "requires": { + "rimraf": "^3.0.0" + } + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true + } + } + }, + "karma-chrome-launcher": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.2.0.tgz", + "integrity": "sha512-rE9RkUPI7I9mAxByQWkGJFXfFD6lE4gC5nPuZdobf/QdTEJI6EU4yIay/cfU/xV4ZxlM5JiTv7zWYgA64NpS5Q==", + "dev": true, + "requires": { + "which": "^1.2.1" + } + }, + "karma-coverage": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/karma-coverage/-/karma-coverage-2.2.1.tgz", + "integrity": "sha512-yj7hbequkQP2qOSb20GuNSIyE//PgJWHwC2IydLE6XRtsnaflv+/OSGNssPjobYUlhVVagy99TQpqUt3vAUG7A==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.1", + "istanbul-reports": "^3.0.5", + "minimatch": "^3.0.4" + }, + "dependencies": { + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "karma-jasmine": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-5.1.0.tgz", + "integrity": "sha512-i/zQLFrfEpRyQoJF9fsCdTMOF5c2dK7C7OmsuKg2D0YSsuZSfQDiLuaiktbuio6F2wiCsZSnSnieIQ0ant/uzQ==", + "dev": true, + "requires": { + "jasmine-core": "^4.1.0" + } + }, + "karma-jasmine-html-reporter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-2.1.0.tgz", + "integrity": "sha512-sPQE1+nlsn6Hwb5t+HHwyy0A1FNCVKuL1192b+XNauMYWThz2kweiBVW1DqloRpVvZIJkIoHVB7XRpK78n1xbQ==", + "dev": true, + "requires": {} + }, + "karma-source-map-support": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz", + "integrity": "sha512-RsBECncGO17KAoJCYXjv+ckIz+Ii9NCi+9enk+rq6XC81ezYkb4/RHE6CTXdA7IOJqoF3wcaLfVG0CPmE5ca6A==", + "dev": true, + "requires": { + "source-map-support": "^0.5.5" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + }, + "klona": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", + "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", + "dev": true + }, + "less": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/less/-/less-4.1.3.tgz", + "integrity": "sha512-w16Xk/Ta9Hhyei0Gpz9m7VS8F28nieJaL/VyShID7cYvP6IL5oHeL6p4TXSDJqZE/lNv0oJ2pGVjJsRkfwm5FA==", + "dev": true, + "requires": { + "copy-anything": "^2.0.1", + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "make-dir": "^2.1.0", + "mime": "^1.4.1", + "needle": "^3.1.0", + "parse-node-version": "^1.0.1", + "source-map": "~0.6.0", + "tslib": "^2.3.0" + }, + "dependencies": { + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "optional": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "optional": true + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "optional": true + }, + "semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "optional": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "optional": true + } + } + }, + "less-loader": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-11.0.0.tgz", + "integrity": "sha512-9+LOWWjuoectIEx3zrfN83NAGxSUB5pWEabbbidVQVgZhN+wN68pOvuyirVlH1IK4VT1f3TmlyvAnCXh8O5KEw==", + "dev": true, + "requires": { + "klona": "^2.0.4" + } + }, + "license-webpack-plugin": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-4.0.2.tgz", + "integrity": "sha512-771TFWFD70G1wLTC4oU2Cw4qvtmNrIw+wRvBtn+okgHl7slJVi7zfNcdmqDL72BojM30VNJ2UHylr1o77U37Jw==", + "dev": true, + "requires": { + "webpack-sources": "^3.0.0" + } + }, + "lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "dev": true + }, + "lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true + }, + "loader-utils": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz", + "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==", + "dev": true + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, + "lodash.castarray": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz", + "integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==", + "dev": true + }, + "lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true + }, + "lodash.isempty": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.isempty/-/lodash.isempty-4.4.0.tgz", + "integrity": "sha512-oKMuF3xEeqDltrGMfDxAPGIVMSSRv8tbRSODbrs4KGsRRLEhrW8N8Rd4DRgB2+621hY8A8XwwrTVhXWpxFvMzg==" + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "lodash.transform": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.transform/-/lodash.transform-4.6.0.tgz", + "integrity": "sha512-LO37ZnhmBVx0GvOU/caQuipEh4GN82TcWv3yHlebGDgOxbxiwwzW5Pcx2AcvpIv2WmvmSMoC492yQFNhy/l/UQ==" + }, + "log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "dependencies": { + "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, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "log4js": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.9.1.tgz", + "integrity": "sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==", + "dev": true, + "requires": { + "date-format": "^4.0.14", + "debug": "^4.3.4", + "flatted": "^3.2.7", + "rfdc": "^1.3.0", + "streamroller": "^3.1.5" + } + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "requires": { + "yallist": "^3.0.2" + } + }, + "magic-string": { + "version": "0.26.2", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.2.tgz", + "integrity": "sha512-NzzlXpclt5zAbmo6h6jNc8zl2gNRGHvmsZW4IvZhTC4W7k4OlLP+S5YLussa/r3ixNT66KOQfNORlXHSOy/X4A==", + "dev": true, + "requires": { + "sourcemap-codec": "^1.4.8" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true + } + } + }, + "make-fetch-happen": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-11.1.1.tgz", + "integrity": "sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w==", + "dev": true, + "requires": { + "agentkeepalive": "^4.2.1", + "cacache": "^17.0.0", + "http-cache-semantics": "^4.1.1", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^5.0.0", + "minipass-fetch": "^3.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^10.0.0" + }, + "dependencies": { + "@npmcli/fs": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.0.tgz", + "integrity": "sha512-7kZUAaLscfgbwBQRbvdMYaZOWyMEcPTH/tJjnyAWJ/dvvs9Ef+CERx/qJb9GExJpl1qipaDGn7KqHnFGGixd0w==", + "dev": true, + "requires": { + "semver": "^7.3.5" + } + }, + "cacache": { + "version": "17.1.3", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-17.1.3.tgz", + "integrity": "sha512-jAdjGxmPxZh0IipMdR7fK/4sDSrHMLUV0+GvVUsjwyGNKHsh79kW/otg+GkbXwl6Uzvy9wsvHOX4nUoWldeZMg==", + "dev": true, + "requires": { + "@npmcli/fs": "^3.1.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^7.7.1", + "minipass": "^5.0.0", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^4.0.0", + "ssri": "^10.0.0", + "tar": "^6.1.11", + "unique-filename": "^3.0.0" + } + }, + "fs-minipass": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.2.tgz", + "integrity": "sha512-2GAfyfoaCDRrM6jaOS3UsBts8yJ55VioXdWcOL7dK9zdAuKT71+WBA4ifnNYqVjYv+4SsPxjK0JT4yIIn4cA/g==", + "dev": true, + "requires": { + "minipass": "^5.0.0" + } + }, + "glob": { + "version": "10.3.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.3.tgz", + "integrity": "sha512-92vPiMb/iqpmEgsOoIDvTjc50wf9CCCvMzsi6W0JLPeUKE8TWP1a73PgqSrqy7iAZxaSD1YdzU7QZR5LF51MJw==", + "dev": true, + "requires": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.0.3", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + } + }, + "lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true + }, + "minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true + }, + "ssri": { + "version": "10.0.4", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.4.tgz", + "integrity": "sha512-12+IR2CB2C28MMAw0Ncqwj5QbTcs0nGIhgJzYWzDkb21vWmfNI83KS4f3Ci6GI98WreIfG7o9UXp3C0qbpA8nQ==", + "dev": true, + "requires": { + "minipass": "^5.0.0" + } + }, + "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, + "requires": { + "unique-slug": "^4.0.0" + } + }, + "unique-slug": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz", + "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4" + } + } + } + }, + "marked": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/marked/-/marked-11.1.1.tgz", + "integrity": "sha512-EgxRjgK9axsQuUa/oKMx5DEY8oXpKJfk61rT5iY3aRlgU6QJtUcxU5OAymdhCvWvhYcd9FKmO5eQoX8m9VGJXg==" + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" + }, + "memfs": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", + "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", + "dev": true, + "requires": { + "fs-monkey": "^1.0.4" + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", + "dev": true + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true + }, + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, + "mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "requires": { + "mime-db": "1.52.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "mini-css-extract-plugin": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.6.1.tgz", + "integrity": "sha512-wd+SD57/K6DiV7jIR34P+s3uckTRuQvx0tKPcvjFlrEylk6P4mQ2KSWk1hblj1Kxaqok7LogKOieygXqBczNlg==", + "dev": true, + "requires": { + "schema-utils": "^4.0.0" + }, + "dependencies": { + "schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + } + } + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true + }, + "minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + }, + "dependencies": { + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + } + }, + "minipass-fetch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.3.tgz", + "integrity": "sha512-n5ITsTkDqYkYJZjcRWzZt9qnZKCT7nKCosJhHoj7S7zD+BP4jVbWs+odsniw5TA3E0sLomhTKOKjF86wf11PuQ==", + "dev": true, + "requires": { + "encoding": "^0.1.13", + "minipass": "^5.0.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "dependencies": { + "minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true + } + } + }, + "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, + "requires": { + "minipass": "^3.0.0" + } + }, + "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==", + "dev": true, + "requires": { + "jsonparse": "^1.3.1", + "minipass": "^3.0.0" + } + }, + "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, + "requires": { + "minipass": "^3.0.0" + } + }, + "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, + "requires": { + "minipass": "^3.0.0" + } + }, + "minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "dependencies": { + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "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, + "requires": { + "dns-packet": "^5.2.2", + "thunky": "^1.0.2" + } + }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "requires": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "nanoid": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", + "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "dev": true + }, + "needle": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/needle/-/needle-3.2.0.tgz", + "integrity": "sha512-oUvzXnyLiVyVGoianLijF9O/RecZUf7TkBfimjGrLM4eQhXyeJwM6GeAWccwfQ9aa4gMCZKqhAOuLaMIcQxajQ==", + "dev": true, + "optional": true, + "requires": { + "debug": "^3.2.6", + "iconv-lite": "^0.6.3", + "sax": "^1.2.4" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "optional": true, + "requires": { + "ms": "^2.1.1" + } + }, + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + } + } + }, + "negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "dev": true + }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "ng2-charts": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ng2-charts/-/ng2-charts-4.1.1.tgz", + "integrity": "sha512-iHwXDbmX86lfeH8VRcsaW2tJATsuAZo4kvvC/Yk2l35zOHjevja1qBvO6BAibiDazi9r9aS6ZRJOqWPsz1pP2w==", + "requires": { + "lodash-es": "^4.17.15", + "tslib": "^2.3.0" + } + }, + "ngx-build-plus": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/ngx-build-plus/-/ngx-build-plus-14.0.0.tgz", + "integrity": "sha512-Rq23dNx9jL34+hbCrSa7LYrkopZpKN6WGAfPRaFlCeuJ/b5YIPVk8cePjj3/CDZr7LlNPedTrEtMC1dpG4AXww==", + "dev": true, + "requires": { + "@angular-devkit/build-angular": ">=14.0.0", + "@schematics/angular": ">=14.0.0", + "webpack-merge": "^5.0.0" + } + }, + "ngx-json-viewer": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ngx-json-viewer/-/ngx-json-viewer-3.2.1.tgz", + "integrity": "sha512-TTHtXsrBX+IXPqqAIsxklHPqSNmyGeQaziFZbCDJq1PnPOQmTrEHfwNrzN3LnWGhf7UxeM1cK0njegVPChwEcg==", + "requires": { + "tslib": "^2.3.0" + } + }, + "nice-napi": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nice-napi/-/nice-napi-1.0.2.tgz", + "integrity": "sha512-px/KnJAJZf5RuBGcfD+Sp2pAKq0ytz8j+1NehvgIGFkvtvFrDM3T8E4x/JJODXK9WZow8RRGrbA9QQ3hs+pDhA==", + "dev": true, + "optional": true, + "requires": { + "node-addon-api": "^3.0.0", + "node-gyp-build": "^4.2.2" + } + }, + "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, + "optional": true + }, + "node-fetch": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz", + "integrity": "sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "node-forge": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", + "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "dev": true + }, + "node-gyp": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-9.4.0.tgz", + "integrity": "sha512-dMXsYP6gc9rRbejLXmTbVRYjAHw7ppswsKyMxuxJxxOHzluIO1rGp9TOQgjFJ+2MCqcOcQTOPB/8Xwhr+7s4Eg==", + "dev": true, + "requires": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^11.0.3", + "nopt": "^6.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "dependencies": { + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "node-gyp-build": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.0.tgz", + "integrity": "sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==", + "dev": true, + "optional": true + }, + "node-releases": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", + "dev": true + }, + "nopt": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz", + "integrity": "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==", + "dev": true, + "requires": { + "abbrev": "^1.0.0" + } + }, + "normalize-package-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-4.0.1.tgz", + "integrity": "sha512-EBk5QKKuocMJhB3BILuKhmaPjI8vNRSpIfO9woLC6NyHVkKKdVEdAO1mrT0ZfxNR1lKwCcTkuZfmGIFdizZ8Pg==", + "dev": true, + "requires": { + "hosted-git-info": "^5.0.0", + "is-core-module": "^2.8.1", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + }, + "normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true + }, + "npm-bundled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz", + "integrity": "sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==", + "dev": true, + "requires": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-install-checks": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-5.0.0.tgz", + "integrity": "sha512-65lUsMI8ztHCxFz5ckCEC44DRvEGdZX5usQFriauxHEwt7upv1FKaQEmAtU0YnOAdwuNWCmk64xYiQABNrEyLA==", + "dev": true, + "requires": { + "semver": "^7.1.1" + } + }, + "npm-normalize-package-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", + "dev": true + }, + "npm-package-arg": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-9.1.0.tgz", + "integrity": "sha512-4J0GL+u2Nh6OnhvUKXRr2ZMG4lR8qtLp+kv7UiV00Y+nGiSxtttCyIRHCt5L5BNkXQld/RceYItau3MDOoGiBw==", + "dev": true, + "requires": { + "hosted-git-info": "^5.0.0", + "proc-log": "^2.0.1", + "semver": "^7.3.5", + "validate-npm-package-name": "^4.0.0" + } + }, + "npm-packlist": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-5.1.3.tgz", + "integrity": "sha512-263/0NGrn32YFYi4J533qzrQ/krmmrWwhKkzwTuM4f/07ug51odoaNjUexxO4vxlzURHcmYMH1QjvHjsNDKLVg==", + "dev": true, + "requires": { + "glob": "^8.0.1", + "ignore-walk": "^5.0.1", + "npm-bundled": "^2.0.0", + "npm-normalize-package-bin": "^2.0.0" + }, + "dependencies": { + "npm-bundled": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-2.0.1.tgz", + "integrity": "sha512-gZLxXdjEzE/+mOstGDqR6b0EkhJ+kM6fxM6vUuckuctuVPh80Q6pw/rSZj9s4Gex9GxWtIicO1pc8DB9KZWudw==", + "dev": true, + "requires": { + "npm-normalize-package-bin": "^2.0.0" + } + }, + "npm-normalize-package-bin": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-2.0.0.tgz", + "integrity": "sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ==", + "dev": true + } + } + }, + "npm-pick-manifest": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-7.0.1.tgz", + "integrity": "sha512-IA8+tuv8KujbsbLQvselW2XQgmXWS47t3CB0ZrzsRZ82DbDfkcFunOaPm4X7qNuhMfq+FmV7hQT4iFVpHqV7mg==", + "dev": true, + "requires": { + "npm-install-checks": "^5.0.0", + "npm-normalize-package-bin": "^1.0.1", + "npm-package-arg": "^9.0.0", + "semver": "^7.3.5" + } + }, + "npm-registry-fetch": { + "version": "13.3.1", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-13.3.1.tgz", + "integrity": "sha512-eukJPi++DKRTjSBRcDZSDDsGqRK3ehbxfFUcgaRd0Yp6kRwOwh2WVn0r+8rMB4nnuzvAk6rQVzl6K5CkYOmnvw==", + "dev": true, + "requires": { + "make-fetch-happen": "^10.0.6", + "minipass": "^3.1.6", + "minipass-fetch": "^2.0.3", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.1.2", + "npm-package-arg": "^9.0.1", + "proc-log": "^2.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true + }, + "make-fetch-happen": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz", + "integrity": "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==", + "dev": true, + "requires": { + "agentkeepalive": "^4.2.1", + "cacache": "^16.1.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^2.0.3", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^9.0.0" + } + }, + "minipass-fetch": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-2.1.2.tgz", + "integrity": "sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==", + "dev": true, + "requires": { + "encoding": "^0.1.13", + "minipass": "^3.1.6", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + } + } + } + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "npmlog": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", + "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "dev": true, + "requires": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + } + }, + "nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "requires": { + "boolbase": "^1.0.0" + } + }, + "nwsapi": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz", + "integrity": "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true + }, + "object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true + }, + "object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==" + }, + "obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true + }, + "on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "open": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "dev": true, + "requires": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + } + }, + "ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "requires": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "dependencies": { + "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, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "dev": true, + "requires": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "dependencies": { + "retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "dev": true + } + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "pacote": { + "version": "13.6.2", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-13.6.2.tgz", + "integrity": "sha512-Gu8fU3GsvOPkak2CkbojR7vjs3k3P9cA6uazKTHdsdV0gpCEQq2opelnEv30KRQWgVzP5Vd/5umjcedma3MKtg==", + "dev": true, + "requires": { + "@npmcli/git": "^3.0.0", + "@npmcli/installed-package-contents": "^1.0.7", + "@npmcli/promise-spawn": "^3.0.0", + "@npmcli/run-script": "^4.1.0", + "cacache": "^16.0.0", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "infer-owner": "^1.0.4", + "minipass": "^3.1.6", + "mkdirp": "^1.0.4", + "npm-package-arg": "^9.0.0", + "npm-packlist": "^5.1.0", + "npm-pick-manifest": "^7.0.0", + "npm-registry-fetch": "^13.0.1", + "proc-log": "^2.0.0", + "promise-retry": "^2.0.1", + "read-package-json": "^5.0.0", + "read-package-json-fast": "^2.0.3", + "rimraf": "^3.0.2", + "ssri": "^9.0.0", + "tar": "^6.1.11" + } + }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "parse-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", + "dev": true + }, + "parse5": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", + "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", + "optional": true + }, + "parse5-html-rewriting-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-6.0.1.tgz", + "integrity": "sha512-vwLQzynJVEfUlURxgnf51yAJDQTtVpNyGD8tKi2Za7m+akukNHxCcUQMAa/mUGLhCeicFdpy7Tlvj8ZNKadprg==", + "dev": true, + "requires": { + "parse5": "^6.0.1", + "parse5-sax-parser": "^6.0.1" + }, + "dependencies": { + "parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + } + } + }, + "parse5-htmlparser2-tree-adapter": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", + "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", + "dev": true, + "requires": { + "parse5": "^6.0.1" + }, + "dependencies": { + "parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + } + } + }, + "parse5-sax-parser": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-6.0.1.tgz", + "integrity": "sha512-kXX+5S81lgESA0LsDuGjAlBybImAChYRMT+/uKCEXFBFOeEhS52qUCydGhU3qLRD8D9DVjaUo821WK7DM4iCeg==", + "dev": true, + "requires": { + "parse5": "^6.0.1" + }, + "dependencies": { + "parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", + "dev": true + } + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "path-scurry": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", + "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "dev": true, + "requires": { + "lru-cache": "^9.1.1 || ^10.0.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.0.tgz", + "integrity": "sha512-svTf/fzsKHffP42sujkO/Rjs37BCIsQVRCeNYIm9WN8rgT7ffoUnRtZCqU+6BqcSBdv8gwJeTz8knJpgACeQMw==", + "dev": true + }, + "minipass": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.2.tgz", + "integrity": "sha512-eL79dXrE1q9dBbDCLg7xfn/vl7MS4F1gvJAgjJrQli/jbQWdUttuVawphqpffoIYfRdq78LHx6GP4bU/EQ2ATA==", + "dev": true + } + } + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", + "dev": true + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true + }, + "pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true + }, + "piscina": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/piscina/-/piscina-3.2.0.tgz", + "integrity": "sha512-yn/jMdHRw+q2ZJhFhyqsmANcbF6V2QwmD84c6xRau+QpQOmtrBCoRGdvTfeuFDYXB5W2m6MfLkjkvQa9lUSmIA==", + "dev": true, + "requires": { + "eventemitter-asyncresource": "^1.0.0", + "hdr-histogram-js": "^2.0.1", + "hdr-histogram-percentiles-obj": "^3.0.0", + "nice-napi": "^1.0.2" + } + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + }, + "postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "dev": true, + "requires": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + } + }, + "postcss-attribute-case-insensitive": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.2.tgz", + "integrity": "sha512-XIidXV8fDr0kKt28vqki84fRK8VW8eTuIa4PChv2MqKuT6C9UjmSKzen6KaWhWEoYvwxFCa7n/tC1SZ3tyq4SQ==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.10" + } + }, + "postcss-clamp": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-clamp/-/postcss-clamp-4.1.0.tgz", + "integrity": "sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-color-functional-notation": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-4.2.4.tgz", + "integrity": "sha512-2yrTAUZUab9s6CpxkxC4rVgFEVaR6/2Pipvi6qcgvnYiVqZcbDHEoBDhrXzyb7Efh2CCfHQNtcqWcIruDTIUeg==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-color-hex-alpha": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-8.0.4.tgz", + "integrity": "sha512-nLo2DCRC9eE4w2JmuKgVA3fGL3d01kGq752pVALF68qpGLmx2Qrk91QTKkdUqqp45T1K1XV8IhQpcu1hoAQflQ==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-color-rebeccapurple": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-7.1.1.tgz", + "integrity": "sha512-pGxkuVEInwLHgkNxUc4sdg4g3py7zUeCQ9sMfwyHAT+Ezk8a4OaaVZ8lIY5+oNqA/BXXgLyXv0+5wHP68R79hg==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-custom-media": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.2.tgz", + "integrity": "sha512-7yi25vDAoHAkbhAzX9dHx2yc6ntS4jQvejrNcC+csQJAXjj15e7VcWfMgLqBNAbOvqi5uIa9huOVwdHbf+sKqg==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-custom-properties": { + "version": "12.1.11", + "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.11.tgz", + "integrity": "sha512-0IDJYhgU8xDv1KY6+VgUwuQkVtmYzRwu+dMjnmdMafXYv86SWqfxkc7qdDvWS38vsjaEtv8e0vGOUQrAiMBLpQ==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-custom-selectors": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-6.0.3.tgz", + "integrity": "sha512-fgVkmyiWDwmD3JbpCmB45SvvlCD6z9CG6Ie6Iere22W5aHea6oWa7EM2bpnv2Fj3I94L3VbtvX9KqwSi5aFzSg==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.4" + } + }, + "postcss-dir-pseudo-class": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-6.0.5.tgz", + "integrity": "sha512-eqn4m70P031PF7ZQIvSgy9RSJ5uI2171O/OO/zcRNYpJbvaeKFUlar1aJ7rmgiQtbm0FSPsRewjpdS0Oew7MPA==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.10" + } + }, + "postcss-double-position-gradients": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-3.1.2.tgz", + "integrity": "sha512-GX+FuE/uBR6eskOK+4vkXgT6pDkexLokPaz/AbJna9s5Kzp/yl488pKPjhy0obB475ovfT1Wv8ho7U/cHNaRgQ==", + "dev": true, + "requires": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-env-function": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-4.0.6.tgz", + "integrity": "sha512-kpA6FsLra+NqcFnL81TnsU+Z7orGtDTxcOhl6pwXeEq1yFPpRMkCDpHhrz8CFQDr/Wfm0jLiNQ1OsGGPjlqPwA==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-focus-visible": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-6.0.4.tgz", + "integrity": "sha512-QcKuUU/dgNsstIK6HELFRT5Y3lbrMLEOwG+A4s5cA+fx3A3y/JTq3X9LaOj3OC3ALH0XqyrgQIgey/MIZ8Wczw==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.9" + } + }, + "postcss-focus-within": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-5.0.4.tgz", + "integrity": "sha512-vvjDN++C0mu8jz4af5d52CB184ogg/sSxAFS+oUJQq2SuCe7T5U2iIsVJtsCp2d6R4j0jr5+q3rPkBVZkXD9fQ==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.9" + } + }, + "postcss-font-variant": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", + "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", + "dev": true, + "requires": {} + }, + "postcss-gap-properties": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.5.tgz", + "integrity": "sha512-IuE6gKSdoUNcvkGIqdtjtcMtZIFyXZhmFd5RUlg97iVEvp1BZKV5ngsAjCjrVy+14uhGBQl9tzmi1Qwq4kqVOg==", + "dev": true, + "requires": {} + }, + "postcss-image-set-function": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-4.0.7.tgz", + "integrity": "sha512-9T2r9rsvYzm5ndsBE8WgtrMlIT7VbtTfE7b3BQnudUqnBcBo7L758oc+o+pdj/dUV0l5wjwSdjeOH2DZtfv8qw==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + } + }, + "postcss-initial": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz", + "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==", + "dev": true, + "requires": {} + }, + "postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "dev": true, + "requires": { + "camelcase-css": "^2.0.1" + } + }, + "postcss-lab-function": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-4.2.1.tgz", + "integrity": "sha512-xuXll4isR03CrQsmxyz92LJB2xX9n+pZJ5jE9JgcnmsCammLyKdlzrBin+25dy6wIjfhJpKBAN80gsTlCgRk2w==", + "dev": true, + "requires": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-load-config": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz", + "integrity": "sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==", + "dev": true, + "requires": { + "lilconfig": "^2.0.5", + "yaml": "^2.1.1" + } + }, + "postcss-loader": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.3.3.tgz", + "integrity": "sha512-YgO/yhtevGO/vJePCQmTxiaEwER94LABZN0ZMT4A0vsak9TpO+RvKRs7EmJ8peIlB9xfXCsS7M8LjqncsUZ5HA==", + "dev": true, + "requires": { + "cosmiconfig": "^8.2.0", + "jiti": "^1.18.2", + "semver": "^7.3.8" + } + }, + "postcss-logical": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz", + "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==", + "dev": true, + "requires": {} + }, + "postcss-media-minmax": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz", + "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==", + "dev": true, + "requires": {} + }, + "postcss-modules-extract-imports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "dev": true, + "requires": {} + }, + "postcss-modules-local-by-default": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.3.tgz", + "integrity": "sha512-2/u2zraspoACtrbFRnTijMiQtb4GW4BvatjaG/bCjYQo8kLTdevCUlwuBHx2sCnSyrI3x3qj4ZK1j5LQBgzmwA==", + "dev": true, + "requires": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-modules-scope": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", + "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.4" + } + }, + "postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dev": true, + "requires": { + "icss-utils": "^5.0.0" + } + }, + "postcss-nested": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", + "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.11" + } + }, + "postcss-nesting": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-10.2.0.tgz", + "integrity": "sha512-EwMkYchxiDiKUhlJGzWsD9b2zvq/r2SSubcRrgP+jujMXFzqvANLt16lJANC+5uZ6hjI7lpRmI6O8JIl+8l1KA==", + "dev": true, + "requires": { + "@csstools/selector-specificity": "^2.0.0", + "postcss-selector-parser": "^6.0.10" + } + }, + "postcss-opacity-percentage": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/postcss-opacity-percentage/-/postcss-opacity-percentage-1.1.3.tgz", + "integrity": "sha512-An6Ba4pHBiDtyVpSLymUUERMo2cU7s+Obz6BTrS+gxkbnSBNKSuD0AVUc+CpBMrpVPKKfoVz0WQCX+Tnst0i4A==", + "dev": true, + "requires": {} + }, + "postcss-overflow-shorthand": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.4.tgz", + "integrity": "sha512-otYl/ylHK8Y9bcBnPLo3foYFLL6a6Ak+3EQBPOTR7luMYCOsiVTUk1iLvNf6tVPNGXcoL9Hoz37kpfriRIFb4A==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-page-break": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", + "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", + "dev": true, + "requires": {} + }, + "postcss-place": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-7.0.5.tgz", + "integrity": "sha512-wR8igaZROA6Z4pv0d+bvVrvGY4GVHihBCBQieXFY3kuSuMyOmEnnfFzHl/tQuqHZkfkIVBEbDvYcFfHmpSet9g==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-preset-env": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-7.8.0.tgz", + "integrity": "sha512-leqiqLOellpLKfbHkD06E04P6d9ZQ24mat6hu4NSqun7WG0UhspHR5Myiv/510qouCjoo4+YJtNOqg5xHaFnCA==", + "dev": true, + "requires": { + "@csstools/postcss-cascade-layers": "^1.0.5", + "@csstools/postcss-color-function": "^1.1.1", + "@csstools/postcss-font-format-keywords": "^1.0.1", + "@csstools/postcss-hwb-function": "^1.0.2", + "@csstools/postcss-ic-unit": "^1.0.1", + "@csstools/postcss-is-pseudo-class": "^2.0.7", + "@csstools/postcss-nested-calc": "^1.0.0", + "@csstools/postcss-normalize-display-values": "^1.0.1", + "@csstools/postcss-oklab-function": "^1.1.1", + "@csstools/postcss-progressive-custom-properties": "^1.3.0", + "@csstools/postcss-stepped-value-functions": "^1.0.1", + "@csstools/postcss-text-decoration-shorthand": "^1.0.0", + "@csstools/postcss-trigonometric-functions": "^1.0.2", + "@csstools/postcss-unset-value": "^1.0.2", + "autoprefixer": "^10.4.8", + "browserslist": "^4.21.3", + "css-blank-pseudo": "^3.0.3", + "css-has-pseudo": "^3.0.4", + "css-prefers-color-scheme": "^6.0.3", + "cssdb": "^7.0.0", + "postcss-attribute-case-insensitive": "^5.0.2", + "postcss-clamp": "^4.1.0", + "postcss-color-functional-notation": "^4.2.4", + "postcss-color-hex-alpha": "^8.0.4", + "postcss-color-rebeccapurple": "^7.1.1", + "postcss-custom-media": "^8.0.2", + "postcss-custom-properties": "^12.1.8", + "postcss-custom-selectors": "^6.0.3", + "postcss-dir-pseudo-class": "^6.0.5", + "postcss-double-position-gradients": "^3.1.2", + "postcss-env-function": "^4.0.6", + "postcss-focus-visible": "^6.0.4", + "postcss-focus-within": "^5.0.4", + "postcss-font-variant": "^5.0.0", + "postcss-gap-properties": "^3.0.5", + "postcss-image-set-function": "^4.0.7", + "postcss-initial": "^4.0.1", + "postcss-lab-function": "^4.2.1", + "postcss-logical": "^5.0.4", + "postcss-media-minmax": "^5.0.0", + "postcss-nesting": "^10.1.10", + "postcss-opacity-percentage": "^1.1.2", + "postcss-overflow-shorthand": "^3.0.4", + "postcss-page-break": "^3.0.4", + "postcss-place": "^7.0.5", + "postcss-pseudo-class-any-link": "^7.1.6", + "postcss-replace-overflow-wrap": "^4.0.0", + "postcss-selector-not": "^6.0.1", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-pseudo-class-any-link": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-7.1.6.tgz", + "integrity": "sha512-9sCtZkO6f/5ML9WcTLcIyV1yz9D1rf0tWc+ulKcvV30s0iZKS/ONyETvoWsr6vnrmW+X+KmuK3gV/w5EWnT37w==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.10" + } + }, + "postcss-replace-overflow-wrap": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", + "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", + "dev": true, + "requires": {} + }, + "postcss-scss": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-4.0.9.tgz", + "integrity": "sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==", + "dev": true, + "requires": {} + }, + "postcss-selector-not": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-6.0.1.tgz", + "integrity": "sha512-1i9affjAe9xu/y9uqWH+tD4r6/hDaXJruk8xn2x1vzxC2U3J3LKO3zJW4CyxlNhA56pADJ/djpEwpH1RClI2rQ==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.10" + } + }, + "postcss-selector-parser": { + "version": "6.0.13", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz", + "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + } + }, + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true + }, + "prettier-plugin-organize-attributes": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/prettier-plugin-organize-attributes/-/prettier-plugin-organize-attributes-0.0.5.tgz", + "integrity": "sha512-dSts16q8wd+oq8Zwk5mwmYXo1aN3B+ZkEJqx/ar5fedNHdOvx7S4XDMH/pNK7rmBW0bPXkp/kJX5gAANsWzh3A==", + "dev": true, + "requires": {} + }, + "pretty-bytes": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", + "dev": true + }, + "proc-log": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-2.0.1.tgz", + "integrity": "sha512-Kcmo2FhfDTXdcbfDH76N7uBYHINxc/8GW7UAVuVP9I+Va3uHSerrnKV6dLooga/gh7GlgzuCCr/eoldnL1muGw==", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "dev": true + }, + "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, + "requires": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + } + }, + "proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "dependencies": { + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true + } + } + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", + "dev": true, + "optional": true + }, + "psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" + }, + "punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==" + }, + "qjobs": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", + "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", + "dev": true + }, + "qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "requires": { + "side-channel": "^1.0.4" + } + }, + "querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true + }, + "raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "requires": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "requires": { + "pify": "^2.3.0" + } + }, + "read-package-json": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-5.0.2.tgz", + "integrity": "sha512-BSzugrt4kQ/Z0krro8zhTwV1Kd79ue25IhNN/VtHFy1mG/6Tluyi+msc0UpwaoQzxSHa28mntAjIZY6kEgfR9Q==", + "dev": true, + "requires": { + "glob": "^8.0.1", + "json-parse-even-better-errors": "^2.3.1", + "normalize-package-data": "^4.0.0", + "npm-normalize-package-bin": "^2.0.0" + }, + "dependencies": { + "npm-normalize-package-bin": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-2.0.0.tgz", + "integrity": "sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ==", + "dev": true + } + } + }, + "read-package-json-fast": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-2.0.3.tgz", + "integrity": "sha512-W/BKtbL+dUjTuRL2vziuYhp76s5HZ9qQhd/dKfWIZveD0O40453QNyZhC0e63lqZrAQ4jiOapVoeJ7JrszenQQ==", + "dev": true, + "requires": { + "json-parse-even-better-errors": "^2.3.0", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "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, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "requires": { + "picomatch": "^2.2.1" + } + }, + "reflect-metadata": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" + }, + "regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true + }, + "regenerate-unicode-properties": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz", + "integrity": "sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==", + "dev": true, + "requires": { + "regenerate": "^1.4.2" + } + }, + "regenerator-runtime": { + "version": "0.13.9", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", + "dev": true + }, + "regenerator-transform": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz", + "integrity": "sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.8.4" + } + }, + "regex-parser": { + "version": "2.2.11", + "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz", + "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==", + "dev": true + }, + "regexpu-core": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", + "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", + "dev": true, + "requires": { + "@babel/regjsgen": "^0.8.0", + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.1.0", + "regjsparser": "^0.9.1", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" + } + }, + "regjsparser": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", + "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", + "dev": true, + "requires": { + "jsesc": "~0.5.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "dev": true + } + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" + }, + "resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dev": true, + "requires": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + }, + "resolve-url-loader": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-5.0.0.tgz", + "integrity": "sha512-uZtduh8/8srhBoMx//5bwqjQ+rfYOUq8zC9NrMUGtjBiGTtFJM42s58/36+hTqeqINcnYe08Nj3LkK9lW4N8Xg==", + "dev": true, + "requires": { + "adjust-sourcemap-loader": "^4.0.0", + "convert-source-map": "^1.7.0", + "loader-utils": "^2.0.0", + "postcss": "^8.2.14", + "source-map": "0.6.1" + }, + "dependencies": { + "loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "rfdc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + }, + "dependencies": { + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "rrweb-cssom": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz", + "integrity": "sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==" + }, + "run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "requires": { + "tslib": "^2.1.0" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sass": { + "version": "1.54.4", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.54.4.tgz", + "integrity": "sha512-3tmF16yvnBwtlPrNBHw/H907j8MlOX8aTBnlNX1yrKx24RKcJGPyLhFUwkoKBKesR3unP93/2z14Ll8NicwQUA==", + "dev": true, + "requires": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + } + }, + "sass-loader": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.0.2.tgz", + "integrity": "sha512-BbiqbVmbfJaWVeOOAu2o7DhYWtcNmTfvroVgFXa6k2hHheMxNAeDHLNoDy/Q5aoaVlz0LH+MbMktKwm9vN/j8Q==", + "dev": true, + "requires": { + "klona": "^2.0.4", + "neo-async": "^2.6.2" + } + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, + "saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "requires": { + "xmlchars": "^2.2.0" + } + }, + "schema-utils": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz", + "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.5", + "ajv": "^6.12.4", + "ajv-keywords": "^3.5.2" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "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, + "requires": {} + }, + "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 + } + } + }, + "select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", + "dev": true + }, + "selfsigned": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", + "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", + "dev": true, + "requires": { + "@types/node-forge": "^1.3.0", + "node-forge": "^1" + } + }, + "semver": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", + "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dev": true, + "requires": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + } + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + } + } + }, + "serialize-javascript": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", + "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", + "dev": true, + "requires": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "dev": true + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "dev": true, + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true + } + } + }, + "serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dev": true, + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "requires": { + "kind-of": "^6.0.2" + } + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true + }, + "smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true + }, + "socket.io": { + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.4.tgz", + "integrity": "sha512-DcotgfP1Zg9iP/dH9zvAQcWrE0TtbMVwXmlV4T4mqsvY+gw+LqUGPfx2AoVyRk0FLME+GQhufDMyacFmw7ksqw==", + "dev": true, + "requires": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.5.2", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + } + }, + "socket.io-adapter": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", + "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", + "dev": true, + "requires": { + "debug": "~4.3.4", + "ws": "~8.17.1" + } + }, + "socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "dev": true, + "requires": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + } + }, + "sockjs": { + "version": "0.3.24", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", + "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", + "dev": true, + "requires": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "socks": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.3.tgz", + "integrity": "sha512-vfuYK48HXCTFD03G/1/zkIls3Ebr2YNa4qU9gHDZdblHLiqhJrJGkY3+0Nx0JpN9qBhJbVObc1CNciT1bIZJxw==", + "dev": true, + "requires": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + } + }, + "socks-proxy-agent": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", + "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", + "dev": true, + "requires": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + } + }, + "source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true + }, + "source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true + }, + "source-map-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-4.0.0.tgz", + "integrity": "sha512-i3KVgM3+QPAHNbGavK+VBq03YoJl24m9JWNbLgsjTj8aJzXG9M61bantBTNBt7CNwY2FYf+RJRYJ3pzalKjIrw==", + "dev": true, + "requires": { + "abab": "^2.0.6", + "iconv-lite": "^0.6.3", + "source-map-js": "^1.0.2" + }, + "dependencies": { + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + } + } + }, + "source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "dev": true + }, + "spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz", + "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==", + "dev": true + }, + "spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + } + }, + "spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "ssri": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz", + "integrity": "sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==", + "dev": true, + "requires": { + "minipass": "^3.1.1" + } + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" + }, + "streamroller": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.5.tgz", + "integrity": "sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==", + "dev": true, + "requires": { + "date-format": "^4.0.14", + "debug": "^4.3.4", + "fs-extra": "^8.1.0" + }, + "dependencies": { + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + } + } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "string-width-cjs": { + "version": "npm:string-width@4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-ansi-cjs": { + "version": "npm:strip-ansi@6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, + "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, + "requires": { + "@adobe/css-tools": "^4.0.1", + "debug": "^4.3.2", + "glob": "^7.1.6", + "sax": "~1.2.4", + "source-map": "^0.7.3" + }, + "dependencies": { + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "stylus-loader": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-7.0.0.tgz", + "integrity": "sha512-WTbtLrNfOfLgzTaR9Lj/BPhQroKk/LC1hfTXSUbrxmxgfUo3Y3LpmKRVA2R1XbjvTAvOfaian9vOyfv1z99E+A==", + "dev": true, + "requires": { + "fast-glob": "^3.2.11", + "klona": "^2.0.5", + "normalize-path": "^3.0.0" + } + }, + "sucrase": { + "version": "3.34.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.34.0.tgz", + "integrity": "sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw==", + "dev": true, + "requires": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "7.1.6", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "dependencies": { + "@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true + }, + "symbol-observable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", + "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==", + "dev": true + }, + "symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" + }, + "tailwindcss": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.3.tgz", + "integrity": "sha512-A0KgSkef7eE4Mf+nKJ83i75TMyq8HqY3qmFIJSWy8bNt0v1lG7jUcpGpoTFxAwYcWOphcTBLPPJg+bDfhDf52w==", + "dev": true, + "requires": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.5.3", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.2.12", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.18.2", + "lilconfig": "^2.1.0", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.23", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.1", + "postcss-nested": "^6.0.1", + "postcss-selector-parser": "^6.0.11", + "resolve": "^1.22.2", + "sucrase": "^3.32.0" + }, + "dependencies": { + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + }, + "resolve": { + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", + "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", + "dev": true, + "requires": { + "is-core-module": "^2.11.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + } + } + }, + "tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true + }, + "tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "dev": true, + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "dependencies": { + "minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } + }, + "terser": { + "version": "5.14.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz", + "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", + "dev": true, + "requires": { + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "dependencies": { + "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 + } + } + }, + "terser-webpack-plugin": { + "version": "5.3.9", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz", + "integrity": "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.17", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.1", + "terser": "^5.16.8" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "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, + "requires": {} + }, + "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 + }, + "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 + }, + "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, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "terser": { + "version": "5.19.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.19.2.tgz", + "integrity": "sha512-qC5+dmecKJA4cpYxRa5aVkKehYsQKc+AHeKl0Oe62aYjBL8ZA33tTljktDHJSaxxMnbI5ZYw+o/S2DxxLu8OfA==", + "dev": true, + "requires": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + } + } + } + }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "dependencies": { + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "requires": { + "any-promise": "^1.0.0" + } + }, + "thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "requires": { + "thenify": ">= 3.1.0 < 4" + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, + "thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "requires": { + "is-number": "^7.0.0" + } + }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" + }, + "tough-cookie": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", + "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", + "requires": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "dependencies": { + "universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==" + } + } + }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true + }, + "ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true + }, + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typed-assert": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/typed-assert/-/typed-assert-1.0.9.tgz", + "integrity": "sha512-KNNZtayBCtmnNmbo5mG47p1XsCyrx6iVqomjcZnec/1Y5GGARaxPs6r49RnSPeUP3YjNYiU9sQHAtY4BBvnZwg==", + "dev": true + }, + "typescript": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", + "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", + "dev": true + }, + "ua-parser-js": { + "version": "0.7.35", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.35.tgz", + "integrity": "sha512-veRf7dawaj9xaWEu9HoTVn5Pggtc/qj+kqTOFvNiN1l0YdxwC1kvel57UCjThjGa3BHBihE8/UJAHI+uQHmd/g==", + "dev": true + }, + "unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", + "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", + "dev": true + }, + "unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dev": true, + "requires": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + } + }, + "unicode-match-property-value-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", + "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", + "dev": true + }, + "unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "dev": true + }, + "unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "dev": true, + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" + }, + "update-browserslist-db": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", + "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", + "dev": true, + "requires": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + } + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "url-join": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-5.0.0.tgz", + "integrity": "sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==" + }, + "url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "dev": true + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "validate-npm-package-name": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-4.0.0.tgz", + "integrity": "sha512-mzR0L8ZDktZjpX4OB46KT+56MAhl4EIazWP/+G/HPGuvfdaqg4YsCdtOm6U9+LOFyYDoh4dpnpxZRB9MQQns5Q==", + "dev": true, + "requires": { + "builtins": "^5.0.0" + } + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true + }, + "void-elements": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", + "integrity": "sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung==", + "dev": true + }, + "w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "requires": { + "xml-name-validator": "^5.0.0" + } + }, + "watchpack": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "dev": true, + "requires": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + } + }, + "wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "dev": true, + "requires": { + "minimalistic-assert": "^1.0.0" + } + }, + "wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "requires": { + "defaults": "^1.0.3" + } + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "webpack": { + "version": "5.88.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.2.tgz", + "integrity": "sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ==", + "dev": true, + "peer": true, + "requires": { + "@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", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.9.0", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.15.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.9", + "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", + "webpack-sources": "^3.2.3" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "peer": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "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, + "peer": true, + "requires": {} + }, + "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, + "peer": true + }, + "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, + "peer": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + } + } + }, + "webpack-dev-middleware": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz", + "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==", + "dev": true, + "requires": { + "colorette": "^2.0.10", + "memfs": "^3.4.3", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "dependencies": { + "schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + } + } + } + }, + "webpack-dev-server": { + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.11.0.tgz", + "integrity": "sha512-L5S4Q2zT57SK7tazgzjMiSMBdsw+rGYIX27MgPgx7LDhWO0lViPrHKoLS7jo5In06PWYAhlYu3PbyoC6yAThbw==", + "dev": true, + "requires": { + "@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.1", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.0.11", + "chokidar": "^3.5.3", + "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", + "http-proxy-middleware": "^2.0.3", + "ipaddr.js": "^2.0.1", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "rimraf": "^3.0.2", + "schema-utils": "^4.0.0", + "selfsigned": "^2.0.1", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^5.3.1", + "ws": "^8.4.2" + }, + "dependencies": { + "schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + } + } + } + }, + "webpack-merge": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", + "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", + "dev": true, + "requires": { + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" + } + }, + "webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true + }, + "webpack-subresource-integrity": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-5.1.0.tgz", + "integrity": "sha512-sacXoX+xd8r4WKsy9MvH/q/vBtEHr86cpImXwyg74pFIpERKt6FmB8cXpeuh0ZLgclOlHI4Wcll7+R5L02xk9Q==", + "dev": true, + "requires": { + "typed-assert": "^1.0.8" + } + }, + "websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", + "dev": true, + "requires": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + } + }, + "websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", + "dev": true + }, + "whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "requires": { + "iconv-lite": "0.6.3" + }, + "dependencies": { + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + } + } + }, + "whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dev": true, + "requires": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "wildcard": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", + "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", + "dev": true + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "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, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } + } + }, + "wrap-ansi-cjs": { + "version": "npm:wrap-ansi@7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "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, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "requires": {} + }, + "xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==" + }, + "xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "yaml": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz", + "integrity": "sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==", + "dev": true + }, + "yargs": { + "version": "17.5.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", + "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" + } + }, + "yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true + }, + "zod": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz", + "integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==" + }, + "zone.js": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.12.0.tgz", + "integrity": "sha512-XtC+I5dXU14HrzidAKBNMqneIVUykLEAA1x+v4KVrd6AUPWlwYORF8KgsVqvgdHiKZ4BkxxjvYi/ksEixTPR0Q==", + "requires": { + "tslib": "^2.3.0" + } + } + } +} diff --git a/connector-ui/package.json b/connector-ui/package.json new file mode 100644 index 000000000..cc540581f --- /dev/null +++ b/connector-ui/package.json @@ -0,0 +1,76 @@ +{ + "name": "sovity-edc-ui", + "version": "0.0.0", + "license": "Apache 2.0", + "scripts": { + "cold-start": "npm ci && npm run start", + "ng": "ng", + "start": "npm run generate-config && ng serve --host 0.0.0.0 ", + "start-prod": "npm run generate-config && ng serve --host 0.0.0.0 --configuration=production", + "build": "ng build", + "watch": "ng build --watch --configuration development", + "test": "ng test", + "generate-config": "node config-generator.js", + "format-all": "prettier --write ." + }, + "private": true, + "dependencies": { + "@angular/animations": "^14.3.0", + "@angular/cdk": "^14.2.7", + "@angular/common": "^14.3.0", + "@angular/compiler": "^14.3.0", + "@angular/core": "^14.3.0", + "@angular/flex-layout": "^14.0.0-beta.41", + "@angular/forms": "^14.3.0", + "@angular/material": "^14.2.7", + "@angular/platform-browser": "^14.3.0", + "@angular/platform-browser-dynamic": "^14.3.0", + "@angular/router": "^14.3.0", + "@ng-apimock/core": "^3.11.0", + "@ngx-translate/core": "^14.0.0", + "@ngx-translate/http-loader": "^7.0.0", + "@ngxs/store": "^3.8.1", + "@sovity.de/edc-client": "10.4.0", + "clean-deep": "^3.4.0", + "date-fns": "^2.30.0", + "date-fns-tz": "^2.0.1", + "dotenv": "^16.3.1", + "isomorphic-dompurify": "^2.0.0", + "json-stable-stringify": "^1.0.2", + "marked": "^11.1.1", + "ng2-charts": "^4.1.1", + "ngx-json-viewer": "^3.2.1", + "rxjs": "7.8.1", + "tslib": "^2.6.2", + "url-join": "^5.0.0", + "zone.js": "^0.12.0" + }, + "devDependencies": { + "@angular-devkit/build-angular": "^14.2.12", + "@angular/cli": "^14.2.13", + "@angular/compiler-cli": "^14.3.0", + "@tailwindcss/typography": "^0.5.10", + "@trivago/prettier-plugin-sort-imports": "^4.2.0", + "@types/dompurify": "^3.0.5", + "@types/jasmine": "^4.3.6", + "@types/node": "^18.17.1", + "jasmine-core": "^4.6.0", + "karma": "^6.4.3", + "karma-chrome-launcher": "~3.2.0", + "karma-coverage": "^2.2.1", + "karma-jasmine": "^5.1.0", + "karma-jasmine-html-reporter": "~2.1.0", + "ngx-build-plus": "^14.0.0", + "postcss-import": "^15.1.0", + "postcss-loader": "^7.3.3", + "postcss-scss": "^4.0.9", + "prettier": "^2.8.8", + "prettier-plugin-organize-attributes": "^0.0.5", + "tailwindcss": "^3.3.3", + "typescript": "^4.8.4" + }, + "resolutions": { + "@angular-devkit/build-angular": "14.2.12", + "@angular-devkit/core": "14.3.0" + } +} diff --git a/connector-ui/postcss.config.js b/connector-ui/postcss.config.js new file mode 100644 index 000000000..e569373fd --- /dev/null +++ b/connector-ui/postcss.config.js @@ -0,0 +1,8 @@ +module.exports = { + plugins: { + 'postcss-import': {}, + 'tailwindcss/nesting': {}, + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/connector-ui/prettier.config.js b/connector-ui/prettier.config.js new file mode 100644 index 000000000..f1f673c72 --- /dev/null +++ b/connector-ui/prettier.config.js @@ -0,0 +1,42 @@ +module.exports = { + tabWidth: 2, + useTabs: false, + singleQuote: true, + semi: true, + arrowParens: 'always', + trailingComma: 'all', + bracketSameLine: true, + printWidth: 80, + bracketSpacing: false, + proseWrap: 'always', + + attributeGroups: [ + '$ANGULAR_STRUCTURAL_DIRECTIVE', + '^(id|name)$', + '^class$', + '$DEFAULT', + '^aria-', + '$ANGULAR_INPUT', + '$ANGULAR_TWO_WAY_BINDING', + '$ANGULAR_OUTPUT', + ], + + // @trivago/prettier-plugin-sort-imports + importOrder: [ + // this import needs to be on top or tests fail + '^zone.js/testing$', + // third parties first + '^@angular/(.*)$', + '^rxjs(/(.*))?$', + '', + // rest after + '^[./]', + ], + importOrderParserPlugins: [ + 'typescript', + 'classProperties', + 'decorators-legacy', + ], + importOrderSeparation: false, + importOrderSortSpecifiers: true, +}; diff --git a/connector-ui/src/app/app-routing.module.ts b/connector-ui/src/app/app-routing.module.ts new file mode 100644 index 000000000..87c2d65f6 --- /dev/null +++ b/connector-ui/src/app/app-routing.module.ts @@ -0,0 +1,36 @@ +import {NgModule} from '@angular/core'; +import {ROUTES, RouterModule, Routes} from '@angular/router'; +import {APP_CONFIG, AppConfig} from './core/config/app-config'; +import {PageNotFoundPageComponent} from './routes/connector-ui/page-not-found-page/page-not-found-page.component'; + +@NgModule({ + imports: [RouterModule.forRoot([], {paramsInheritanceStrategy: 'always'})], + exports: [RouterModule], + providers: [ + { + provide: ROUTES, + deps: [APP_CONFIG], + multi: true, + + useFactory: (config: AppConfig): Routes => { + const routes: Routes = []; + switch (config.routes) { + case 'connector-ui': + routes.push({ + path: '', + loadChildren: () => + import('./routes/connector-ui/connector-ui.module').then( + (m) => m.ConnectorUiModule, + ), + }); + break; + default: + throw new Error(`Unhandled PageSet: ${config.routes}`); + } + routes.push({path: '**', component: PageNotFoundPageComponent}); + return routes; + }, + }, + ], +}) +export class AppRoutingModule {} diff --git a/connector-ui/src/app/app.component.html b/connector-ui/src/app/app.component.html new file mode 100644 index 000000000..8f67d6fb5 --- /dev/null +++ b/connector-ui/src/app/app.component.html @@ -0,0 +1,12 @@ + + + +
+ +
diff --git a/connector-ui/src/app/app.component.ts b/connector-ui/src/app/app.component.ts new file mode 100644 index 000000000..97215ff6c --- /dev/null +++ b/connector-ui/src/app/app.component.ts @@ -0,0 +1,23 @@ +import {Component, Inject, OnInit} from '@angular/core'; +import {APP_CONFIG, AppConfig} from './core/config/app-config'; +import {FaviconService} from './core/services/favicon.service'; + +@Component({ + selector: 'app-root', + templateUrl: './app.component.html', +}) +export class AppComponent implements OnInit { + constructor( + @Inject(APP_CONFIG) private config: AppConfig, + private faviconService: FaviconService, + ) {} + + ngOnInit(): void { + this.setThemeFromConfig(); + } + + private setThemeFromConfig() { + window.document.body.classList.add(this.config.theme); + this.faviconService.setFavicon(this.config.brandFaviconSrc); + } +} diff --git a/connector-ui/src/app/app.module.ts b/connector-ui/src/app/app.module.ts new file mode 100644 index 000000000..c166dc256 --- /dev/null +++ b/connector-ui/src/app/app.module.ts @@ -0,0 +1,60 @@ +import { + HTTP_INTERCEPTORS, + HttpClient, + HttpClientModule, +} from '@angular/common/http'; +import {NgModule} from '@angular/core'; +import {BrowserModule} from '@angular/platform-browser'; +import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; +import {TitleStrategy} from '@angular/router'; +import {TranslateLoader, TranslateModule} from '@ngx-translate/core'; +import {TranslateHttpLoader} from '@ngx-translate/http-loader'; +import {NgxsModule} from '@ngxs/store'; +import {NgChartsModule} from 'ng2-charts'; +import {AppRoutingModule} from './app-routing.module'; +import {AppComponent} from './app.component'; +import {provideAppConfig} from './core/config/app-config-initializer'; +import {ApiKeyInterceptor} from './core/services/api/api-key.interceptor'; +import {CustomPageTitleStrategy} from './core/services/page-title-strategy'; +import {SharedModule} from './shared/shared.module'; + +@NgModule({ + imports: [ + // Angular + BrowserAnimationsModule, + BrowserModule, + HttpClientModule, + + //Translation + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useFactory: (http: HttpClient) => new TranslateHttpLoader(http), + deps: [HttpClient], + }, + }), + + // NgXs + NgxsModule.forRoot([]), + + // Third Party + NgChartsModule.forRoot(), + + // Features + SharedModule, + + // Routing + AppRoutingModule, + ], + declarations: [AppComponent], + providers: [ + HttpClient, + provideAppConfig(), + + {provide: HTTP_INTERCEPTORS, multi: true, useClass: ApiKeyInterceptor}, + {provide: TitleStrategy, useClass: CustomPageTitleStrategy}, + ], + bootstrap: [AppComponent], + exports: [TranslateModule], +}) +export class AppModule {} diff --git a/connector-ui/src/app/core/adapters/custom-date-adapter.ts b/connector-ui/src/app/core/adapters/custom-date-adapter.ts new file mode 100644 index 000000000..d920b162c --- /dev/null +++ b/connector-ui/src/app/core/adapters/custom-date-adapter.ts @@ -0,0 +1,23 @@ +import {Injectable} from '@angular/core'; +import {NativeDateAdapter} from '@angular/material/core'; +import {isValid, parse as parseDate} from 'date-fns'; +import {format as formateDate} from 'date-fns-tz'; + +@Injectable() +export class CustomDateAdapter extends NativeDateAdapter { + parse(value: any): Date | null { + if (typeof value === 'string' && value.indexOf('/') > -1) { + const parsedDate = parseDate(value, 'dd/MM/yyyy', new Date()); + return isValid(parsedDate) ? parsedDate : null; + } + + const timestamp = typeof value === 'number' ? value : Date.parse(value); + return isNaN(timestamp) ? null : new Date(timestamp); + } + + format(date: Date, displayFormat: Object): string { + return formateDate(date, 'dd/MM/yyyy', { + timeZone: 'UTC', + }); + } +} diff --git a/connector-ui/src/app/core/config/active-feature-set.ts b/connector-ui/src/app/core/config/active-feature-set.ts new file mode 100644 index 000000000..b6f58cd50 --- /dev/null +++ b/connector-ui/src/app/core/config/active-feature-set.ts @@ -0,0 +1,20 @@ +import {Inject, Injectable} from '@angular/core'; +import {APP_CONFIG, AppConfig} from './app-config'; +import {EdcUiFeature} from './profiles/edc-ui-feature'; + +@Injectable({providedIn: 'root'}) +export class ActiveFeatureSet { + constructor(@Inject(APP_CONFIG) private config: AppConfig) {} + + hasMdsFields(): boolean { + return this.has('mds-fields'); + } + + hasConnectorLimits(): boolean { + return this.has('connector-limits'); + } + + has(feature: EdcUiFeature): boolean { + return this.config.features.has(feature); + } +} diff --git a/connector-ui/src/app/core/config/app-config-initializer.ts b/connector-ui/src/app/core/config/app-config-initializer.ts new file mode 100644 index 000000000..1185fbc19 --- /dev/null +++ b/connector-ui/src/app/core/config/app-config-initializer.ts @@ -0,0 +1,22 @@ +import {Provider} from '@angular/core'; +import {APP_CONFIG, AppConfig} from './app-config'; +import {AppConfigBuilder} from './app-config.builder'; +import {AppConfigFetcher} from './app-config.fetcher'; +import {AppConfigMerger} from './app-config.merger'; + +let appConfig: AppConfig | null = null; + +export async function loadAppConfig(): Promise { + const merger = new AppConfigMerger(); + const builder = new AppConfigBuilder(); + const fetcher = new AppConfigFetcher(merger); + return fetcher + .fetchEffectiveConfig('/assets/config/app-configuration.json', null) + .then((json) => builder.buildAppConfig(json)) + .then((config) => (appConfig = config)); +} + +export const provideAppConfig = (): Provider => ({ + provide: APP_CONFIG, + useFactory: () => appConfig, +}); diff --git a/connector-ui/src/app/core/config/app-config-injection-utils.ts b/connector-ui/src/app/core/config/app-config-injection-utils.ts new file mode 100644 index 000000000..771cb85b9 --- /dev/null +++ b/connector-ui/src/app/core/config/app-config-injection-utils.ts @@ -0,0 +1,19 @@ +import {InjectionToken, Provider} from '@angular/core'; +import {KeysOfType} from '../utils/type-utils'; +import {APP_CONFIG, AppConfig} from './app-config'; + +/** + * Provide individual {@link AppConfig} properties for better Angular Component APIs. + * + * @param token injection token + * @param key property in {@link AppConfig} + * @return {@link Provider} + */ +export const provideAppConfigProperty = ( + token: InjectionToken, + key: KeysOfType, +): Provider => ({ + provide: token, + useFactory: (s: AppConfig) => s[key], + deps: [APP_CONFIG], +}); diff --git a/connector-ui/src/app/core/config/app-config-properties.ts b/connector-ui/src/app/core/config/app-config-properties.ts new file mode 100644 index 000000000..7511ca17c --- /dev/null +++ b/connector-ui/src/app/core/config/app-config-properties.ts @@ -0,0 +1,68 @@ +/** + * Supported Config ENV Vars + * + * All ENV Vars need to start with EDC_UI_ because only those will be written into app-config.json. + */ +export const AppConfigProperties = { + /** + * Instead of providing multiple ENV Vars, + * provide a single one as JSON. + * + * Individual ENV Vars will take precedence over this JSON. + */ + configJson: 'EDC_UI_CONFIG_JSON', + + /** + * Additional URL to fetch a Config JSON from that will take precedence. + * + * This allows an EDC Backend Extension to provide EDC UI configuration + * + * If this URL is relative, it will be appended to the {@link AppConfigProperties.managementApiUrl}. + * + * The JSON should be a {@link Record} + */ + configUrl: 'EDC_UI_CONFIG_URL', + + /** + * Customer-Specific Feature Set and/or Theme. + * + * See {@link EDC_UI_PROFILE_DATA} for all available profiles. + */ + activeProfile: 'EDC_UI_ACTIVE_PROFILE', + + /** + * EDC Backend URL + */ + managementApiUrl: 'EDC_UI_MANAGEMENT_API_URL', + + /** + * Hard-Coded API key (?) + */ + managementApiKey: 'EDC_UI_MANAGEMENT_API_KEY', + + /** + * Overridden management API URL to be displayed for the user + */ + shownManagementApiUrl: 'EDC_UI_MANAGEMENT_API_URL_SHOWN_IN_DASHBOARD', + + /** + * Logout URL. + */ + logoutUrl: 'EDC_UI_LOGOUT_URL', + + /** + * Pre-configured Other Connector Endpoints to be used in catalog browser, comma separated. + */ + catalogUrls: 'EDC_UI_CATALOG_URLS', + + /** + * Whether to use the fake backend (local development). + */ + useFakeBackend: 'EDC_UI_USE_FAKE_BACKEND', + + /** + * Only for Enterprise Edition. + * Enables Marketing for other Enterprise Edition Variants. + */ + showEeBasicMarketing: 'EDC_UI_SHOW_EE_BASIC_MARKETING', +}; diff --git a/connector-ui/src/app/core/config/app-config.builder.ts b/connector-ui/src/app/core/config/app-config.builder.ts new file mode 100644 index 000000000..770299dbe --- /dev/null +++ b/connector-ui/src/app/core/config/app-config.builder.ts @@ -0,0 +1,46 @@ +import {Injectable} from '@angular/core'; +import {AppConfig} from './app-config'; +import {AppConfigProperties} from './app-config-properties'; +import {getProfileOrFallback} from './profiles/get-profile-or-fallback'; + +@Injectable() +export class AppConfigBuilder { + /** + * Build {@link AppConfig} from ENV Vars + * + * @param vars env vars + */ + buildAppConfig(vars: Record): AppConfig { + const {profile, profileConfig} = getProfileOrFallback( + vars[AppConfigProperties.activeProfile], + ); + + return { + // profile and theme + profile, + ...profileConfig, + + // EDC Backend Endpoints + managementApiKey: + vars[AppConfigProperties.managementApiKey] ?? 'no-api-key-configured', + managementApiUrl: + vars[AppConfigProperties.managementApiUrl] ?? + 'https://no-backend-api-url-configured', + logoutUrl: + vars[AppConfigProperties.logoutUrl] ?? + 'https://no-logout-url-configured', + shownManagementApiUrl: + vars[AppConfigProperties.shownManagementApiUrl] ?? + vars[AppConfigProperties.managementApiUrl] ?? + 'https://no-backend-api-url-configured', + + // Other EDC Backend Endpoints + catalogUrls: vars[AppConfigProperties.catalogUrls] ?? '', + useFakeBackend: vars[AppConfigProperties.useFakeBackend] === 'true', + + // Enterprise Edition + showEeBasicMarketing: + vars[AppConfigProperties.showEeBasicMarketing] === 'true', + }; + } +} diff --git a/connector-ui/src/app/core/config/app-config.fetcher.ts b/connector-ui/src/app/core/config/app-config.fetcher.ts new file mode 100644 index 000000000..99b7c6ff5 --- /dev/null +++ b/connector-ui/src/app/core/config/app-config.fetcher.ts @@ -0,0 +1,82 @@ +import urlJoin from 'url-join'; +import {validUrlPattern} from '../validators/url-validator'; +import {AppConfigProperties} from './app-config-properties'; +import {AppConfigMerger} from './app-config.merger'; + +export class AppConfigFetcher { + constructor(private appConfigMerger: AppConfigMerger) {} + + /** + * Fetches app-config.json, applies {@link AppConfigProperties.configJson}, + * fetches another config from {@link AppConfigProperties.configUrl}, and + * merges the results. + */ + async fetchEffectiveConfig( + configUrl: string, + apiKey: string | null, + ): Promise> { + let config = await this.fetchConfigJson(configUrl, apiKey); + config = this.appConfigMerger.applyEmbeddedConfig(configUrl, config); + + const additionalConfigUrl = this.buildAdditionConfigUrl(config); + if (additionalConfigUrl) { + apiKey = config[AppConfigProperties.managementApiKey] ?? apiKey; + const additionalConfig = await this.fetchEffectiveConfig( + additionalConfigUrl, + apiKey, + ); + config = this.appConfigMerger.mergeConfigs( + configUrl, + config, + additionalConfigUrl, + additionalConfig, + ); + } + + return config; + } + + private fetchConfigJson( + path: string, + apiKey: string | null, + ): Promise> { + const headers = apiKey ? {'X-API-KEY': apiKey} : undefined; + + // We fetch the config using the Fetch API because we want to fetch it before application initialization + // At this time the Angular Http Client is not ready yet + return fetch(path, {headers}) + .then((response) => response.json()) + .catch((err) => { + console.error(`Could not fetch app-config.json from ${path}`, err); + return {}; + }); + } + + private buildAdditionConfigUrl( + config: Record, + ): string | null { + const relativeUrl = config[AppConfigProperties.configUrl]; + if (!relativeUrl) { + return null; + } + + // Absolute URL + if (validUrlPattern.test(relativeUrl)) { + return relativeUrl; + } + + // Relative URL + const managementApiUrl = config[AppConfigProperties.managementApiUrl]; + + if (!managementApiUrl) { + console.error( + `Invalid value for ${AppConfigProperties.configUrl} and ${AppConfigProperties.managementApiUrl}. Could not build Additional Config URL:`, + relativeUrl, + managementApiUrl, + ); + return null; + } + + return urlJoin(managementApiUrl, relativeUrl); + } +} diff --git a/connector-ui/src/app/core/config/app-config.merger.ts b/connector-ui/src/app/core/config/app-config.merger.ts new file mode 100644 index 000000000..b588a4e9f --- /dev/null +++ b/connector-ui/src/app/core/config/app-config.merger.ts @@ -0,0 +1,91 @@ +import {AppConfigProperties} from './app-config-properties'; + +export class AppConfigMerger { + /** + * Merges two configs. + * + * @param configName first config name for logging + * @param config first config + * @param overridesName second config name for logging + * @param overrides second config (takes precedence) + */ + mergeConfigs( + configName: string, + config: Record, + overridesName: string, + overrides: Record, + ): Record { + Object.keys(config) + .filter((key) => overrides.hasOwnProperty(key)) + .forEach((key) => { + if (config[key] != overrides[key]) { + console.info( + `Overriding '${key}' from '${configName}' with value '${config[key]}'` + + ` with '${overrides[key]}' from '${overridesName}'.`, + ); + } + }); + + return { + ...config, + ...overrides, + }; + } + + /** + * Applies special value {@link AppConfigProperties.configJson} that might contain a JSON as single property. + * + * @param configName for better error logging + * @param config config + */ + applyEmbeddedConfig( + configName: string, + config: Record, + ): Record { + // Read JSON property + const embeddedConfigName = `${configName} -> ${AppConfigProperties.configJson}`; + let embeddedConfig = this.parseEmbeddedConfig(config); + + // Apply Embedded Config recursively + if (embeddedConfig.hasOwnProperty(AppConfigProperties.configJson)) { + embeddedConfig = this.applyEmbeddedConfig( + embeddedConfigName, + embeddedConfig, + ); + } + + // Remove Embedded Config key from config for cleanliness + const {[AppConfigProperties.configJson]: _, ...rest} = config; + + // Merge with original config taking precedence + config = this.mergeConfigs( + embeddedConfigName, + embeddedConfig, + `${configName}`, + rest, + ); + + return config; + } + + /** + * Tries to parse {@link AppConfigProperties.configJson} as JSON. + * + * @param config config + * @returns parsed JSON + */ + private parseEmbeddedConfig( + config: Record, + ): Record { + try { + return JSON.parse(config[AppConfigProperties.configJson] || '{}'); + } catch (e) { + console.error( + `Could not parse not parse embedded Config JSON`, + e, + config[AppConfigProperties.configJson], + ); + return {}; + } + } +} diff --git a/connector-ui/src/app/core/config/app-config.ts b/connector-ui/src/app/core/config/app-config.ts new file mode 100644 index 000000000..4fc6b8011 --- /dev/null +++ b/connector-ui/src/app/core/config/app-config.ts @@ -0,0 +1,41 @@ +import {InjectionToken} from '@angular/core'; +import {EdcUiColorTheme} from './profiles/edc-ui-color-theme'; +import {EdcUiFeature} from './profiles/edc-ui-feature'; +import {EdcUiProfile} from './profiles/edc-ui-profile'; +import {EdcUiRouteSet} from './profiles/edc-ui-route-set'; + +/** + * Injection Token for {@link AppConfig} + */ +export const APP_CONFIG = new InjectionToken('APP_CONFIG'); + +/** + * Type-Safe and interpreted App Config + * + * See {@link AppConfigProperties} for available ENV Vars. + */ +export interface AppConfig { + // selected profile + profile: EdcUiProfile; + features: Set; + routes: EdcUiRouteSet; + + // selected theme (by profile) + theme: EdcUiColorTheme; + brandFaviconSrc: string; + brandLogoSrc: string; + brandLogoStyle: string; + + // EDC Backend Endpoints + managementApiUrl: string; + managementApiKey: string; + logoutUrl: string; // requires feature flag logout-button + shownManagementApiUrl: string; + + // Other EDC Backend Endpoints + catalogUrls: string; + useFakeBackend: boolean; + + // Enterprise Edition + showEeBasicMarketing: boolean; +} diff --git a/connector-ui/src/app/core/config/profiles/edc-ui-color-theme.ts b/connector-ui/src/app/core/config/profiles/edc-ui-color-theme.ts new file mode 100644 index 000000000..d848c2050 --- /dev/null +++ b/connector-ui/src/app/core/config/profiles/edc-ui-color-theme.ts @@ -0,0 +1 @@ +export type EdcUiColorTheme = 'theme-sovity' | 'theme-mds'; diff --git a/connector-ui/src/app/core/config/profiles/edc-ui-feature.ts b/connector-ui/src/app/core/config/profiles/edc-ui-feature.ts new file mode 100644 index 000000000..4d498abf2 --- /dev/null +++ b/connector-ui/src/app/core/config/profiles/edc-ui-feature.ts @@ -0,0 +1,21 @@ +export type EdcUiFeature = + // Enables MDS Specific Asset Fields such as Data Category, Transport Mode + | 'mds-fields' + + // Enables MDS Specific Connector ID support + | 'mds-connector-id' + + // Enables support functionalities of connectors commercially hosted by sovity. + | 'sovity-zammad-integration' + + // Enables logout button to configured LOGOUT_URL + | 'logout-button' + + // Enables marketing for sovity in open-source variants + | 'open-source-marketing' + + // Enterprise Edition specific attribute to view limits enforced on consuming contract agreements + | 'connector-limits' + + // Enterprise Edition specific flag to enable marketing for other Enterprise Edition variants in basic connectors + | 'mds-marketing'; diff --git a/connector-ui/src/app/core/config/profiles/edc-ui-profile-config.ts b/connector-ui/src/app/core/config/profiles/edc-ui-profile-config.ts new file mode 100644 index 000000000..1dc46c795 --- /dev/null +++ b/connector-ui/src/app/core/config/profiles/edc-ui-profile-config.ts @@ -0,0 +1,5 @@ +import {AppConfig} from '../app-config'; +import {EdcUiThemeConfig} from './edc-ui-theme-config'; + +export type EdcUiProfileConfig = Pick & + EdcUiThemeConfig; diff --git a/connector-ui/src/app/core/config/profiles/edc-ui-profile-data.ts b/connector-ui/src/app/core/config/profiles/edc-ui-profile-data.ts new file mode 100644 index 000000000..0f43746b8 --- /dev/null +++ b/connector-ui/src/app/core/config/profiles/edc-ui-profile-data.ts @@ -0,0 +1,42 @@ +import {MDS_THEME, SOVITY_THEME} from './edc-ui-theme-data'; +import {COMMUNITY_EDITION_FEATURES} from './feature-sets/community-edition-features'; +import {ENTERPRISE_EDITION_FEATURES} from './feature-sets/enterprise-edition-features'; +import {MDS_FEATURES} from './feature-sets/mds-features'; +import {inferEdcUiProfileType} from './infer-edc-ui-profile-type'; + +/** + * List of available profiles. + * + * This codebase supports multiple profiles since it incorporates multiple deployment targets. + */ +export const EDC_UI_PROFILE_DATA = inferEdcUiProfileType({ + 'sovity-open-source': { + ...SOVITY_THEME, + routes: 'connector-ui', + features: new Set(COMMUNITY_EDITION_FEATURES), + }, + 'sovity-hosted-by-sovity': { + ...SOVITY_THEME, + routes: 'connector-ui', + features: new Set(ENTERPRISE_EDITION_FEATURES), + }, + 'mds-open-source': { + ...MDS_THEME, + routes: 'connector-ui', + features: new Set([...MDS_FEATURES, ...COMMUNITY_EDITION_FEATURES]), + }, + 'mds-hosted-by-sovity': { + ...MDS_THEME, + routes: 'connector-ui', + features: new Set([ + 'mds-marketing', + ...MDS_FEATURES, + ...ENTERPRISE_EDITION_FEATURES, + ]), + }, + 'mds-blue-hosted-by-sovity': { + ...SOVITY_THEME, + routes: 'connector-ui', + features: new Set([...MDS_FEATURES, ...ENTERPRISE_EDITION_FEATURES]), + }, +}); diff --git a/connector-ui/src/app/core/config/profiles/edc-ui-profile.ts b/connector-ui/src/app/core/config/profiles/edc-ui-profile.ts new file mode 100644 index 000000000..0e8f4d4e0 --- /dev/null +++ b/connector-ui/src/app/core/config/profiles/edc-ui-profile.ts @@ -0,0 +1,6 @@ +import {EDC_UI_PROFILE_DATA} from './edc-ui-profile-data'; + +/** + * Available Configuration Profiles. + */ +export type EdcUiProfile = keyof typeof EDC_UI_PROFILE_DATA; diff --git a/connector-ui/src/app/core/config/profiles/edc-ui-route-set.ts b/connector-ui/src/app/core/config/profiles/edc-ui-route-set.ts new file mode 100644 index 000000000..7392c0d7b --- /dev/null +++ b/connector-ui/src/app/core/config/profiles/edc-ui-route-set.ts @@ -0,0 +1,4 @@ +/** + * Multiple "applications" on an edc basis are currently supported by this UI + */ +export type EdcUiRouteSet = 'connector-ui'; diff --git a/connector-ui/src/app/core/config/profiles/edc-ui-theme-config.ts b/connector-ui/src/app/core/config/profiles/edc-ui-theme-config.ts new file mode 100644 index 000000000..5a78190fe --- /dev/null +++ b/connector-ui/src/app/core/config/profiles/edc-ui-theme-config.ts @@ -0,0 +1,9 @@ +import {AppConfig} from '../app-config'; + +/** + * Type-Safe and interpreted App Config + */ +export type EdcUiThemeConfig = Pick< + AppConfig, + 'theme' | 'brandLogoStyle' | 'brandLogoSrc' | 'brandFaviconSrc' +>; diff --git a/connector-ui/src/app/core/config/profiles/edc-ui-theme-data.ts b/connector-ui/src/app/core/config/profiles/edc-ui-theme-data.ts new file mode 100644 index 000000000..3d983a05c --- /dev/null +++ b/connector-ui/src/app/core/config/profiles/edc-ui-theme-data.ts @@ -0,0 +1,15 @@ +import {EdcUiThemeConfig} from './edc-ui-theme-config'; + +export const SOVITY_THEME: EdcUiThemeConfig = { + theme: 'theme-sovity', + brandFaviconSrc: '/assets/images/sovity_favicon-192x192.png', + brandLogoSrc: '/assets/images/sovity_logo.svg', + brandLogoStyle: 'width: 70%;', +}; + +export const MDS_THEME: EdcUiThemeConfig = { + theme: 'theme-mds', + brandFaviconSrc: '/assets/images/mds_favicon.ico', + brandLogoSrc: '/assets/images/mds_logo.svg', + brandLogoStyle: 'height: 57px; margin-top: 5px; margin-left: 5px;', +}; diff --git a/connector-ui/src/app/core/config/profiles/feature-sets/community-edition-features.ts b/connector-ui/src/app/core/config/profiles/feature-sets/community-edition-features.ts new file mode 100644 index 000000000..a9f0f9a8f --- /dev/null +++ b/connector-ui/src/app/core/config/profiles/feature-sets/community-edition-features.ts @@ -0,0 +1,5 @@ +import {EdcUiFeature} from '../edc-ui-feature'; + +export const COMMUNITY_EDITION_FEATURES: EdcUiFeature[] = [ + 'open-source-marketing', +]; diff --git a/connector-ui/src/app/core/config/profiles/feature-sets/enterprise-edition-features.ts b/connector-ui/src/app/core/config/profiles/feature-sets/enterprise-edition-features.ts new file mode 100644 index 000000000..3a153260f --- /dev/null +++ b/connector-ui/src/app/core/config/profiles/feature-sets/enterprise-edition-features.ts @@ -0,0 +1,7 @@ +import {EdcUiFeature} from '../edc-ui-feature'; + +export const ENTERPRISE_EDITION_FEATURES: EdcUiFeature[] = [ + 'logout-button', + 'connector-limits', + 'sovity-zammad-integration', +]; diff --git a/connector-ui/src/app/core/config/profiles/feature-sets/mds-features.ts b/connector-ui/src/app/core/config/profiles/feature-sets/mds-features.ts new file mode 100644 index 000000000..90b663c05 --- /dev/null +++ b/connector-ui/src/app/core/config/profiles/feature-sets/mds-features.ts @@ -0,0 +1,3 @@ +import {EdcUiFeature} from '../edc-ui-feature'; + +export const MDS_FEATURES: EdcUiFeature[] = ['mds-connector-id', 'mds-fields']; diff --git a/connector-ui/src/app/core/config/profiles/get-profile-or-fallback.ts b/connector-ui/src/app/core/config/profiles/get-profile-or-fallback.ts new file mode 100644 index 000000000..41f00bf4e --- /dev/null +++ b/connector-ui/src/app/core/config/profiles/get-profile-or-fallback.ts @@ -0,0 +1,37 @@ +import {AppConfigProperties} from '../app-config-properties'; +import {EdcUiProfile} from './edc-ui-profile'; +import {EdcUiProfileConfig} from './edc-ui-profile-config'; +import {EDC_UI_PROFILE_DATA} from './edc-ui-profile-data'; + +/** + * Find profile (or default to first) + * @param profile profile + */ +export function getProfileOrFallback(profile?: string | null): { + profile: EdcUiProfile; + profileConfig: EdcUiProfileConfig; +} { + if (EDC_UI_PROFILE_DATA[profile as EdcUiProfile]) { + return { + profile: profile as EdcUiProfile, + profileConfig: EDC_UI_PROFILE_DATA[profile as EdcUiProfile], + }; + } + + const fallback: EdcUiProfile = 'sovity-open-source'; + + const availableProfiles = Object.keys(EDC_UI_PROFILE_DATA) + .map((s) => `"${s}"`) + .join(', '); + + console.error( + `Invalid ${AppConfigProperties.activeProfile}: ${JSON.stringify(profile)}.`, + `Expected one of ${availableProfiles}.`, + `Falling back to ${JSON.stringify(fallback)}'.`, + ); + + return { + profile: fallback, + profileConfig: EDC_UI_PROFILE_DATA[fallback], + }; +} diff --git a/connector-ui/src/app/core/config/profiles/infer-edc-ui-profile-type.ts b/connector-ui/src/app/core/config/profiles/infer-edc-ui-profile-type.ts new file mode 100644 index 000000000..f5bf26784 --- /dev/null +++ b/connector-ui/src/app/core/config/profiles/infer-edc-ui-profile-type.ts @@ -0,0 +1,13 @@ +import {EdcUiProfileConfig} from './edc-ui-profile-config'; + +/** + * Type utility for inferring the keys of EDC_UI_PROFILE_DATA as type. + * see https://stackoverflow.com/a/74691877 + * + * @param profiles Record + */ +export const inferEdcUiProfileType = < + T extends Record, +>( + profiles: T, +) => profiles; diff --git a/connector-ui/src/app/core/services/api/api-key.interceptor.ts b/connector-ui/src/app/core/services/api/api-key.interceptor.ts new file mode 100644 index 000000000..885bc6542 --- /dev/null +++ b/connector-ui/src/app/core/services/api/api-key.interceptor.ts @@ -0,0 +1,31 @@ +import { + HttpEvent, + HttpHandler, + HttpInterceptor, + HttpRequest, +} from '@angular/common/http'; +import {Inject, Injectable} from '@angular/core'; +import {Observable} from 'rxjs'; +import {APP_CONFIG, AppConfig} from '../../config/app-config'; + +@Injectable() +export class ApiKeyInterceptor implements HttpInterceptor { + constructor(@Inject(APP_CONFIG) private config: AppConfig) {} + + intercept( + req: HttpRequest, + next: HttpHandler, + ): Observable> { + const apiKey = this.getApiKey(); + if (apiKey) { + req = req.clone({ + setHeaders: {'X-Api-Key': apiKey}, + }); + } + return next.handle(req); + } + + private getApiKey() { + return this.config.managementApiKey; + } +} diff --git a/connector-ui/src/app/core/services/api/edc-api.service.ts b/connector-ui/src/app/core/services/api/edc-api.service.ts new file mode 100644 index 000000000..2575fb157 --- /dev/null +++ b/connector-ui/src/app/core/services/api/edc-api.service.ts @@ -0,0 +1,250 @@ +import {Inject, Injectable} from '@angular/core'; +import {Observable} from 'rxjs'; +import { + AssetPage, + ConnectorLimits, + ContractAgreementCard, + ContractAgreementPage, + ContractDefinitionPage, + ContractDefinitionRequest, + ContractNegotiationRequest, + CreateDataOfferRequest, + DashboardPage, + EdcClient, + GetContractAgreementPageRequest, + IdAvailabilityResponse, + IdResponseDto, + InitiateCustomTransferRequest, + InitiateTransferRequest, + PolicyDefinitionCreateDto, + PolicyDefinitionCreateRequest, + PolicyDefinitionPage, + TerminateContractAgreementRequest, + TransferHistoryPage, + UiAsset, + UiAssetCreateRequest, + UiAssetEditRequest, + UiContractNegotiation, + UiDataOffer, + buildEdcClient, +} from '@sovity.de/edc-client'; +import {APP_CONFIG, AppConfig} from '../../config/app-config'; +import {toObservable} from '../../utils/rxjs-utils'; +import {EDC_FAKE_BACKEND} from './fake-backend/edc-fake-backend'; + +@Injectable({providedIn: 'root'}) +export class EdcApiService { + edcClient: EdcClient; + + constructor(@Inject(APP_CONFIG) private config: AppConfig) { + this.edcClient = buildEdcClient({ + managementApiUrl: this.config.managementApiUrl, + managementApiKey: this.config.managementApiKey, + configOverrides: { + fetchApi: config.useFakeBackend ? EDC_FAKE_BACKEND : undefined, + }, + }); + } + + getDashboardPage(): Observable { + return toObservable(() => this.edcClient.uiApi.getDashboardPage()); + } + + getAssetPage(): Observable { + return toObservable(() => this.edcClient.uiApi.getAssetPage()); + } + + createAsset( + uiAssetCreateRequest: UiAssetCreateRequest, + ): Observable { + return toObservable(() => + this.edcClient.uiApi.createAsset({uiAssetCreateRequest}), + ); + } + + editAsset( + assetId: string, + uiAssetEditRequest: UiAssetEditRequest, + ): Observable { + return toObservable(() => + this.edcClient.uiApi.editAsset({ + assetId, + uiAssetEditRequest, + }), + ); + } + + deleteAsset(assetId: string): Observable { + return toObservable(() => this.edcClient.uiApi.deleteAsset({assetId})); + } + + getPolicyDefinitionPage(): Observable { + return toObservable(() => this.edcClient.uiApi.getPolicyDefinitionPage()); + } + + createPolicyDefinition( + policyDefinitionCreateRequest: PolicyDefinitionCreateRequest, + ): Observable { + return toObservable(() => + this.edcClient.uiApi.createPolicyDefinition({ + policyDefinitionCreateRequest, + }), + ); + } + + createPolicyDefinitionV2( + policyDefinitionCreateDto: PolicyDefinitionCreateDto, + ): Observable { + return toObservable(() => + this.edcClient.uiApi.createPolicyDefinitionV2({ + policyDefinitionCreateDto, + }), + ); + } + + deletePolicyDefinition(policyId: string): Observable { + return toObservable(() => + this.edcClient.uiApi.deletePolicyDefinition({policyId}), + ); + } + + getContractDefinitionPage(): Observable { + return toObservable(() => this.edcClient.uiApi.getContractDefinitionPage()); + } + + createContractDefinition( + contractDefinitionRequest: ContractDefinitionRequest, + ): Observable { + return toObservable(() => + this.edcClient.uiApi.createContractDefinition({ + contractDefinitionRequest, + }), + ); + } + + deleteContractDefinition( + contractDefinitionId: string, + ): Observable { + return toObservable(() => + this.edcClient.uiApi.deleteContractDefinition({contractDefinitionId}), + ); + } + + createDataOffer( + dataOfferCreationRequest: CreateDataOfferRequest, + ): Observable { + return toObservable(() => + this.edcClient.uiApi.createDataOffer(dataOfferCreationRequest), + ); + } + + getCatalogPageDataOffers( + connectorEndpoint: string, + ): Observable { + return toObservable(() => + this.edcClient.uiApi.getCatalogPageDataOffers({connectorEndpoint}), + ); + } + + initiateContractNegotiation( + contractNegotiationRequest: ContractNegotiationRequest, + ): Observable { + return toObservable(() => + this.edcClient.uiApi.initiateContractNegotiation({ + contractNegotiationRequest, + }), + ); + } + + terminateContractAgreement( + terminateContractAgreementRequest: TerminateContractAgreementRequest, + ): Observable { + return toObservable(() => + this.edcClient.uiApi.terminateContractAgreement( + terminateContractAgreementRequest, + ), + ); + } + + getContractNegotiation( + contractNegotiationId: string, + ): Observable { + return toObservable(() => + this.edcClient.uiApi.getContractNegotiation({contractNegotiationId}), + ); + } + + getContractAgreementPage( + getContractAgreementPageRequest: GetContractAgreementPageRequest, + ): Observable { + return toObservable(() => + this.edcClient.uiApi.getContractAgreementPage( + getContractAgreementPageRequest, + ), + ); + } + + getContractAgreementById( + contractAgreementId: string, + ): Observable { + return toObservable(() => + this.edcClient.uiApi.getContractAgreementCard({contractAgreementId}), + ); + } + + initiateTransfer( + initiateTransferRequest: InitiateTransferRequest, + ): Observable { + return toObservable(() => + this.edcClient.uiApi.initiateTransfer({initiateTransferRequest}), + ); + } + + initiateCustomTransfer( + initiateCustomTransferRequest: InitiateCustomTransferRequest, + ): Observable { + return toObservable(() => + this.edcClient.uiApi.initiateCustomTransfer({ + initiateCustomTransferRequest, + }), + ); + } + + getTransferHistoryPage(): Observable { + return toObservable(() => this.edcClient.uiApi.getTransferHistoryPage()); + } + + getTransferProcessAsset(transferProcessId: string): Observable { + return toObservable(() => + this.edcClient.uiApi.getTransferProcessAsset({transferProcessId}), + ); + } + + getEnterpriseEditionConnectorLimits(): Observable { + return toObservable(() => + this.edcClient.enterpriseEditionApi.connectorLimits(), + ); + } + + isAssetIdAvailable(assetId: string): Observable { + return toObservable(() => + this.edcClient.uiApi.isAssetIdAvailable({assetId}), + ); + } + + isPolicyIdAvailable(policyId: string): Observable { + return toObservable(() => + this.edcClient.uiApi.isPolicyIdAvailable({policyId}), + ); + } + + isContractDefinitionIdAvailable( + contractDefinitionId: string, + ): Observable { + return toObservable(() => + this.edcClient.uiApi.isContractDefinitionIdAvailable({ + contractDefinitionId, + }), + ); + } +} diff --git a/connector-ui/src/app/core/services/api/fake-backend/connector-fake-impl/asset-fake-service.ts b/connector-ui/src/app/core/services/api/fake-backend/connector-fake-impl/asset-fake-service.ts new file mode 100644 index 000000000..146aed9c8 --- /dev/null +++ b/connector-ui/src/app/core/services/api/fake-backend/connector-fake-impl/asset-fake-service.ts @@ -0,0 +1,138 @@ +import { + AssetPage, + IdAvailabilityResponse, + IdResponseDto, + UiAsset, + UiAssetCreateRequest, + UiAssetEditRequest, + UiDataSource, +} from '@sovity.de/edc-client'; +import {Patcher, patchObj} from '../../../../utils/object-utils'; +import {TestAssets} from './data/test-assets'; + +let assets: UiAsset[] = [ + TestAssets.full, + TestAssets.onRequestAsset, + TestAssets.boring, + TestAssets.short, + TestAssets.assetWithCustomProperties, +]; + +export const assetPage = (): AssetPage => { + return { + assets, + }; +}; + +export const assetIdAvailable = (assetId: string): IdAvailabilityResponse => { + return { + id: assetId, + available: !assets.some((it) => it.assetId === assetId), + }; +}; + +export const getAssetById = (id: string) => + assets.find((it) => it.assetId === id); + +function patchAsset(assetId: string, patcher: Patcher): UiAsset { + assets = assets.map((it) => + it.assetId === assetId ? patchObj(it, patcher) : it, + ); + return getAssetById(assetId)!; +} + +export const createAsset = (asset: UiAssetCreateRequest): IdResponseDto => { + const assetId = asset.id; + assets.push({ + assetId, + ...createAssetMetadata(assetId, asset), + connectorEndpoint: 'https://my-connector/api/dsp', + participantId: 'MDSL1234XX.C1234XX', + isOwnConnector: false, + creatorOrganizationName: 'My Org', + }); + return { + id: asset.id, + lastUpdatedDate: new Date(), + }; +}; + +export const editAsset = ( + assetId: string, + request: UiAssetEditRequest, +): IdResponseDto => { + const asset = patchAsset(assetId, () => + createAssetMetadata(assetId, request), + ); + + return { + id: asset.assetId, + lastUpdatedDate: new Date(), + }; +}; + +function createAssetMetadata( + assetId: string, + request: UiAssetCreateRequest | UiAssetEditRequest, +): Omit< + UiAsset, + | 'assetId' + | 'assetJsonLd' + | 'connectorEndpoint' + | 'isOwnConnector' + | 'creatorOrganizationName' + | 'participantId' +> { + const dataSource: UiDataSource | null = + (request as UiAssetCreateRequest).dataSource ?? + (request as UiAssetEditRequest).dataSourceOverrideOrNull; + return { + title: request.title ?? assetId, + description: request.description, + descriptionShortText: request.description, + publisherHomepage: request.publisherHomepage, + dataSourceAvailability: + dataSource?.type === 'ON_REQUEST' ? 'ON_REQUEST' : 'LIVE', + language: request.language, + onRequestContactEmail: dataSource?.onRequest?.contactEmail, + onRequestContactEmailSubject: + dataSource?.onRequest?.contactPreferredEmailSubject, + licenseUrl: request.licenseUrl, + version: request.version, + keywords: request.keywords, + mediaType: request.mediaType, + landingPageUrl: request.landingPageUrl, + dataCategory: request.dataCategory, + dataSubcategory: request.dataSubcategory, + dataModel: request.dataModel, + geoReferenceMethod: request.geoReferenceMethod, + transportMode: request.transportMode, + sovereignLegalName: request.sovereignLegalName, + geoLocation: request.geoLocation, + nutsLocations: request.nutsLocations, + dataSampleUrls: request.dataSampleUrls, + referenceFileUrls: request.referenceFileUrls, + referenceFilesDescription: request.referenceFilesDescription, + conditionsForUse: request.conditionsForUse, + dataUpdateFrequency: request.dataUpdateFrequency, + temporalCoverageFrom: request.temporalCoverageFrom, + temporalCoverageToInclusive: request.temporalCoverageToInclusive, + httpDatasourceHintsProxyMethod: + dataSource?.httpData?.enableMethodParameterization, + httpDatasourceHintsProxyPath: + dataSource?.httpData?.enablePathParameterization, + httpDatasourceHintsProxyQueryParams: + dataSource?.httpData?.enableQueryParameterization, + httpDatasourceHintsProxyBody: + dataSource?.httpData?.enableBodyParameterization, + customJsonAsString: '{}', + customJsonLdAsString: '{}', + privateCustomJsonAsString: '{}', + privateCustomJsonLdAsString: '{}', + }; +} + +export const deleteAsset = (id: string): IdResponseDto => { + assets = assets.filter((it) => it.assetId !== id); + return {id, lastUpdatedDate: new Date()}; +}; diff --git a/connector-ui/src/app/core/services/api/fake-backend/connector-fake-impl/catalog-fake-service.ts b/connector-ui/src/app/core/services/api/fake-backend/connector-fake-impl/catalog-fake-service.ts new file mode 100644 index 000000000..23976bd55 --- /dev/null +++ b/connector-ui/src/app/core/services/api/fake-backend/connector-fake-impl/catalog-fake-service.ts @@ -0,0 +1,62 @@ +import {UiDataOffer} from '@sovity.de/edc-client'; +import {TestAssets} from './data/test-assets'; +import {TestPolicies} from './data/test-policies'; + +let dataOffers: UiDataOffer[] = [ + { + endpoint: 'http://existing-other-connector/api/dsp', + participantId: 'MDSL1234XX.C1234XX', + asset: TestAssets.full, + contractOffers: [ + { + contractOfferId: + 'dGVzdHRlc3R0ZXN0dGVzdHRlc3Q=:dGVzdHRlc3R0ZXN0dGVzdHRlc3Q=:MDE5MjA4ZWMtMjI0My03YmEyLWE5ZGYtYzRjZTZkZDEyYzAx', + policy: TestPolicies.connectorRestricted, + }, + { + contractOfferId: + 'Zmlyc3QtY2Q=:Zmlyc3QtYXNzZXQtMS4w:MjgzNTZkMTMtN2ZhYy00NTQwLTgwZjItMjI5NzJjOTc1ZWNi', + policy: TestPolicies.warnings, + }, + ], + }, + { + endpoint: 'http://existing-other-connector/api/dsp', + participantId: 'MDSL1234XX.C1234XX', + asset: TestAssets.onRequestAsset, + contractOffers: [ + { + contractOfferId: 'on-request-contract-offer', + policy: TestPolicies.failedMapping, + }, + ], + }, + { + endpoint: 'http://existing-other-connector/api/dsp', + asset: TestAssets.boring, + participantId: 'MDSL1234XX.C1234XX', + contractOffers: [ + { + contractOfferId: 'test-contract-offer-2', + policy: TestPolicies.failedMapping, + }, + ], + }, + { + endpoint: 'http://existing-other-connector/api/dsp', + asset: TestAssets.short, + participantId: 'MDSL1234XX.C1234XX', + contractOffers: [ + { + contractOfferId: 'test-contract-offer-3', + policy: TestPolicies.failedMapping, + }, + ], + }, +]; + +export const getCatalogPageDataOffers = ( + connectorEndpoint: string, +): UiDataOffer[] => { + return dataOffers.filter((it) => it.endpoint === connectorEndpoint); +}; diff --git a/connector-ui/src/app/core/services/api/fake-backend/connector-fake-impl/contract-agreement-fake-service.ts b/connector-ui/src/app/core/services/api/fake-backend/connector-fake-impl/contract-agreement-fake-service.ts new file mode 100644 index 000000000..5fff4fe69 --- /dev/null +++ b/connector-ui/src/app/core/services/api/fake-backend/connector-fake-impl/contract-agreement-fake-service.ts @@ -0,0 +1,244 @@ +import { + ContractAgreementCard, + ContractAgreementPage, + ContractAgreementTransferProcess, + ContractTerminationStatus, + IdResponseDto, + InitiateTransferRequest, +} from '@sovity.de/edc-client'; +import {Patcher, patchObj} from '../../../../utils/object-utils'; +import {TestAssets} from './data/test-assets'; +import {TestPolicies} from './data/test-policies'; + +let contractAgreements: ContractAgreementCard[] = [ + { + contractAgreementId: 'my-own-asset-cd:f52a5d30-6356-4a55-a75a-3c45d7a88c3a', + contractNegotiationId: + 'my-own-asset-neg:f52a5d30-6356-4a55-a75a-3c45d7a88c3e', + direction: 'PROVIDING', + counterPartyAddress: 'http://edc2:11003/api/v1/ids/data', + counterPartyId: 'MDSL1234XX.C1234XX', + contractSigningDate: new Date('2022-03-20T11:18:59.659Z'), + asset: TestAssets.full, + contractPolicy: TestPolicies.connectorRestricted, + terminationInformation: undefined, + terminationStatus: 'ONGOING', + transferProcesses: [ + { + transferProcessId: '2679d234-3340-44bf-a96b-c88b57838033', + lastUpdatedDate: new Date('2023-04-24T12:34:52.896Z'), + state: { + code: 600, + name: 'IN_PROGRESS', + simplifiedState: 'RUNNING', + }, + }, + { + transferProcessId: 'c2863791-c8f3-49e7-8137-7fadaa36b4e4', + lastUpdatedDate: new Date('2023-04-24T12:34:40.801Z'), + state: { + code: 800, + name: 'COMPLETED', + simplifiedState: 'OK', + }, + }, + { + transferProcessId: 'f3ee5129-1909-4d7b-a6fe-a25994d67b56', + lastUpdatedDate: new Date('2023-04-24T12:34:36.735Z'), + state: { + code: 900, + name: 'DEPROVISIONING', + simplifiedState: 'OK', + }, + }, + { + transferProcessId: '2cf2c9be-3b8c-4768-b10e-c1d9f9874e62', + lastUpdatedDate: new Date('2023-04-24T12:34:31.674Z'), + state: { + code: -1, + name: 'ERROR', + simplifiedState: 'ERROR', + }, + errorMessage: 'Something went wrong!', + }, + ], + }, + { + contractAgreementId: + 'my-test-asset-cd:6ebbc301-9b1e-4cd7-9f17-97b5b786753b', + contractNegotiationId: + 'my-test-asset-neg:6ebbc301-9b1e-4cd7-9f17-97b5b7867531', + direction: 'CONSUMING', + counterPartyAddress: 'http://edc2:11003/api/v1/ids/data', + counterPartyId: 'MDSL1234XX.C1234XX', + contractSigningDate: new Date('2022-03-25T14:18:59.659Z'), + asset: TestAssets.toDummyAsset(TestAssets.boring), + contractPolicy: TestPolicies.connectorRestricted, + terminationInformation: undefined, + terminationStatus: 'ONGOING', + transferProcesses: [ + { + transferProcessId: '522138de-349d-4b68-9356-7e5929f053e0', + lastUpdatedDate: new Date('2023-04-24T12:32:43.027Z'), + state: { + code: 800, + name: 'COMPLETED', + simplifiedState: 'OK', + }, + }, + ], + }, + { + contractAgreementId: + 'my-test-asset-2-cd:6ebbc301-9b1e-4cd7-9f17-08b5b786753c', + contractNegotiationId: + 'my-test-asset-2-neg:6ebbc301-9b1e-4cd7-9f17-08b5b7867533', + direction: 'CONSUMING', + counterPartyAddress: 'http://edc2:11003/api/v1/ids/data', + counterPartyId: 'MDSL1234XX.C1234XX', + contractSigningDate: new Date('2022-03-25T11:18:59.659Z'), + asset: TestAssets.toDummyAsset(TestAssets.boring), + contractPolicy: TestPolicies.connectorRestricted, + terminationInformation: undefined, + terminationStatus: 'ONGOING', + transferProcesses: [], + }, + { + contractAgreementId: 'my-test-asset-cd:6ebbc301-9b1e-4cd7-9f17-97b5b78675d', + contractNegotiationId: + 'my-test-asset-neg:6ebbc301-9b1e-4cd7-9f17-97b5b786752', + direction: 'CONSUMING', + counterPartyAddress: 'http://edc2:11003/api/v1/ids/data', + counterPartyId: 'MDSL1234XX.C1234XX', + contractSigningDate: new Date('2022-03-25T11:18:59.659Z'), + asset: TestAssets.toDummyAsset(TestAssets.boring), + contractPolicy: TestPolicies.connectorRestricted, + terminationInformation: undefined, + terminationStatus: 'ONGOING', + transferProcesses: [ + { + transferProcessId: '522138de-349d-4b68-9356-7e5929f053e0', + lastUpdatedDate: new Date('2023-04-24T12:32:43.027Z'), + state: { + code: 800, + name: 'COMPLETED', + simplifiedState: 'OK', + }, + }, + ], + }, + { + contractAgreementId: 'my-test-asset-cd:6ebbc301-9b1e-4cd7-9f17-97b5b78675e', + contractNegotiationId: + 'my-test-asset-neg:6ebbc301-9b1e-4cd7-9f17-97b5b786759', + direction: 'CONSUMING', + counterPartyAddress: 'http://edc2:11003/api/v1/ids/data', + counterPartyId: 'MDSL1234XX.C1234XX', + contractSigningDate: new Date('2022-03-25T11:18:59.659Z'), + asset: TestAssets.toDummyAsset(TestAssets.full), + contractPolicy: TestPolicies.connectorRestricted, + terminationInformation: { + terminatedAt: new Date('2024-07-16T08:25:12.031Z'), + reason: 'Creative termination reason', + detail: 'Creative termination details', + terminatedBy: 'COUNTERPARTY', + }, + terminationStatus: 'TERMINATED', + transferProcesses: [ + { + transferProcessId: '522138de-349d-4b68-9356-7e5929f053e0', + lastUpdatedDate: new Date('2023-04-24T12:32:43.027Z'), + state: { + code: 800, + name: 'COMPLETED', + simplifiedState: 'OK', + }, + }, + ], + }, + { + contractAgreementId: + 'my-test-asset-cd:6ebbc301-9b1e-4cd7-9f17-97b5b786758f', + contractNegotiationId: + 'my-test-asset-neg:6ebbc301-9b1e-4cd7-9f17-97b5b786758', + direction: 'PROVIDING', + counterPartyAddress: 'http://edc2:11003/api/v1/ids/data', + counterPartyId: 'MDSL1234XX.C1234XX', + contractSigningDate: new Date('2022-03-25T11:18:59.659Z'), + asset: TestAssets.toDummyAsset(TestAssets.boring), + contractPolicy: TestPolicies.connectorRestricted, + terminationInformation: { + terminatedAt: new Date('2024-07-16T08:25:12.031Z'), + reason: 'Creative termination reason', + detail: + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed tristique facilisis ornare. Maecenas at facilisis dui, vel dapibus nisi. Nam dapibus, sapien ac iaculis sodales, tellus ante dictum libero, eu viverra metus lorem nec lectus. Donec cursus feugiat massa sed pharetra. Etiam nec lacus nisi. Etiam ut justo bibendum felis tincidunt tempor vel et sem. Suspendisse potenti. Nam volutpat ornare mi, at fringilla sapien accumsan congue. Maecenas ornare rutrum ipsum, quis fermentum risus. Proin vitae tortor nec metus tristique posuere. Cras ornare lobortis diam. Proin pellentesque, massa eu bibendum posuere, augue nibh porta libero, eu rhoncus ex enim vel nulla. Fusce eget dui non velit rutrum euismod.\n' + + '\n' + + 'Mauris finibus vel lectus eu aliquam. Proin et leo sit amet turpis venenatis faucibus. Fusce nisl quam, malesuada sit amet feugiat at, vehicula id mauris. Phasellus aliquam libero quis lobortis viverra. Vivamus luctus purus et nibh pellentesque, eget tristique ipsum pretium. Nulla rhoncus lacus sed lectus elementum vulputate. Nunc massa mauris, viverra vitae magna nec, mollis molestie tellus. Donec accumsan massa sit amet ultricies mollis. Mauris dui nunc, eleifend vel risus vitae, convallis bibendum dolor. Aliquam felis quam, rhoncus non gravida a, bibendum feugiat nunc. Sed varius dictum nisi, id lacinia enim condimentum.', + terminatedBy: 'SELF', + }, + terminationStatus: 'TERMINATED', + transferProcesses: [ + { + transferProcessId: '522138de-349d-4b68-9356-7e5929f053e0', + lastUpdatedDate: new Date('2023-04-24T12:32:43.027Z'), + state: { + code: 800, + name: 'COMPLETED', + simplifiedState: 'OK', + }, + }, + ], + }, +]; +export const contractAgreementPage = ( + terminationStatus?: ContractTerminationStatus, +): ContractAgreementPage => { + return { + contractAgreements: terminationStatus + ? contractAgreements.filter( + (x) => x.terminationStatus === terminationStatus, + ) + : contractAgreements, + }; +}; + +export const addContractAgreement = ( + contractAgreement: ContractAgreementCard, +) => { + contractAgreements = [contractAgreement, ...contractAgreements]; +}; + +export const contractAgreementInitiateTransfer = ( + request: InitiateTransferRequest, +): IdResponseDto => { + const contractAgreementId = request?.contractAgreementId ?? ''; + const transferProcessId = + 'transfer-process-' + Math.random().toString().substring(2); + + const newTransferProcess: ContractAgreementTransferProcess = { + transferProcessId, + state: { + code: 800, + name: 'COMPLETED', + simplifiedState: 'OK', + }, + lastUpdatedDate: new Date(), + }; + + updateAgreement(contractAgreementId, (agremeent) => ({ + transferProcesses: [newTransferProcess, ...agremeent.transferProcesses], + })); + + return {id: transferProcessId, lastUpdatedDate: new Date(new Date())}; +}; + +export const updateAgreement = ( + contractAgreementId: string, + patcher: Patcher, +) => { + contractAgreements = contractAgreements.map((agreement) => + agreement.contractAgreementId === contractAgreementId + ? patchObj(agreement, patcher) + : agreement, + ); +}; diff --git a/connector-ui/src/app/core/services/api/fake-backend/connector-fake-impl/contract-definition-fake-service.ts b/connector-ui/src/app/core/services/api/fake-backend/connector-fake-impl/contract-definition-fake-service.ts new file mode 100644 index 000000000..2fb59c3ce --- /dev/null +++ b/connector-ui/src/app/core/services/api/fake-backend/connector-fake-impl/contract-definition-fake-service.ts @@ -0,0 +1,65 @@ +import { + ContractDefinitionEntry, + ContractDefinitionPage, + ContractDefinitionRequest, + IdAvailabilityResponse, + IdResponseDto, +} from '@sovity.de/edc-client'; +import {AssetProperty} from '../../../models/asset-properties'; + +let contractDefinitions: ContractDefinitionEntry[] = [ + { + contractDefinitionId: 'test-data-offer-1', + contractPolicyId: 'test-policy-definition-1', + accessPolicyId: 'test-policy-definition-1', + assetSelector: [ + { + operandLeft: AssetProperty.id, + operator: 'EQ', + operandRight: {type: 'VALUE', value: 'test-asset-1'}, + }, + ], + }, +]; + +export const contractDefinitionPage = (): ContractDefinitionPage => { + return { + contractDefinitions, + }; +}; + +export const contractDefinitionIdAvailable = ( + contractDefinitionId: string, +): IdAvailabilityResponse => { + return { + id: contractDefinitionId, + available: !contractDefinitions.some( + (it) => it.contractDefinitionId === contractDefinitionId, + ), + }; +}; + +export const createContractDefinition = ( + request: ContractDefinitionRequest, +): IdResponseDto => { + let newEntry: ContractDefinitionEntry = { + contractDefinitionId: request.contractDefinitionId!, + contractPolicyId: request.contractPolicyId!, + accessPolicyId: request.accessPolicyId!, + assetSelector: request.assetSelector!, + }; + + contractDefinitions = [newEntry, ...contractDefinitions]; + + return { + id: newEntry.contractDefinitionId, + lastUpdatedDate: new Date(), + }; +}; + +export const deleteContractDefinition = (id: string): IdResponseDto => { + contractDefinitions = contractDefinitions.filter( + (it) => it.contractDefinitionId !== id, + ); + return {id, lastUpdatedDate: new Date()}; +}; diff --git a/connector-ui/src/app/core/services/api/fake-backend/connector-fake-impl/contract-negotiation-fake-service.ts b/connector-ui/src/app/core/services/api/fake-backend/connector-fake-impl/contract-negotiation-fake-service.ts new file mode 100644 index 000000000..8a38398cd --- /dev/null +++ b/connector-ui/src/app/core/services/api/fake-backend/connector-fake-impl/contract-negotiation-fake-service.ts @@ -0,0 +1,88 @@ +import { + ContractNegotiationRequest, + ContractNegotiationSimplifiedState, + ContractNegotiationState, + UiContractNegotiation, +} from '@sovity.de/edc-client'; +import {Patcher, patchObj} from '../../../../utils/object-utils'; +import {getAssetById} from './asset-fake-service'; +import {addContractAgreement} from './contract-agreement-fake-service'; +import {getPolicyDefinitionByJsonLd} from './policy-definition-fake-service'; + +const initiated: ContractNegotiationState = { + name: 'INITIATED', + code: 500, + simplifiedState: ContractNegotiationSimplifiedState.InProgress, +}; + +const agreed: ContractNegotiationState = { + name: 'AGREED', + code: 1000, + simplifiedState: ContractNegotiationSimplifiedState.Agreed, +}; + +let negotiations: UiContractNegotiation[] = [ + { + contractNegotiationId: 'test-contract-negotiation-1', + createdAt: new Date(), + contractAgreementId: 'test-contract-agreement-1', + state: initiated, + }, + { + contractNegotiationId: 'test-contract-negotiation-2', + createdAt: new Date(), + contractAgreementId: 'test-contract-agreement-2', + state: agreed, + }, +]; + +export const initiateContractNegotiation = ( + request: ContractNegotiationRequest, +): UiContractNegotiation => { + let contractNegotiationId = + 'dummy-negotiation-' + Math.random().toString().substring(2); + let negotiation: UiContractNegotiation = { + contractNegotiationId, + state: initiated, + createdAt: new Date(), + }; + negotiations = [...negotiations, negotiation]; + + setTimeout(() => { + let contractAgreementId = + 'dummy-agreement' + Math.random().toString().substring(2); + + updateNegotiation(contractNegotiationId, () => ({ + state: agreed, + contractAgreementId, + })); + + addContractAgreement({ + contractNegotiationId, + contractAgreementId, + direction: 'CONSUMING', + counterPartyAddress: request.counterPartyAddress, + transferProcesses: [], + counterPartyId: request.counterPartyParticipantId, + asset: getAssetById(request.assetId)!, + contractSigningDate: new Date(), + contractPolicy: getPolicyDefinitionByJsonLd(request.policyJsonLd)!, + terminationInformation: undefined, + terminationStatus: 'ONGOING', + }); + }, 4000); + return negotiation; +}; + +export const getContractNegotiation = (id: string): UiContractNegotiation => { + return negotiations.find((it) => it.contractNegotiationId === id)!; +}; + +const updateNegotiation = ( + id: string, + patcher: Patcher, +) => { + negotiations = negotiations.map((it) => + it.contractNegotiationId === id ? patchObj(it, patcher) : it, + ); +}; diff --git a/connector-ui/src/app/core/services/api/fake-backend/connector-fake-impl/contract-termination-fake-service.ts b/connector-ui/src/app/core/services/api/fake-backend/connector-fake-impl/contract-termination-fake-service.ts new file mode 100644 index 000000000..c2a061454 --- /dev/null +++ b/connector-ui/src/app/core/services/api/fake-backend/connector-fake-impl/contract-termination-fake-service.ts @@ -0,0 +1,29 @@ +import { + ContractAgreementCard, + IdResponseDto, + TerminateContractAgreementRequest, +} from '@sovity.de/edc-client'; +import {updateAgreement} from './contract-agreement-fake-service'; + +export const initiateContractTermination = ( + request: TerminateContractAgreementRequest, +): IdResponseDto => { + let response: IdResponseDto = { + id: request.contractAgreementId, + lastUpdatedDate: new Date(), + }; + + updateAgreement( + request.contractAgreementId, + (agremeent: ContractAgreementCard) => ({ + terminationStatus: 'TERMINATED', + terminationInformation: { + terminatedAt: new Date(), + terminatedBy: 'SELF', + reason: request.contractTerminationRequest?.reason ?? '', + detail: request.contractTerminationRequest?.detail ?? '', + }, + }), + ); + return response; +}; diff --git a/connector-ui/src/app/core/services/api/fake-backend/connector-fake-impl/dashboard-fake-service.ts b/connector-ui/src/app/core/services/api/fake-backend/connector-fake-impl/dashboard-fake-service.ts new file mode 100644 index 000000000..a853b1f61 --- /dev/null +++ b/connector-ui/src/app/core/services/api/fake-backend/connector-fake-impl/dashboard-fake-service.ts @@ -0,0 +1,38 @@ +import {DashboardPage} from '@sovity.de/edc-client'; + +export const dashboardPage = (): DashboardPage => ({ + numAssets: 4, + numContractAgreementsConsuming: 10, + numContractAgreementsProviding: 123, + numPolicies: 4, + numContractDefinitions: 4, + transferProcessesConsuming: { + numTotal: 8, + numOk: 5, + numRunning: 1, + numError: 2, + }, + transferProcessesProviding: { + numTotal: 2, + numOk: 2, + numError: 0, + numRunning: 0, + }, + connectorParticipantId: 'MDSL1234XX.C1234XX', + connectorTitle: 'My Connector', + connectorDescription: 'Example Connector with Fake Backend', + connectorMaintainerName: 'sovity GmbH', + connectorMaintainerUrl: 'https://sovity.de', + connectorCuratorName: 'Example GmbH', + connectorCuratorUrl: 'https://example.com', + connectorEndpoint: 'https://edc.fake-backend/api/dsp', + connectorMiwConfig: { + url: 'https://miw.fake-backend', + tokenUrl: 'https://miw.fake-backend/token', + authorityId: 'fake-miw', + }, + connectorDapsConfig: { + tokenUrl: 'https://daps.fake-backend/token', + jwksUrl: 'https://daps.fake-backend/jwks.json', + }, +}); diff --git a/connector-ui/src/app/core/services/api/fake-backend/connector-fake-impl/data-offer-fake-service.ts b/connector-ui/src/app/core/services/api/fake-backend/connector-fake-impl/data-offer-fake-service.ts new file mode 100644 index 000000000..5369d59a9 --- /dev/null +++ b/connector-ui/src/app/core/services/api/fake-backend/connector-fake-impl/data-offer-fake-service.ts @@ -0,0 +1,84 @@ +import { + DataOfferCreationRequest, + DataOfferCreationRequestPolicyEnum, + IdResponseDto, + UiCriterionLiteralType, +} from '@sovity.de/edc-client'; +import {ALWAYS_TRUE_POLICY_ID} from '../../../../../shared/business/edit-asset-form/form/model/always-true-policy-id'; +import {assetIdAvailable, createAsset} from './asset-fake-service'; +import { + contractDefinitionIdAvailable, + createContractDefinition, +} from './contract-definition-fake-service'; +import { + createPolicyDefinitionV2, + policyDefinitionIdAvailable, +} from './policy-definition-fake-service'; + +const checkIdAvailability = (id: string): void => { + if ( + !policyDefinitionIdAvailable(id).available || + !assetIdAvailable(id).available || + !contractDefinitionIdAvailable(id).available + ) { + throw new Error('Id already exists'); + } +}; + +const checkIfNoAlwaysTruePolicyExists = (): void => { + if (policyDefinitionIdAvailable(ALWAYS_TRUE_POLICY_ID).available) { + createPolicyDefinitionV2({ + policyDefinitionId: ALWAYS_TRUE_POLICY_ID, + expression: { + type: 'EMPTY', + }, + }); + } +}; + +export const createDataOffer = ( + request: DataOfferCreationRequest, +): IdResponseDto => { + const commonId = request.uiAssetCreateRequest.id; + let accessPolicyId = null; + let contractPolicyId = null; + + checkIfNoAlwaysTruePolicyExists(); + checkIdAvailability(commonId); + createAsset(request.uiAssetCreateRequest); + + switch (request.policy) { + case DataOfferCreationRequestPolicyEnum.DontPublish: + return {id: commonId, lastUpdatedDate: new Date()}; + case DataOfferCreationRequestPolicyEnum.PublishRestricted: + createPolicyDefinitionV2({ + policyDefinitionId: commonId, + expression: request.uiPolicyExpression!, + }); + accessPolicyId = commonId; + contractPolicyId = commonId; + break; + case DataOfferCreationRequestPolicyEnum.PublishUnrestricted: + accessPolicyId = ALWAYS_TRUE_POLICY_ID; + contractPolicyId = ALWAYS_TRUE_POLICY_ID; + break; + } + + createContractDefinition({ + contractDefinitionId: commonId, + accessPolicyId, + contractPolicyId, + assetSelector: [ + { + operandLeft: commonId, + operator: 'EQ', + operandRight: { + type: UiCriterionLiteralType.Value, + value: commonId, + }, + }, + ], + }); + + return {id: commonId, lastUpdatedDate: new Date()}; +}; diff --git a/connector-ui/src/app/core/services/api/fake-backend/connector-fake-impl/data/test-assets.ts b/connector-ui/src/app/core/services/api/fake-backend/connector-fake-impl/data/test-assets.ts new file mode 100644 index 000000000..499102363 --- /dev/null +++ b/connector-ui/src/app/core/services/api/fake-backend/connector-fake-impl/data/test-assets.ts @@ -0,0 +1,227 @@ +import {UiAsset} from '@sovity.de/edc-client'; + +export namespace TestAssets { + const markdownDescription = `Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod + tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At + vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, + no sea takimata sanctus est Lorem ipsum dolor sit amet. + + ![scenery2](https://images.pexels.com/photos/255419/pexels-photo-255419.jpeg?cs=srgb&dl=pexels-pixabay-255419.jpg&fm=jpg) + + Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod + tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At + vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, + + Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod + tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At + vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, + + Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod + tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At + vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, + + Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod + tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At + vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, + + ![scenery](https://images.rawpixel.com/image_800/cHJpdmF0ZS9sci9pbWFnZXMvd2Vic2l0ZS8yMDIyLTA1L3NrOTc5MS1pbWFnZS1rd3Z1amE5Ni5qcGc.jpg) Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, + + + + + # Omen + + This is **bold!** This is _italic_. This is inline \`code\`. + + > here we quote + + Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod + tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At + vero eos et accusam et justo duo dolores et ea rebum. + + ## Sage + + - list item 1 + - list item 2 + + ### Raze + + 1. list item 1 + 2. list item 2 + + #### Cypher + + \`\`\`javascript + alert(1); + \`\`\` + + ##### Jett + + [Sovity Website](https://sovity.de/) + + **Table** +| Item | In Stock | Price | Description | +| :---------------- | :------: | ----: | :---------- | +| Python Hat | True | 24.99 | This is a long description to test the scrolling behavior of the table. This is a long description to test the scrolling behavior of the table. | +| SQL Hat | True | 24.99 | This is a long description to test the scrolling behavior of the table. This is a long description to test the scrolling behavior of the table. | +| Codecademy Tee | False | 20.99 | This is a long description to test the scrolling behavior of the table. This is a long description to test the scrolling behavior of the table. | +| Codecademy Hoodie | False | 43.99 | This is a long description to test the scrolling behavior of the table. This is a long description to test the scrolling behavior of the table. | +`; + + const shortMarkdownDescription = `# Short Description + +This is a short description text that should be fully rendered without being **collapsed**. No *show more* button should be visible. +`; + + export const assetWithCustomProperties: UiAsset = { + dataSourceAvailability: 'LIVE', + assetId: 'asset-with-custom-properties', + title: 'Asset with Custom Properties', + description: 'Asset with Custom Properties', + descriptionShortText: 'Asset with Custom Properties', + connectorEndpoint: 'https://my-connector/api/dsp', + participantId: 'MDSL1234XX.C1234XX', + creatorOrganizationName: 'My Org', + isOwnConnector: true, + customJsonLdAsString: '{"http://custom.json.test/LD":"https://google.com"}', + customJsonAsString: '{"testKey":"testValue"}', + privateCustomJsonLdAsString: + '{"http://custom.json.test/LD/private":"https://google.com/private"}', + privateCustomJsonAsString: '{"privateTestKey":"testValue"}', + }; + + export const onRequestAsset: UiAsset = { + dataSourceAvailability: 'ON_REQUEST', + assetId: 'part-names-july-2024', + title: 'Part Names July 2024', + description: 'Example "On Request" data Offer', + descriptionShortText: 'Example "On Request" data Offer', + connectorEndpoint: 'https://my-other-connector/api/dsp', + participantId: 'MDSL1234XX.C1234XX', + creatorOrganizationName: 'my-org', + temporalCoverageFrom: new Date('2024-01-01'), + onRequestContactEmail: 'contact@my-org.mail', + onRequestContactEmailSubject: "Data Offer 'Part Names July 2024'", + version: 'July 2024', + keywords: ['automotive', 'partnumber', 'part names'], + isOwnConnector: false, + }; + + export const boring: UiAsset = { + dataSourceAvailability: 'LIVE', + assetId: 'data-sample-ckd-skd-demands-2023-Jan', + title: 'data-sample-ckd-skd-demands-2023-Jan', + description: '', + descriptionShortText: '', + connectorEndpoint: 'https://my-other-connector/api/dsp', + participantId: 'MDSL1234XX.C1234XX', + creatorOrganizationName: 'my-other-connector', + temporalCoverageFrom: new Date('2024-01-01'), + isOwnConnector: true, + }; + + export const short: UiAsset = { + dataSourceAvailability: 'LIVE', + assetId: 'data-sample-ckd-skd-demands-2023-Feb', + title: 'data-sample-ckd-skd-demands-2023-Feb', + connectorEndpoint: 'https://my-other-connector/api/dsp', + participantId: 'MDSL1234XX.C1235XX', + creatorOrganizationName: 'my-other-connector', + description: shortMarkdownDescription, + descriptionShortText: + 'Short Description This is a short description text that should be fully rendered without being collapsed. No show more button should be visible.', + isOwnConnector: false, + }; + + export const full: UiAsset = { + dataSourceAvailability: 'LIVE', + assetId: 'ckd-skd-demands-2023-Jan', + title: 'CKD / SKD Demands January 2023', + connectorEndpoint: 'https://my-other-connector/api/dsp', + participantId: 'MDSL1234XX.C1236XX', + version: '2023-A-Program', + creatorOrganizationName: 'My-German-OEM', + keywords: ['automotive', 'part-demands', '2023', 'January'], + mediaType: 'application/json', + description: markdownDescription, + descriptionShortText: + 'Part demands for CKD/SKD parts January 2023 Split by plant / day / model code. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum.', + isOwnConnector: true, + language: 'https://w3id.org/idsa/code/EN', + publisherHomepage: + 'https://teamabc.departmentxyz.my-german-oem.de/offers/ckd-skd-demands', + licenseUrl: + 'https://teamabc.departmentxyz.my-german-oem.de/offers/ckd-skd-demands#license', + landingPageUrl: + 'https://teamabc.departmentxyz.my-german-oem.de/offers/ckd-skd-demands#documentation', + dataCategory: 'Infrastructure and Logistics', + dataSubcategory: 'General Information About Planning Of Routes', + dataModel: 'unspecified', + geoReferenceMethod: 'Lat/Lon', + transportMode: 'Rail', + sovereignLegalName: 'Data Owning Company GmbH', + geoLocation: '40.741895,-73.989308', + nutsLocations: ['DE', 'DE9', 'DE92', 'DE929'], + dataSampleUrls: [ + 'https://teamabc.departmentxyz.sample/a', + 'https://teamabc.departmentxyz.sample/b', + 'https://teamabc.departmentxyz.sample/c', + 'https://teamabc.departmentxyz.sample/d', + 'https://teamabc.departmentxyz.sample/e', + ], + referenceFileUrls: [ + 'https://teamabc.departmentxyz.reference/a', + 'https://teamabc.departmentxyz.reference/b', + 'https://teamabc.departmentxyz.reference/c', + 'https://teamabc.departmentxyz.reference/d', + 'https://teamabc.departmentxyz.reference/e', + 'https://teamabc.departmentxyz.reference/f', + 'https://teamabc.departmentxyz.reference/g', + 'https://teamabc.departmentxyz.reference/h', + 'https://teamabc.departmentxyz.reference/i', + 'https://teamabc.departmentxyz.reference/j', + 'https://teamabc.departmentxyz.reference/k', + 'https://teamabc.departmentxyz.reference/l', + 'https://teamabc.departmentxyz.reference/m', + 'https://teamabc.departmentxyz.reference/n', + 'https://teamabc.departmentxyz.reference/o', + 'https://teamabc.departmentxyz.reference/p', + 'https://teamabc.departmentxyz.reference/q', + 'https://teamabc.departmentxyz.reference/r', + 'https://teamabc.departmentxyz.reference/s', + ], + referenceFilesDescription: 'This reference file is important', + conditionsForUse: + 'If you use the dataset please cite it in your work and give attribution', + dataUpdateFrequency: 'every month', + temporalCoverageFrom: new Date('2024-01-01'), + temporalCoverageToInclusive: new Date('2024-01-24'), + httpDatasourceHintsProxyQueryParams: true, + httpDatasourceHintsProxyPath: true, + httpDatasourceHintsProxyMethod: true, + httpDatasourceHintsProxyBody: true, + customJsonAsString: '{"http://unknown/usecase": "my-use-case"}', + privateCustomJsonAsString: + '{ "http://unknown/internal-id": "my-internal-id-123"}', + }; + + export function toDummyAsset(entry: UiAsset): UiAsset { + return { + assetId: entry.assetId, + title: entry.assetId, + participantId: entry.participantId, + connectorEndpoint: entry.connectorEndpoint, + creatorOrganizationName: entry.participantId, + isOwnConnector: entry.isOwnConnector, + dataSourceAvailability: 'LIVE', + }; + } + + export function withSuffix(asset: UiAsset, suffix: string): UiAsset { + return { + ...asset, + assetId: `${asset.assetId}-${suffix}`, + title: `${asset.title} ${suffix}`, + }; + } +} diff --git a/connector-ui/src/app/core/services/api/fake-backend/connector-fake-impl/data/test-policies.ts b/connector-ui/src/app/core/services/api/fake-backend/connector-fake-impl/data/test-policies.ts new file mode 100644 index 000000000..3613cdf14 --- /dev/null +++ b/connector-ui/src/app/core/services/api/fake-backend/connector-fake-impl/data/test-policies.ts @@ -0,0 +1,56 @@ +import {UiPolicy, UiPolicyExpression} from '@sovity.de/edc-client'; +import {policyLeftExpressions} from '../../../../../../shared/business/policy-editor/model/policy-left-expressions'; +import { + constraint, + constraintList, + multi, +} from '../../../../../../shared/business/policy-editor/model/ui-policy-expression-utils'; + +export namespace TestPolicies { + const policy = ( + expression: UiPolicyExpression, + errors: string[] = [], + ): UiPolicy => ({ + policyJsonLd: JSON.stringify({ + _description: + 'The actual JSON-LD will look different. This is just data from the fake backend.', + expression, + }), + expression, + errors, + }); + + export const connectorRestricted: UiPolicy = policy( + multi( + 'AND', + constraint( + policyLeftExpressions.policyEvaluationTime, + 'GEQ', + '2020-11-30T23:00:00.000Z', + ), + constraint( + policyLeftExpressions.policyEvaluationTime, + 'LT', + '2020-12-07T23:00:00.000Z', + ), + multi( + 'OR', + constraint('REFERRING_CONNECTOR', 'EQ', 'MDSL1234XX.C1234XX'), + constraint('REFERRING_CONNECTOR', 'EQ', 'MDSL1234XX.C1235YY'), + ), + constraint('ALWAYS_TRUE', 'EQ', 'true'), + ), + ); + + export const warnings: UiPolicy = policy( + constraintList('SOME_UNKNOWN_PROP', 'HAS_PART', ['A', 'B', 'C']), + ['$.duties: Duties are currently unsupported.'], + ); + export const failedMapping: UiPolicy = policy({ + type: 'EMPTY', + }); + + export const unrestricted: UiPolicy = policy({ + type: 'EMPTY', + }); +} diff --git a/connector-ui/src/app/core/services/api/fake-backend/connector-fake-impl/ee-fake-service.ts b/connector-ui/src/app/core/services/api/fake-backend/connector-fake-impl/ee-fake-service.ts new file mode 100644 index 000000000..c96dd82d8 --- /dev/null +++ b/connector-ui/src/app/core/services/api/fake-backend/connector-fake-impl/ee-fake-service.ts @@ -0,0 +1,11 @@ +import {ConnectorLimits} from '@sovity.de/edc-client'; +import {contractAgreementPage} from './contract-agreement-fake-service'; + +export const connectorLimits = (): ConnectorLimits => ({ + numActiveConsumingContractAgreements: + contractAgreementPage().contractAgreements.filter( + (it) => + it.direction === 'CONSUMING' && it.terminationStatus === 'ONGOING', + ).length, + maxActiveConsumingContractAgreements: 1, +}); diff --git a/connector-ui/src/app/core/services/api/fake-backend/connector-fake-impl/policy-definition-fake-service.ts b/connector-ui/src/app/core/services/api/fake-backend/connector-fake-impl/policy-definition-fake-service.ts new file mode 100644 index 000000000..18e1a1f80 --- /dev/null +++ b/connector-ui/src/app/core/services/api/fake-backend/connector-fake-impl/policy-definition-fake-service.ts @@ -0,0 +1,90 @@ +import { + IdAvailabilityResponse, + IdResponseDto, + PolicyDefinitionCreateDto, + PolicyDefinitionCreateRequest, + PolicyDefinitionDto, + PolicyDefinitionPage, + UiPolicyExpression, +} from '@sovity.de/edc-client'; +import {ALWAYS_TRUE_POLICY_ID} from '../../../../../shared/business/edit-asset-form/form/model/always-true-policy-id'; +import {TestPolicies} from './data/test-policies'; + +let policyDefinitions: PolicyDefinitionDto[] = [ + { + policyDefinitionId: 'test-policy-definition-1', + policy: TestPolicies.connectorRestricted, + }, + { + policyDefinitionId: 'test-policy-definition-2', + policy: TestPolicies.warnings, + }, + { + policyDefinitionId: 'test-policy-definition-3', + policy: TestPolicies.failedMapping, + }, + { + policyDefinitionId: ALWAYS_TRUE_POLICY_ID, + policy: TestPolicies.unrestricted, + }, +]; +export const policyDefinitionPage = (): PolicyDefinitionPage => { + return {policies: policyDefinitions}; +}; + +export const policyDefinitionIdAvailable = ( + policyDefinitionId: string, +): IdAvailabilityResponse => { + return { + id: policyDefinitionId, + available: !policyDefinitions.some( + (it) => it.policyDefinitionId === policyDefinitionId, + ), + }; +}; + +export const getPolicyDefinitionByJsonLd = (jsonLd: string) => + policyDefinitions.find((it) => it.policy.policyJsonLd === jsonLd)?.policy; + +export const createPolicyDefinition = ( + request: PolicyDefinitionCreateRequest, +): IdResponseDto => { + const expression: UiPolicyExpression = { + type: 'AND', + expressions: (request.policy.constraints ?? []).map((it) => ({ + type: 'CONSTRAINT', + constraint: it, + })), + }; + + return createPolicyDefinitionV2({ + policyDefinitionId: request.policyDefinitionId, + expression, + }); +}; + +export const createPolicyDefinitionV2 = ( + request: PolicyDefinitionCreateDto, +): IdResponseDto => { + const newPolicyDefinition: PolicyDefinitionDto = { + policyDefinitionId: request.policyDefinitionId, + policy: { + expression: request.expression, + errors: [], + policyJsonLd: '{"example-policy-jsonld": true}', + }, + }; + policyDefinitions = [newPolicyDefinition, ...policyDefinitions]; + + return { + id: request.policyDefinitionId, + lastUpdatedDate: new Date(), + }; +}; + +export const deletePolicyDefinition = (id: string): IdResponseDto => { + policyDefinitions = policyDefinitions.filter( + (it) => it.policyDefinitionId !== id, + ); + return {id, lastUpdatedDate: new Date()}; +}; diff --git a/connector-ui/src/app/core/services/api/fake-backend/connector-fake-impl/transfer-history-fake-service.ts b/connector-ui/src/app/core/services/api/fake-backend/connector-fake-impl/transfer-history-fake-service.ts new file mode 100644 index 000000000..708af6938 --- /dev/null +++ b/connector-ui/src/app/core/services/api/fake-backend/connector-fake-impl/transfer-history-fake-service.ts @@ -0,0 +1,106 @@ +import { + TransferHistoryEntry, + TransferHistoryPage, + UiAsset, +} from '@sovity.de/edc-client'; +import {getAssetById} from './asset-fake-service'; +import {TestAssets} from './data/test-assets'; + +let transferHistoryEntries: TransferHistoryEntry[] = [ + { + transferProcessId: '339b2a27-3b66-49f5-8b43-6a400d5914b5', + createdDate: new Date('2023-03-20T11:18:59.659Z'), + lastUpdatedDate: new Date('2023-07-25T11:18:59.659Z'), + state: { + code: 800, + name: 'COMPLETED', + simplifiedState: 'OK', + }, + contractAgreementId: 'test-asset-1-cd:f52a5d30-6356-4a55-a75a-3c45d7a88c3e', + direction: 'CONSUMING', + counterPartyConnectorEndpoint: 'https://sovity-demo4-mds/api/v1/ids/data', + counterPartyParticipantId: 'MDSL1234XX.C1234XX', + assetName: 'test-asset-1', + assetId: 'test-asset-1', + }, + { + transferProcessId: '1317d0da-cdc6-42ab-b54b-1f90bcfed508', + createdDate: new Date('2023-01-20T11:18:59.659Z'), + lastUpdatedDate: new Date('2023-03-25T11:18:59.659Z'), + state: { + code: -1, + name: 'ERROR', + simplifiedState: 'ERROR', + }, + contractAgreementId: 'test-asset-2-cd:5816a60b-86c1-489a-b26a-ed129947f973', + counterPartyParticipantId: 'MDSL1234XX.C1234XX', + direction: 'CONSUMING', + counterPartyConnectorEndpoint: 'http://edc2:11003/api/v1/ids/data', + assetName: 'test-asset-2', + assetId: 'test-asset-2', + errorMessage: + 'TransferProcessManager: attempt #8 failed to send transfer. Retry limit exceeded, TransferProcess 1317d0da-cdc6-42ab-b54b-1f90bcfed508 moves to ERROR state. Cause: java.net.SocketException: Connection reset', + }, + { + transferProcessId: '81cdf4cf-8427-480f-9662-8a29d66ddd3b', + createdDate: new Date('2022-03-25T11:18:59.659Z'), + lastUpdatedDate: new Date('2022-11-20T11:18:59.659Z'), + state: { + code: 800, + name: 'COMPLETED', + simplifiedState: 'OK', + }, + contractAgreementId: 'test-asset-3-cd:6ebbc301-9b1e-4cd7-9f17-97b5b7867531', + direction: 'CONSUMING', + counterPartyConnectorEndpoint: 'https://sovity-demo2-edc/api/v1/ids/data', + counterPartyParticipantId: 'MDSL1234XX.C1234XX', + assetName: 'test-asset-3', + assetId: 'test-asset-3', + }, + { + transferProcessId: '47240a35-d8fc-41d9-b020-07b87f3cc7b6', + createdDate: new Date('2022-01-29T11:18:59.659Z'), + lastUpdatedDate: new Date('2022-02-24T11:18:59.659Z'), + state: { + code: 600, + name: 'IN_PROGRESS', + simplifiedState: 'RUNNING', + }, + contractAgreementId: 'test-asset-4-cd:f52a5d30-6356-4a55-a75a-3c45d7a88c3e', + direction: 'PROVIDING', + counterPartyConnectorEndpoint: 'https://sovity-demo2-edc/api/v1/ids/data', + counterPartyParticipantId: 'MDSL1234XX.C1234XX', + assetName: TestAssets.full.title, + assetId: TestAssets.full.assetId, + }, +]; + +export const transferHistoryPage = (): TransferHistoryPage => { + return { + transferEntries: transferHistoryEntries, + }; +}; + +export const transferProcessAsset = (transferProcessId: string): UiAsset => { + const transfer = transferHistoryEntries.find( + (it) => it.transferProcessId === transferProcessId, + ); + const assetId = transfer?.assetId ?? 'unknown'; + const isProviding = transfer?.direction === 'PROVIDING'; + + const dummyAsset: UiAsset = { + assetId, + dataSourceAvailability: 'LIVE', + title: assetId, + participantId: 'unknown', + connectorEndpoint: 'https://unknown/api/dsp', + isOwnConnector: false, + creatorOrganizationName: 'unknown', + }; + + const assetEntry = getAssetById(assetId); + + return isProviding && assetEntry + ? TestAssets.toDummyAsset(assetEntry) + : dummyAsset; +}; diff --git a/connector-ui/src/app/core/services/api/fake-backend/edc-fake-backend.ts b/connector-ui/src/app/core/services/api/fake-backend/edc-fake-backend.ts new file mode 100644 index 000000000..57a129c7c --- /dev/null +++ b/connector-ui/src/app/core/services/api/fake-backend/edc-fake-backend.ts @@ -0,0 +1,266 @@ +import { + AssetPageToJSON, + ConnectorLimitsToJSON, + ContractAgreementPageQueryFromJSON, + ContractAgreementPageToJSON, + ContractDefinitionPageToJSON, + ContractDefinitionRequestFromJSON, + ContractNegotiationRequestFromJSON, + ContractTerminationRequestFromJSON, + DashboardPageToJSON, + DataOfferCreationRequestFromJSON, + FetchAPI, + IdAvailabilityResponseToJSON, + IdResponseDtoToJSON, + InitiateTransferRequestFromJSON, + PolicyDefinitionCreateDtoFromJSON, + PolicyDefinitionCreateRequestFromJSON, + PolicyDefinitionPageToJSON, + TransferHistoryPageToJSON, + UiAssetCreateRequestFromJSON, + UiAssetEditRequestFromJSON, + UiAssetToJSON, + UiContractNegotiationToJSON, + UiDataOfferToJSON, +} from '@sovity.de/edc-client'; +import { + assetIdAvailable, + assetPage, + createAsset, + deleteAsset, + editAsset, +} from './connector-fake-impl/asset-fake-service'; +import {getCatalogPageDataOffers} from './connector-fake-impl/catalog-fake-service'; +import { + contractAgreementInitiateTransfer, + contractAgreementPage, +} from './connector-fake-impl/contract-agreement-fake-service'; +import { + contractDefinitionIdAvailable, + contractDefinitionPage, + createContractDefinition, + deleteContractDefinition, +} from './connector-fake-impl/contract-definition-fake-service'; +import { + getContractNegotiation, + initiateContractNegotiation, +} from './connector-fake-impl/contract-negotiation-fake-service'; +import {initiateContractTermination} from './connector-fake-impl/contract-termination-fake-service'; +import {dashboardPage} from './connector-fake-impl/dashboard-fake-service'; +import {createDataOffer} from './connector-fake-impl/data-offer-fake-service'; +import {connectorLimits} from './connector-fake-impl/ee-fake-service'; +import { + createPolicyDefinition, + createPolicyDefinitionV2, + deletePolicyDefinition, + policyDefinitionIdAvailable, + policyDefinitionPage, +} from './connector-fake-impl/policy-definition-fake-service'; +import { + transferHistoryPage, + transferProcessAsset, +} from './connector-fake-impl/transfer-history-fake-service'; +import { + getBody, + getMethod, + getQueryParams, + getUrl, +} from './utils/request-utils'; +import {ok} from './utils/response-utils'; +import {UrlInterceptor} from './utils/url-interceptor'; + +export const EDC_FAKE_BACKEND: FetchAPI = async ( + input: RequestInfo, + init?: RequestInit, +): Promise => { + const url = getUrl(input, 'http://edc.fake-backend/wrapper/'); + const method = getMethod(init); + const body = getBody(init); + const params = getQueryParams(input); + + console.log( + ...[ + 'Fake Backend:', + method, + url, + (params as any)['size'] ? params : null, + body, + ].filter((it) => !!it), + ); + + return new UrlInterceptor(url, method) + .url('ui/pages/dashboard-page') + .on('GET', () => { + const page = dashboardPage(); + return ok(DashboardPageToJSON(page)); + }) + + .url('ui/pages/asset-page') + .on('GET', () => { + const page = assetPage(); + return ok(AssetPageToJSON(page)); + }) + + .url('ui/pages/asset-page/assets') + .on('POST', () => { + const createRequest = UiAssetCreateRequestFromJSON(body); + const created = createAsset(createRequest); + return ok(IdResponseDtoToJSON(created)); + }) + + .url('ui/pages/asset-page/assets/*') + .on('DELETE', (assetId) => { + const deleted = deleteAsset(assetId); + return ok(IdResponseDtoToJSON(deleted)); + }) + + .url('ui/pages/asset-page/assets/*') + .on('PUT', (assetId) => { + const editRequest = UiAssetEditRequestFromJSON(body); + const created = editAsset(assetId, editRequest); + return ok(IdResponseDtoToJSON(created)); + }) + + .url('ui/pages/policy-page') + .on('GET', () => { + const page = policyDefinitionPage(); + return ok(PolicyDefinitionPageToJSON(page)); + }) + + .url('ui/pages/policy-page/policy-definitions') + .on('POST', () => { + const createRequest = PolicyDefinitionCreateRequestFromJSON(body); + const created = createPolicyDefinition(createRequest); + return ok(IdResponseDtoToJSON(created)); + }) + + .url('ui/v2/pages/policy-page/policy-definitions') + .on('POST', () => { + const createRequest = PolicyDefinitionCreateDtoFromJSON(body); + const created = createPolicyDefinitionV2(createRequest); + return ok(IdResponseDtoToJSON(created)); + }) + + .url('ui/pages/policy-page/policy-definitions/*') + .on('DELETE', (policyDefinitionId) => { + const deleted = deletePolicyDefinition(policyDefinitionId); + return ok(IdResponseDtoToJSON(deleted)); + }) + + .url('ui/pages/contract-definition-page') + .on('GET', () => { + const page = contractDefinitionPage(); + return ok(ContractDefinitionPageToJSON(page)); + }) + + .url('ui/pages/contract-definition-page/contract-definitions') + .on('POST', () => { + const createRequest = ContractDefinitionRequestFromJSON(body); + const created = createContractDefinition(createRequest); + return ok(IdResponseDtoToJSON(created)); + }) + + .url('ui/pages/contract-definition-page/contract-definitions/*') + .on('DELETE', (contractDefinitionId) => { + const deleted = deleteContractDefinition(contractDefinitionId); + return ok(IdResponseDtoToJSON(deleted)); + }) + + .url('ui/pages/catalog-page/data-offers') + .on('GET', () => { + const connectorEndpoint = params.get('connectorEndpoint')!; + const dataOffers = getCatalogPageDataOffers(connectorEndpoint); + return ok(dataOffers.map(UiDataOfferToJSON)); + }) + + .url('ui/pages/catalog-page/contract-negotiations') + .on('POST', () => { + const createRequest = ContractNegotiationRequestFromJSON(body); + const contractNegotiation = initiateContractNegotiation(createRequest); + return ok(UiContractNegotiationToJSON(contractNegotiation)); + }) + + .url('ui/pages/catalog-page/contract-negotiations/*') + .on('GET', (contractNegotiationId) => { + const contractNegotiation = getContractNegotiation(contractNegotiationId); + return ok(UiContractNegotiationToJSON(contractNegotiation)); + }) + + .url('ui/pages/contract-agreement-page') + .on('POST', () => { + const pageQuery = body ? ContractAgreementPageQueryFromJSON(body) : null; + const page = contractAgreementPage(pageQuery?.terminationStatus); + return ok(ContractAgreementPageToJSON(page)); + }) + + .url('ui/pages/contract-agreement-page/*') + .on('GET', (contractAgreementId: String) => { + return ok( + contractAgreementPage().contractAgreements.find( + (contractAgreement) => + contractAgreement.contractAgreementId === contractAgreementId, + ), + ); + }) + + .url('ui/pages/content-agreement-page/*/terminate') + .on('POST', (contractAgreementId) => { + const request = ContractTerminationRequestFromJSON(body); + const response = initiateContractTermination({ + contractAgreementId: contractAgreementId, + contractTerminationRequest: request, + }); + return ok(IdResponseDtoToJSON(response)); + }) + + .url('ui/pages/contract-agreement-page/transfers') + .on('POST', () => { + const transferRequest = InitiateTransferRequestFromJSON(body); + const created = contractAgreementInitiateTransfer(transferRequest); + return ok(IdResponseDtoToJSON(created)); + }) + + .url('ui/pages/transfer-history-page') + .on('GET', () => { + const page = transferHistoryPage(); + return ok(TransferHistoryPageToJSON(page)); + }) + + .url('ui/pages/transfer-history-page/transfer-processes/*/asset') + .on('GET', (transferProcessId) => { + const asset = transferProcessAsset(transferProcessId); + return ok(UiAssetToJSON(asset)); + }) + + .url('ee/connector-limits') + .on('GET', () => { + const limits = connectorLimits(); + return ok(ConnectorLimitsToJSON(limits)); + }) + + .url('ui/pages/create-data-offer') + .on('POST', () => { + const response = createDataOffer(DataOfferCreationRequestFromJSON(body)); + return ok(IdResponseDtoToJSON(response)); + }) + + .url('ui/pages/data-offer-page/validate-asset-id/*') + .on('GET', (assetId) => { + const response = assetIdAvailable(assetId); + return ok(IdAvailabilityResponseToJSON(response)); + }) + + .url('ui/pages/data-offer-page/validate-policy-id/*') + .on('GET', (policyId) => { + const response = policyDefinitionIdAvailable(policyId); + return ok(IdAvailabilityResponseToJSON(response)); + }) + + .url('ui/pages/data-offer-page/validate-contract-definition-id/*') + .on('GET', (contractDefinitionId) => { + const response = contractDefinitionIdAvailable(contractDefinitionId); + return ok(IdAvailabilityResponseToJSON(response)); + }) + + .tryMatch(); +}; diff --git a/connector-ui/src/app/core/services/api/fake-backend/utils/request-utils.ts b/connector-ui/src/app/core/services/api/fake-backend/utils/request-utils.ts new file mode 100644 index 000000000..cc9fd256a --- /dev/null +++ b/connector-ui/src/app/core/services/api/fake-backend/utils/request-utils.ts @@ -0,0 +1,20 @@ +export const getUrl = (input: Request | string, baseUrl: string): string => { + const url = new URL(typeof input === 'string' ? input : input.url); + const urlNoQuery = url.origin + url.pathname; + return urlNoQuery.startsWith(baseUrl) + ? urlNoQuery.substring(baseUrl.length) + : urlNoQuery; +}; + +export const getMethod = (init: RequestInit | undefined): string => + init?.method ?? 'GET'; + +export const getBody = (input: RequestInit | undefined): null | any => { + const body = input?.body?.toString(); + return body ? JSON.parse(body) : null; +}; + +export const getQueryParams = (input: Request | string): URLSearchParams => { + const url = new URL(typeof input === 'string' ? input : input.url); + return url.searchParams; +}; diff --git a/connector-ui/src/app/core/services/api/fake-backend/utils/response-utils.ts b/connector-ui/src/app/core/services/api/fake-backend/utils/response-utils.ts new file mode 100644 index 000000000..7d8b6a8b7 --- /dev/null +++ b/connector-ui/src/app/core/services/api/fake-backend/utils/response-utils.ts @@ -0,0 +1,7 @@ +export function ok(body: any): Promise { + console.log('Fake Backend returns: ', body); + return new Promise((resolve) => { + const response = new Response(JSON.stringify(body), {status: 200}); + setTimeout(() => resolve(response), 400); + }); +} diff --git a/connector-ui/src/app/core/services/api/fake-backend/utils/url-interceptor.ts b/connector-ui/src/app/core/services/api/fake-backend/utils/url-interceptor.ts new file mode 100644 index 000000000..9c01d8b3a --- /dev/null +++ b/connector-ui/src/app/core/services/api/fake-backend/utils/url-interceptor.ts @@ -0,0 +1,65 @@ +/** + * Collects URLs + Method + ResponseFn and then matches them in order. + * + * This class only exists to clean up the fake-backend code. + */ +export class UrlInterceptor { + private entries: { + urlPattern: string; + method: string; + response: ResponseFn; + }[] = []; + + private lastUrlPattern: string | null = null; + + constructor(public requestUrl: String, public requestMethod: string) {} + + url(urlPattern: string): this { + this.lastUrlPattern = urlPattern; + return this; + } + + on(method: string, response: ResponseFn): this { + const urlPattern = this.lastUrlPattern; + if (!urlPattern) { + throw new Error('Call .url() before calling .on()'); + } + this.entries.push({urlPattern, method, response}); + return this; + } + + async tryMatch(): Promise { + for (let entry of this.entries) { + if (entry.method !== this.requestMethod) { + continue; + } + + const regexp = '^' + entry.urlPattern.replace(/\*/g, '(.*)') + '$'; + let match = this.requestUrl.match(regexp); + if (!match) { + continue; + } + + match = match + .filter((_, index) => index > 0) + .map((pathSegment) => decodeURIComponent(pathSegment)); + + return await entry.response(...match); + } + + console.warn( + `Unmatched request: ${this.requestMethod} ${this.requestUrl}`, + this.entries.map((it) => `${it.method} ${it.urlPattern}`), + ); + + return Promise.reject( + `Unmatched request: ${this.requestMethod} ${this.requestUrl}`, + ); + } +} + +export type ResponseFn = (...match: string[]) => Promise; + +export interface MethodMatcher { + on(method: string, accept: ResponseFn): MethodMatcher; +} diff --git a/connector-ui/src/app/core/services/api/last-commit-info.service.ts b/connector-ui/src/app/core/services/api/last-commit-info.service.ts new file mode 100644 index 000000000..64c8f51f2 --- /dev/null +++ b/connector-ui/src/app/core/services/api/last-commit-info.service.ts @@ -0,0 +1,30 @@ +import {HttpClient} from '@angular/common/http'; +import {Inject, Injectable} from '@angular/core'; +import {Observable} from 'rxjs'; +import {APP_CONFIG, AppConfig} from '../../config/app-config'; +import {LastCommitInfo} from './model/last-commit-info'; + +@Injectable({ + providedIn: 'root', +}) +export class LastCommitInfoService { + constructor( + private http: HttpClient, + @Inject(APP_CONFIG) public config: AppConfig, + ) {} + + getLastCommitInfoData(): Observable { + const url = `${this.config.managementApiUrl}/last-commit-info`; + return this.http.get(url); + } + + getUiCommitDetails(): Observable { + const path = '/assets/config/version.txt'; + return this.http.get(path, {responseType: 'text'}); + } + + getUiBuildDateDetails(): Observable { + const path = '/assets/config/ui-build-date.txt'; + return this.http.get(path, {responseType: 'text'}); + } +} diff --git a/connector-ui/src/app/core/services/api/model/criterion-type-ext.ts b/connector-ui/src/app/core/services/api/model/criterion-type-ext.ts new file mode 100644 index 000000000..518a80fb8 --- /dev/null +++ b/connector-ui/src/app/core/services/api/model/criterion-type-ext.ts @@ -0,0 +1,7 @@ +import {UiCriterionOperator} from '@sovity.de/edc-client'; + +export const CRITERION_OPERATOR_SYMBOLS: Record = { + EQ: '=', + IN: 'in', + LIKE: 'like', +}; diff --git a/connector-ui/src/app/core/services/api/model/last-commit-info.ts b/connector-ui/src/app/core/services/api/model/last-commit-info.ts new file mode 100644 index 000000000..0f1502d52 --- /dev/null +++ b/connector-ui/src/app/core/services/api/model/last-commit-info.ts @@ -0,0 +1,6 @@ +export interface LastCommitInfo { + envLastCommitInfo: string | null; + envBuildDate: string | null; + jarLastCommitInfo: string | null; + jarBuildDate: string | null; +} diff --git a/connector-ui/src/app/core/services/asset-builder.ts b/connector-ui/src/app/core/services/asset-builder.ts new file mode 100644 index 000000000..7fba6a0b2 --- /dev/null +++ b/connector-ui/src/app/core/services/asset-builder.ts @@ -0,0 +1,122 @@ +import {Injectable} from '@angular/core'; +import {UiAsset} from '@sovity.de/edc-client'; +import {DataCategorySelectItem} from '../../shared/form-elements/data-category-select/data-category-select-item'; +import {DataCategorySelectItemService} from '../../shared/form-elements/data-category-select/data-category-select-item.service'; +import {DataSubcategorySelectItem} from '../../shared/form-elements/data-subcategory-select/data-subcategory-select-item'; +import {DataSubcategorySelectItemService} from '../../shared/form-elements/data-subcategory-select/data-subcategory-select-item.service'; +import {LanguageSelectItem} from '../../shared/form-elements/language-select/language-select-item'; +import {LanguageSelectItemService} from '../../shared/form-elements/language-select/language-select-item.service'; +import {TransportModeSelectItem} from '../../shared/form-elements/transport-mode-select/transport-mode-select-item'; +import {TransportModeSelectItemService} from '../../shared/form-elements/transport-mode-select/transport-mode-select-item.service'; +import {AdditionalAssetProperty, UiAssetMapped} from './models/ui-asset-mapped'; + +/** + * Maps between EDC Asset and our type safe asset + */ +@Injectable({ + providedIn: 'root', +}) +export class AssetBuilder { + constructor( + private languageSelectItemService: LanguageSelectItemService, + private transportModeSelectItemService: TransportModeSelectItemService, + private dataCategorySelectItemService: DataCategorySelectItemService, + private dataSubcategorySelectItemService: DataSubcategorySelectItemService, + ) {} + + buildAsset(asset: UiAsset): UiAssetMapped { + const { + language, + dataCategory, + dataSubcategory, + transportMode, + ...assetProperties + } = asset; + + const { + customJsonAsString, + customJsonLdAsString, + privateCustomJsonAsString, + privateCustomJsonLdAsString, + } = asset; + + return { + ...assetProperties, + language: this.getLanguageItem(language), + dataCategory: this.getDataCategoryItem(dataCategory), + dataSubcategory: this.getDataSubcategoryItem(dataSubcategory), + transportMode: this.getTransportModeItem(transportMode), + customJsonProperties: this.buildAdditionalProperties(customJsonAsString), + customJsonLdProperties: + this.buildAdditionalProperties(customJsonLdAsString), + privateCustomJsonProperties: this.buildAdditionalProperties( + privateCustomJsonAsString, + ), + privateCustomJsonLdProperties: this.buildAdditionalProperties( + privateCustomJsonLdAsString, + ), + }; + } + + private getTransportModeItem( + transportMode: string | undefined, + ): TransportModeSelectItem | null { + return transportMode == null + ? null + : this.transportModeSelectItemService.findById(transportMode); + } + + private getDataSubcategoryItem( + dataSubcategory: string | undefined, + ): DataSubcategorySelectItem | null { + return dataSubcategory == null + ? null + : this.dataSubcategorySelectItemService.findById(dataSubcategory); + } + + private getDataCategoryItem( + dataCategory: string | undefined, + ): DataCategorySelectItem | null { + return dataCategory == null + ? null + : this.dataCategorySelectItemService.findById(dataCategory); + } + + private getLanguageItem( + language: string | undefined, + ): LanguageSelectItem | null { + return language == null + ? null + : this.languageSelectItemService.findById(language); + } + + private buildAdditionalProperties( + json: string | undefined, + ): AdditionalAssetProperty[] { + const obj = this.tryParseJsonObj(json || '{}'); + return Object.entries(obj).map( + ([key, value]): AdditionalAssetProperty => ({ + key: `${key}`, + value: + typeof value === 'object' + ? JSON.stringify(value, null, 2) + : `${value}`, + }), + ); + } + + private tryParseJsonObj(json: string): any { + const bad = {'Conversion Failure': `Bad JSON: ${json}`}; + + try { + const parsed = JSON.parse(json); + if (parsed == null) { + return {}; + } else if (typeof parsed === 'object') { + return parsed; + } + } catch (e) {} + + return bad; + } +} diff --git a/connector-ui/src/app/core/services/asset-data-source-mapper-legacy.ts b/connector-ui/src/app/core/services/asset-data-source-mapper-legacy.ts new file mode 100644 index 000000000..fb68d58b3 --- /dev/null +++ b/connector-ui/src/app/core/services/asset-data-source-mapper-legacy.ts @@ -0,0 +1,106 @@ +import {Injectable} from '@angular/core'; +import {UiDataSource} from '@sovity.de/edc-client'; +import {AssetDatasourceFormValue} from '../../routes/connector-ui/asset-list-page/asset-create-dialog/form/model/asset-datasource-form-model'; +import {HttpDatasourceHeaderFormValue} from '../../routes/connector-ui/asset-list-page/asset-create-dialog/form/model/http-datasource-header-form-model'; +import {getAuthFields} from '../utils/form-value-utils'; +import {QueryParamsMapper} from './query-params-mapper'; + +@Injectable({providedIn: 'root'}) +export class AssetDataSourceMapperLegacy { + constructor(private queryParamsMapper: QueryParamsMapper) {} + + buildDataSourceOrNullLegacy( + formValue: AssetDatasourceFormValue, + ): UiDataSource | null { + if ( + !formValue?.dataAddressType || + formValue.dataAddressType === 'Unchanged' + ) { + return null; + } + return this.buildDataSourceLegacy(formValue); + } + + buildDataSourceLegacy(formValue: AssetDatasourceFormValue): UiDataSource { + switch (formValue?.dataAddressType) { + case 'Custom-Data-Address-Json': + return this.buildCustomDataSourceLegacy(formValue); + case 'On-Request': + return this.buildOnRequestDataSourceLegacy(formValue); + case 'Http': + return this.buildHttpDataSourceLegacy(formValue); + default: + throw new Error( + `Invalid Data Address Type ${formValue?.dataAddressType}`, + ); + } + } + + private buildCustomDataSourceLegacy( + formValue: AssetDatasourceFormValue, + ): UiDataSource { + const json = JSON.parse(formValue.dataDestination?.trim()!!); + return { + type: 'CUSTOM', + customProperties: json, + }; + } + + private buildHttpDataSourceLegacy( + formValue: AssetDatasourceFormValue, + ): UiDataSource { + const baseUrl = this.queryParamsMapper.getBaseUrlWithoutQueryParams( + formValue.httpUrl!, + )!; + const queryString = this.queryParamsMapper.getFullQueryString( + formValue.httpUrl!, + formValue.httpQueryParams ?? [], + ); + + const authFields = getAuthFields(formValue); + + return { + type: 'HTTP_DATA', + httpData: { + method: formValue.httpMethod, + baseUrl, + queryString: queryString ?? undefined, + authHeaderName: authFields.authHeaderName ?? undefined, + authHeaderValue: { + secretName: authFields.authHeaderSecretName ?? undefined, + rawValue: authFields.authHeaderValue ?? undefined, + }, + headers: this.buildHttpHeaders(formValue.httpHeaders ?? []), + enableMethodParameterization: formValue.httpProxyMethod, + enablePathParameterization: formValue.httpProxyPath, + enableQueryParameterization: formValue.httpProxyQueryParams, + enableBodyParameterization: formValue.httpProxyBody, + }, + }; + } + + private buildOnRequestDataSourceLegacy( + formValue: AssetDatasourceFormValue, + ): UiDataSource { + return { + type: 'ON_REQUEST', + onRequest: { + contactEmail: formValue.contactEmail!!, + contactPreferredEmailSubject: formValue.contactPreferredEmailSubject!!, + }, + }; + } + + private buildHttpHeaders( + headers: HttpDatasourceHeaderFormValue[], + ): Record { + return Object.fromEntries( + headers + .map((header) => [ + header.headerName?.trim() || '', + header.headerValue?.trim() || '', + ]) + .filter((a) => a[0] && a[1]), + ); + } +} diff --git a/connector-ui/src/app/core/services/asset-data-source-mapper.ts b/connector-ui/src/app/core/services/asset-data-source-mapper.ts new file mode 100644 index 000000000..03f4b74d4 --- /dev/null +++ b/connector-ui/src/app/core/services/asset-data-source-mapper.ts @@ -0,0 +1,105 @@ +import {Injectable} from '@angular/core'; +import {UiDataSource} from '@sovity.de/edc-client'; +import {AssetDatasourceFormValue} from '../../shared/business/edit-asset-form/form/model/asset-datasource-form-model'; +import {HttpDatasourceHeaderFormValue} from '../../shared/business/edit-asset-form/form/model/http-datasource-header-form-model'; +import {getAuthFields} from '../utils/form-value-utils'; +import {QueryParamsMapper} from './query-params-mapper'; + +@Injectable({providedIn: 'root'}) +export class AssetDataSourceMapper { + constructor(private queryParamsMapper: QueryParamsMapper) {} + + buildDataSourceOrNull( + formValue: AssetDatasourceFormValue | undefined, + ): UiDataSource | null { + if (!formValue || formValue.dataSourceAvailability === 'Unchanged') { + return null; + } + return this.buildDataSource(formValue); + } + + buildDataSource(formValue: AssetDatasourceFormValue): UiDataSource { + if (formValue.dataSourceAvailability === 'On-Request') { + return this.buildOnRequestDataSource(formValue); + } + + switch (formValue?.dataAddressType) { + case 'Custom-Data-Address-Json': + return this.buildCustomDataSource(formValue); + case 'Http': + return this.buildHttpDataSource(formValue); + default: + throw new Error( + `Invalid Data Address Type ${formValue?.dataAddressType}`, + ); + } + } + + private buildCustomDataSource( + formValue: AssetDatasourceFormValue, + ): UiDataSource { + const json = JSON.parse(formValue.dataDestination?.trim()!!); + return { + type: 'CUSTOM', + customProperties: json, + }; + } + + private buildHttpDataSource( + formValue: AssetDatasourceFormValue, + ): UiDataSource { + const baseUrl = this.queryParamsMapper.getBaseUrlWithoutQueryParams( + formValue.httpUrl!, + )!; + const queryString = this.queryParamsMapper.getFullQueryString( + formValue.httpUrl!, + formValue.httpQueryParams ?? [], + ); + + const authFields = getAuthFields(formValue); + + return { + type: 'HTTP_DATA', + httpData: { + method: formValue.httpMethod, + baseUrl, + queryString: queryString ?? undefined, + authHeaderName: authFields.authHeaderName ?? undefined, + authHeaderValue: { + secretName: authFields.authHeaderSecretName ?? undefined, + rawValue: authFields.authHeaderValue ?? undefined, + }, + headers: this.buildHttpHeaders(formValue.httpHeaders ?? []), + enableMethodParameterization: formValue.httpProxyMethod, + enablePathParameterization: formValue.httpProxyPath, + enableQueryParameterization: formValue.httpProxyQueryParams, + enableBodyParameterization: formValue.httpProxyBody, + }, + }; + } + + private buildOnRequestDataSource( + formValue: AssetDatasourceFormValue, + ): UiDataSource { + return { + type: 'ON_REQUEST', + onRequest: { + contactEmail: formValue.contactEmail!!, + contactPreferredEmailSubject: formValue.contactPreferredEmailSubject!!, + }, + }; + } + + private buildHttpHeaders( + headers: HttpDatasourceHeaderFormValue[], + ): Record { + return Object.fromEntries( + headers + .map((header) => [ + header.headerName?.trim() || '', + header.headerValue?.trim() || '', + ]) + .filter((a) => a[0] && a[1]), + ); + } +} diff --git a/connector-ui/src/app/core/services/asset-request-builder-legacy.ts b/connector-ui/src/app/core/services/asset-request-builder-legacy.ts new file mode 100644 index 000000000..e97533f70 --- /dev/null +++ b/connector-ui/src/app/core/services/asset-request-builder-legacy.ts @@ -0,0 +1,97 @@ +import {Injectable} from '@angular/core'; +import {UiAssetCreateRequest} from '@sovity.de/edc-client'; +import {AssetCreateDialogFormValue} from '../../routes/connector-ui/asset-list-page/asset-create-dialog/form/model/asset-create-dialog-form-model'; +import {toGmtZeroHourDate} from '../utils/date-utils'; +import {AssetDataSourceMapperLegacy} from './asset-data-source-mapper-legacy'; +import {AssetRequestCommonMetadata} from './asset-request-common-metadata'; + +@Injectable() +export class AssetRequestBuilderLegacy { + constructor(private assetDataSourceMapper: AssetDataSourceMapperLegacy) {} + + buildAssetCreateRequestLegacy( + formValue: AssetCreateDialogFormValue, + ): UiAssetCreateRequest { + const id = formValue.metadata?.id!; + const metadata = this.buildAssetRequestCommonMetadataLegacy(formValue); + const dataSource = this.assetDataSourceMapper.buildDataSourceLegacy( + formValue.datasource!, + ); + return { + id, + ...metadata, + dataSource, + }; + } + + buildAssetRequestCommonMetadataLegacy( + formValue: AssetCreateDialogFormValue, + ): AssetRequestCommonMetadata { + const title = formValue.metadata?.title!; + const version = formValue.metadata?.version; + const description = formValue.metadata?.description; + const language = formValue.metadata?.language?.id; + const keywords = formValue.metadata?.keywords; + const licenseUrl = formValue.metadata?.standardLicense; + const publisherHomepage = formValue.metadata?.publisher; + const mediaType = formValue.metadata?.contentType; + const landingPageUrl = formValue.metadata?.endpointDocumentation; + + const dataCategory = formValue.advanced?.dataCategory?.id; + const dataSubcategory = formValue.advanced?.dataSubcategory?.id; + const transportMode = formValue.advanced?.transportMode?.id; + const geoReferenceMethod = formValue.advanced?.geoReferenceMethod; + const dataModel = formValue.advanced?.dataModel; + + const sovereignLegalName = formValue.advanced?.sovereignLegalName; + const geoLocation = formValue.advanced?.geoLocation; + const nutsLocations = formValue.advanced?.nutsLocations; + const dataSampleUrls = formValue.advanced?.dataSampleUrls; + const referenceFileUrls = formValue.advanced?.referenceFileUrls; + const referenceFilesDescription = + formValue.advanced?.referenceFilesDescription; + const conditionsForUse = formValue.advanced?.conditionsForUse; + const dataUpdateFrequency = formValue.advanced?.dataUpdateFrequency; + let temporalCoverageFrom = + formValue.advanced?.temporalCoverage?.from || undefined; + let temporalCoverageToInclusive = + formValue.advanced?.temporalCoverage?.toInclusive || undefined; + temporalCoverageFrom = temporalCoverageFrom + ? toGmtZeroHourDate(temporalCoverageFrom) + : undefined; + temporalCoverageToInclusive = temporalCoverageToInclusive + ? toGmtZeroHourDate(temporalCoverageToInclusive) + : undefined; + + return { + title, + language, + description, + publisherHomepage, + licenseUrl, + version, + keywords, + mediaType, + landingPageUrl, + dataCategory, + dataSubcategory, + dataModel, + geoReferenceMethod, + transportMode, + sovereignLegalName, + geoLocation, + nutsLocations, + dataSampleUrls, + referenceFileUrls, + referenceFilesDescription, + conditionsForUse, + dataUpdateFrequency, + temporalCoverageFrom, + temporalCoverageToInclusive, + customJsonAsString: undefined, + customJsonLdAsString: undefined, + privateCustomJsonAsString: undefined, + privateCustomJsonLdAsString: undefined, + }; + } +} diff --git a/connector-ui/src/app/core/services/asset-request-builder.ts b/connector-ui/src/app/core/services/asset-request-builder.ts new file mode 100644 index 000000000..284b71987 --- /dev/null +++ b/connector-ui/src/app/core/services/asset-request-builder.ts @@ -0,0 +1,105 @@ +import {Injectable} from '@angular/core'; +import {UiAssetCreateRequest, UiAssetEditRequest} from '@sovity.de/edc-client'; +import {EditAssetFormValue} from 'src/app/shared/business/edit-asset-form/form/model/edit-asset-form-model'; +import {toGmtZeroHourDate} from '../utils/date-utils'; +import {AssetDataSourceMapper} from './asset-data-source-mapper'; +import {AssetRequestCommonMetadata} from './asset-request-common-metadata'; + +@Injectable() +export class AssetRequestBuilder { + constructor(private assetDataSourceMapper: AssetDataSourceMapper) {} + + buildAssetCreateRequest(formValue: EditAssetFormValue): UiAssetCreateRequest { + const id = formValue.general?.id!; + const metadata = this.buildAssetRequestCommonMetadata(formValue); + const dataSource = this.assetDataSourceMapper.buildDataSource( + formValue.datasource!, + ); + return { + id, + ...metadata, + dataSource, + }; + } + + buildAssetEditRequest(formValue: EditAssetFormValue): UiAssetEditRequest { + const metadata = this.buildAssetRequestCommonMetadata(formValue); + const dataSourceOrNull = this.assetDataSourceMapper.buildDataSourceOrNull( + formValue.datasource, + ); + return { + ...metadata, + dataSourceOverrideOrNull: dataSourceOrNull ?? undefined, + }; + } + + buildAssetRequestCommonMetadata( + formValue: EditAssetFormValue, + ): AssetRequestCommonMetadata { + const title = formValue.general?.name!; + const version = formValue.general?.version; + const description = formValue.general?.description; + const language = formValue.general?.language?.id; + const keywords = formValue.general?.keywords; + const licenseUrl = formValue.general?.standardLicense; + const publisherHomepage = formValue.general?.publisher; + const mediaType = formValue.general?.contentType; + const landingPageUrl = formValue.general?.endpointDocumentation; + const dataCategory = formValue.general?.dataCategory?.id; + const dataSubcategory = formValue.general?.dataSubcategory?.id; + + const transportMode = formValue.advanced?.transportMode?.id; + const geoReferenceMethod = formValue.advanced?.geoReferenceMethod; + const dataModel = formValue.advanced?.dataModel; + const sovereignLegalName = formValue.advanced?.sovereignLegalName; + const geoLocation = formValue.advanced?.geoLocation; + const nutsLocations = formValue.advanced?.nutsLocations; + const dataSampleUrls = formValue.advanced?.dataSampleUrls; + const referenceFileUrls = formValue.advanced?.referenceFileUrls; + const referenceFilesDescription = + formValue.advanced?.referenceFilesDescription; + const conditionsForUse = formValue.advanced?.conditionsForUse; + const dataUpdateFrequency = formValue.advanced?.dataUpdateFrequency; + let temporalCoverageFrom = + formValue.advanced?.temporalCoverage?.from || undefined; + let temporalCoverageToInclusive = + formValue.advanced?.temporalCoverage?.toInclusive || undefined; + temporalCoverageFrom = temporalCoverageFrom + ? toGmtZeroHourDate(temporalCoverageFrom) + : undefined; + temporalCoverageToInclusive = temporalCoverageToInclusive + ? toGmtZeroHourDate(temporalCoverageToInclusive) + : undefined; + + return { + title, + language, + description, + publisherHomepage, + licenseUrl, + version, + keywords, + mediaType, + landingPageUrl, + dataCategory, + dataSubcategory, + dataModel, + geoReferenceMethod, + transportMode, + sovereignLegalName, + geoLocation, + nutsLocations, + dataSampleUrls, + referenceFileUrls, + referenceFilesDescription, + conditionsForUse, + dataUpdateFrequency, + temporalCoverageFrom, + temporalCoverageToInclusive, + customJsonAsString: undefined, + customJsonLdAsString: undefined, + privateCustomJsonAsString: undefined, + privateCustomJsonLdAsString: undefined, + }; + } +} diff --git a/connector-ui/src/app/core/services/asset-request-common-metadata.ts b/connector-ui/src/app/core/services/asset-request-common-metadata.ts new file mode 100644 index 000000000..575fc1722 --- /dev/null +++ b/connector-ui/src/app/core/services/asset-request-common-metadata.ts @@ -0,0 +1,144 @@ +/** + * Common Properties between Asset Create and Edit Request + */ +export interface AssetRequestCommonMetadata { + /** + * Asset Title + */ + title: string | undefined; + + /** + * Asset Language + */ + language: string | undefined; + + /** + * Asset Description + */ + description: string | undefined; + + /** + * Asset Homepage + */ + publisherHomepage: string | undefined; + + /** + * License URL + */ + licenseUrl: string | undefined; + + /** + * Version + */ + version: string | undefined; + + /** + * Asset Keywords + */ + keywords: Array | undefined; + + /** + * Asset MediaType + */ + mediaType: string | undefined; + + /** + * Landing Page URL + */ + landingPageUrl: string | undefined; + + /** + * Data Category + */ + dataCategory: string | undefined; + + /** + * Data Subcategory + */ + dataSubcategory: string | undefined; + + /** + * Data Model + */ + dataModel: string | undefined; + + /** + * Geo-Reference Method + */ + geoReferenceMethod: string | undefined; + + /** + * Transport Mode + */ + transportMode: string | undefined; + + /** + * The sovereign is distinct from the publisher by being the legal owner of the data. + */ + sovereignLegalName: string | undefined; + + /** + * Geo location + */ + geoLocation: string | undefined; + + /** + * Locations by NUTS standard which divides countries into administrative divisions + */ + nutsLocations: Array | undefined; + + /** + * Data sample URLs + */ + dataSampleUrls: Array | undefined; + + /** + * Reference file/schema URLs + */ + referenceFileUrls: Array | undefined; + + /** + * Additional information on reference files/schemas + */ + referenceFilesDescription: string | undefined; + + /** + * Instructions for use that are not legally relevant e.g. information on how to cite the dataset in papers + */ + conditionsForUse: string | undefined; + + /** + * Data update frequency + */ + dataUpdateFrequency: string | undefined; + + /** + * Temporal coverage start date + */ + temporalCoverageFrom: Date | undefined; + + /** + * Temporal coverage end date (inclusive) + */ + temporalCoverageToInclusive: Date | undefined; + + /** + * Contains serialized custom properties in the JSON format. + */ + customJsonAsString: string | undefined; + + /** + * Contains serialized custom properties in the JSON LD format. Contrary to the customJsonAsString field, this string must represent a JSON LD object and will be affected by JSON LD compaction and expansion. Due to a technical limitation, the properties can't be booleans. + */ + customJsonLdAsString: string | undefined; + + /** + * Same as customJsonAsString but the data will be stored in the private properties. + */ + privateCustomJsonAsString: string | undefined; + + /** + * Same as customJsonLdAsString but the data will be stored in the private properties. The same limitations apply. + */ + privateCustomJsonLdAsString: string | undefined; +} diff --git a/connector-ui/src/app/core/services/asset.service.ts b/connector-ui/src/app/core/services/asset.service.ts new file mode 100644 index 000000000..ce7bcc597 --- /dev/null +++ b/connector-ui/src/app/core/services/asset.service.ts @@ -0,0 +1,29 @@ +import {Injectable} from '@angular/core'; +import {Observable} from 'rxjs'; +import {map} from 'rxjs/operators'; +import {EdcApiService} from './api/edc-api.service'; +import {AssetBuilder} from './asset-builder'; +import {UiAssetMapped} from './models/ui-asset-mapped'; + +/** + * Wrapped AssetService with AssetPropertyMapper + */ +@Injectable({ + providedIn: 'root', +}) +export class AssetService { + constructor( + private assetBuilder: AssetBuilder, + private edcApiService: EdcApiService, + ) {} + + fetchAssets(): Observable { + return this.edcApiService + .getAssetPage() + .pipe( + map((assetPage) => + assetPage.assets.map((asset) => this.assetBuilder.buildAsset(asset)), + ), + ); + } +} diff --git a/connector-ui/src/app/core/services/chart-color.service.ts b/connector-ui/src/app/core/services/chart-color.service.ts new file mode 100644 index 000000000..c2739d9b5 --- /dev/null +++ b/connector-ui/src/app/core/services/chart-color.service.ts @@ -0,0 +1,29 @@ +import {Injectable} from '@angular/core'; + +@Injectable({providedIn: 'root'}) +export class ChartColorService { + private chartColors = [ + '#fd7f6f', + '#7eb0d5', + '#b2e061', + '#bd7ebe', + '#ffb55a', + '#ffee65', + '#beb9db', + '#fdcce5', + '#8bd3c7', + ]; + + /** + * To make charts look different but recognizable, we take colors at different offsets, but rotate the colors in + * the same direction on the palette. If necessary we wrap around + * @param amount + */ + getColors(amount: number, offset: number): string[] { + return Array.from({length: amount}, (_, i) => this.colorAt(i + offset)); + } + + colorAt(index: number): string { + return this.chartColors[index % this.chartColors.length]; + } +} diff --git a/connector-ui/src/app/core/services/connector-info-property-grid-group-builder.ts b/connector-ui/src/app/core/services/connector-info-property-grid-group-builder.ts new file mode 100644 index 000000000..64faf64af --- /dev/null +++ b/connector-ui/src/app/core/services/connector-info-property-grid-group-builder.ts @@ -0,0 +1,275 @@ +import {Injectable} from '@angular/core'; +import {MatDialog} from '@angular/material/dialog'; +import {TranslateService} from '@ngx-translate/core'; +import {DashboardPage} from '@sovity.de/edc-client'; +import {JsonDialogComponent} from '../../shared/common/json-dialog/json-dialog.component'; +import {JsonDialogData} from '../../shared/common/json-dialog/json-dialog.data'; +import {PropertyGridGroup} from '../../shared/common/property-grid-group/property-grid-group'; +import {PropertyGridField} from '../../shared/common/property-grid/property-grid-field'; +import {PropertyGridFieldService} from '../../shared/common/property-grid/property-grid-field.service'; +import {LastCommitInfo} from './api/model/last-commit-info'; +import {Fetched} from './models/fetched'; +import {ParticipantIdLocalization} from './participant-id-localization'; + +@Injectable({providedIn: 'root'}) +export class ConnectorInfoPropertyGridGroupBuilder { + constructor( + private participantIdLocalization: ParticipantIdLocalization, + private propertyGridUtils: PropertyGridFieldService, + private matDialog: MatDialog, + private translateService: TranslateService, + ) {} + + private onShowConnectorVersionClick(title: string, versionData: any) { + const data: JsonDialogData = { + title, + subtitle: this.translateService.instant('general.details'), + icon: 'link', + objectForJson: versionData, + }; + this.matDialog.open(JsonDialogComponent, {data}); + } + + private asDate(dateString?: string) { + return dateString ? new Date(dateString!).toLocaleString() : ''; + } + + getBackendVersionFields( + lastCommitInfo: Fetched, + ): PropertyGridField[] { + const buildProp = ( + label: string, + dateProp: keyof LastCommitInfo, + detailsProp: keyof LastCommitInfo, + ) => + lastCommitInfo.match({ + ifOk: (lastCommitInfo) => ({ + icon: 'link', + label, + text: lastCommitInfo[dateProp] + ? this.asDate(lastCommitInfo[dateProp] ?? undefined) + : 'Show Details', + onclick: () => + this.onShowConnectorVersionClick('Version Information', { + [`${label} Last Commit Information"`]: + lastCommitInfo[detailsProp], + }), + }), + ifError: (error) => ({ + icon: 'link', + label, + text: 'Show Details', + onclick: () => + this.onShowConnectorVersionClick('Version Information', { + Error: error.failureMessage, + }), + }), + ifLoading: () => ({ + icon: 'link', + label, + text: 'Loading...', + }), + }); + + return [ + buildProp('CE Extensions', 'jarBuildDate', 'jarLastCommitInfo'), + buildProp('Connector', 'envBuildDate', 'envLastCommitInfo'), + ]; + } + + getUiVersionField( + uiBuildDate: Fetched, + uiCommitDetails: Fetched, + ): PropertyGridField[] { + return uiBuildDate.match({ + ifOk: (data) => [ + { + icon: 'link', + label: 'UI Version', + text: data.trim().toString() + ? this.asDate(data.trim().toString()) + : this.translateService.instant('general.details'), + onclick: () => + this.onShowConnectorVersionClick('Version Information', { + 'UI Last Commit Information': uiCommitDetails.match({ + ifOk: (uiCommitdata) => uiCommitdata, + ifError: (error) => error.failureMessage, + ifLoading: () => + this.translateService.instant('general.still_loading'), + }), + }), + }, + ], + ifError: (error) => [ + { + icon: 'link', + label: 'UI Version', + text: this.translateService.instant('general.details'), + onclick: () => + this.onShowConnectorVersionClick('Version Information', { + 'UI Commit Information': error.failureMessage, + }), + }, + ], + ifLoading: () => [ + { + icon: 'link', + label: 'UI Version', + text: this.translateService.instant('general.loading'), + }, + ], + }); + } + + buildConnectorPropertyGridGroup( + groupLabel: string | null, + dashboardData: Fetched, + ): PropertyGridGroup { + const fields: PropertyGridField[] = dashboardData.match< + PropertyGridField[] + >({ + ifLoading: () => [ + { + icon: 'info', + label: this.translateService.instant('general.loading1'), + text: this.translateService.instant('general.loading'), + }, + ], + ifError: () => [ + { + icon: 'error', + label: this.translateService.instant('general.error'), + text: this.translateService.instant('services.failed_loading'), + }, + ], + ifOk: (data) => this.buildConnectorMetadata(data), + }); + + return { + groupLabel, + properties: fields, + }; + } + + private buildConnectorMetadata(data: DashboardPage): PropertyGridField[] { + const fields = [ + { + icon: 'link', + label: this.translateService.instant('general.endpoint'), + ...this.propertyGridUtils.guessValue(data.connectorEndpoint), + }, + { + icon: 'category', + label: this.participantIdLocalization.participantId, + ...this.propertyGridUtils.guessValue(data.connectorParticipantId), + }, + { + icon: 'title', + label: this.translateService.instant('general.title'), + ...this.propertyGridUtils.guessValue(data.connectorTitle), + }, + { + icon: 'apartment', + label: this.translateService.instant('services.curator_org'), + ...this.propertyGridUtils.guessValue(data.connectorCuratorName), + }, + { + icon: 'apartment', + label: this.translateService.instant('services.curator_url'), + ...this.propertyGridUtils.guessValue(data.connectorCuratorUrl), + }, + { + icon: 'title', + label: this.translateService.instant('general.description'), + ...this.propertyGridUtils.guessValue(data.connectorDescription), + }, + { + icon: 'contact_support', + label: this.translateService.instant('services.maintainer'), + ...this.propertyGridUtils.guessValue(data.connectorMaintainerName), + }, + { + icon: 'contact_support', + label: this.translateService.instant('services.main_url'), + ...this.propertyGridUtils.guessValue(data.connectorMaintainerUrl), + }, + ]; + + if (data.connectorDapsConfig != null) { + fields.push( + { + icon: 'vpn_key', + label: 'DAPS Token URL', + ...this.propertyGridUtils.guessValue( + data.connectorDapsConfig.tokenUrl, + ), + }, + { + icon: 'lock', + label: 'DAPS JWKS URL', + ...this.propertyGridUtils.guessValue( + data.connectorDapsConfig.jwksUrl, + ), + }, + ); + } + + if (data.connectorMiwConfig != null) { + fields.push( + { + icon: 'category', + label: 'MIW Authority ID', + ...this.propertyGridUtils.guessValue( + data.connectorMiwConfig.authorityId, + ), + }, + { + icon: 'link', + label: 'MIW URL', + ...this.propertyGridUtils.guessValue(data.connectorMiwConfig.url), + }, + { + icon: 'vpn_key', + label: 'MIW Token URL', + ...this.propertyGridUtils.guessValue( + data.connectorMiwConfig.tokenUrl, + ), + }, + ); + } + + return fields; + } + + buildConnectorVersionGroup( + lastCommitInfo: Fetched, + uiBuildDate: Fetched, + uiCommitDetails: Fetched, + ): PropertyGridGroup { + return { + groupLabel: 'Version Information', + properties: [ + ...this.getBackendVersionFields(lastCommitInfo), + ...this.getUiVersionField(uiBuildDate, uiCommitDetails), + ], + }; + } + + buildPropertyGridGroups( + lastCommitInformation: Fetched, + UiBuildDate: Fetched, + UiCommitDetails: Fetched, + dashboardPageData: Fetched, + ): PropertyGridGroup[] { + const fieldGroups: PropertyGridGroup[] = [ + this.buildConnectorPropertyGridGroup(null, dashboardPageData), + this.buildConnectorVersionGroup( + lastCommitInformation, + UiBuildDate, + UiCommitDetails, + ), + ]; + + return fieldGroups.filter((it) => it.properties.length); + } +} diff --git a/connector-ui/src/app/core/services/connector-limits.service.ts b/connector-ui/src/app/core/services/connector-limits.service.ts new file mode 100644 index 000000000..d35b6dc36 --- /dev/null +++ b/connector-ui/src/app/core/services/connector-limits.service.ts @@ -0,0 +1,31 @@ +import {Injectable} from '@angular/core'; +import {Observable, of} from 'rxjs'; +import {map} from 'rxjs/operators'; +import {ConnectorLimits} from '@sovity.de/edc-client'; +import {ActiveFeatureSet} from '../config/active-feature-set'; +import {EdcApiService} from './api/edc-api.service'; + +@Injectable({ + providedIn: 'root', +}) +export class ConnectorLimitsService { + constructor( + private edcApiService: EdcApiService, + private activeFeatureSet: ActiveFeatureSet, + ) {} + + isConsumingAgreementLimitExceeded(): Observable { + return this.activeFeatureSet.hasConnectorLimits() + ? this.edcApiService + .getEnterpriseEditionConnectorLimits() + .pipe(map(this.limitsExceeded)) + : of(false); + } + + private limitsExceeded = (limits: ConnectorLimits) => { + const max = limits.maxActiveConsumingContractAgreements; + const current = limits.numActiveConsumingContractAgreements; + + return max != null && max >= 0 && current >= max; + }; +} diff --git a/connector-ui/src/app/core/services/contract-definition-builder.ts b/connector-ui/src/app/core/services/contract-definition-builder.ts new file mode 100644 index 000000000..82d41fabc --- /dev/null +++ b/connector-ui/src/app/core/services/contract-definition-builder.ts @@ -0,0 +1,38 @@ +import {Injectable} from '@angular/core'; +import { + ContractDefinitionRequest, + UiCriterionLiteralType, +} from '@sovity.de/edc-client'; +import {ContractDefinitionEditorDialogFormValue} from '../../routes/connector-ui/contract-definition-page/contract-definition-editor-dialog/contract-definition-editor-dialog-form-model'; +import {AssetProperty} from './models/asset-properties'; + +@Injectable({ + providedIn: 'root', +}) +export class ContractDefinitionBuilder { + /** + * Build {@link ContractDefinitionDto} from {@link ContractDefinitionEditorDialogFormValue} + * + * @param formValue form value + * @return contract definition dto + */ + buildContractDefinition( + formValue: ContractDefinitionEditorDialogFormValue, + ): ContractDefinitionRequest { + return { + contractDefinitionId: formValue.id ?? '', + accessPolicyId: formValue.accessPolicy!.policyDefinitionId, + contractPolicyId: formValue.contractPolicy!.policyDefinitionId, + assetSelector: [ + { + operandLeft: AssetProperty.id, + operator: 'IN', + operandRight: { + type: UiCriterionLiteralType.ValueList, + valueList: formValue.assets!.map((it) => it.assetId), + }, + }, + ], + }; + } +} diff --git a/connector-ui/src/app/core/services/contract-negotiation.service.ts b/connector-ui/src/app/core/services/contract-negotiation.service.ts new file mode 100644 index 000000000..d813ee12c --- /dev/null +++ b/connector-ui/src/app/core/services/contract-negotiation.service.ts @@ -0,0 +1,141 @@ +import {Injectable} from '@angular/core'; +import {MatDialog} from '@angular/material/dialog'; +import {EMPTY, Observable, interval} from 'rxjs'; +import {catchError, filter, first, switchMap, tap} from 'rxjs/operators'; +import {TranslateService} from '@ngx-translate/core'; +import { + ContractNegotiationRequest, + UiContractNegotiation, + UiContractOffer, +} from '@sovity.de/edc-client'; +import {environment} from '../../../environments/environment'; +import {InitiateNegotiationConfirmTosDialogComponent} from '../../shared/business/initiate-negotiation-confirm-tos-dialog/initiate-negotiation-confirm-tos-dialog.component'; +import {EdcApiService} from './api/edc-api.service'; +import {DataOffer} from './models/data-offer'; +import {NotificationService} from './notification.service'; + +@Injectable({providedIn: 'root'}) +export class ContractNegotiationService { + runningContractOffers = new Set(); + doneContractOffers = new Set(); + + constructor( + private edcApiService: EdcApiService, + private notificationService: NotificationService, + private confirmationDialog: MatDialog, + private translateService: TranslateService, + ) { + if (!environment.production) { + // Test data on local dev + this.runningContractOffers.add( + 'offer:6db25605-cd8c-4528-ade2-6a90349d06ac', + ); + this.doneContractOffers.add('offer:6db25605-cd8c-4528-ade1-6a90389d06ac'); + } + } + + negotiationState( + contractOffer: UiContractOffer, + ): 'ready' | 'negotiating' | 'negotiated' { + const isNegotiated = this.isNegotiated(contractOffer); + + if (isNegotiated) { + return 'negotiated'; + } + + const isBusy = this.isBusy(contractOffer); + return isBusy ? 'negotiating' : 'ready'; + } + + isBusy(contractOffer: UiContractOffer) { + return this.runningContractOffers.has(contractOffer.contractOfferId); + } + + isNegotiated(contractOffer: UiContractOffer) { + return this.doneContractOffers.has(contractOffer.contractOfferId); + } + + negotiate(dataOffer: DataOffer, contractOffer: UiContractOffer) { + const contractOfferId = contractOffer.contractOfferId; + const initiateRequest: ContractNegotiationRequest = { + counterPartyParticipantId: dataOffer.participantId, + counterPartyAddress: dataOffer.endpoint!, + assetId: dataOffer.asset.assetId, + contractOfferId, + policyJsonLd: contractOffer.policy.policyJsonLd, + }; + + this.confirmationDialog + .open(InitiateNegotiationConfirmTosDialogComponent, {maxWidth: '30rem'}) + .afterClosed() + .subscribe((result) => { + if (result) { + this.startNegotiation(initiateRequest); + } + }); + } + + private startNegotiation(initiateRequest: ContractNegotiationRequest) { + const contractOfferId = initiateRequest.contractOfferId; + + this.initiateNegotiation(initiateRequest) + .pipe( + tap(() => this.onStarted(contractOfferId)), + switchMap((negotiation) => + interval(1000).pipe( + switchMap(() => + this.fetchNegotiation(negotiation.contractNegotiationId).pipe( + catchError(() => EMPTY), + ), + ), + ), + ), + filter( + (negotiation) => negotiation.state.simplifiedState != 'IN_PROGRESS', + ), + first(), + ) + .subscribe({ + next: (negotiation) => { + if (negotiation.state.simplifiedState === 'AGREED') { + this.onSuccess(contractOfferId); + } else { + this.onFailureNegotiating(contractOfferId); + } + }, + error: () => this.onFailureStarting(), + }); + } + + private onFailureStarting() { + const err = this.translateService.instant('notification.starting_neg'); + this.notificationService.showError(err); + } + + private onStarted(contractOfferId: string) { + this.runningContractOffers.add(contractOfferId); + } + + private onFailureNegotiating(contractOfferId: string) { + this.runningContractOffers.delete(contractOfferId); + const err2 = this.translateService.instant('notification.negotiation'); + this.notificationService.showError(err2); + } + + private onSuccess(contractOfferId: string) { + this.runningContractOffers.delete(contractOfferId); + this.doneContractOffers.add(contractOfferId); + const mes = this.translateService.instant('notification.compl_negotiation'); + this.notificationService.showError(mes); + } + + private initiateNegotiation( + initiateDto: ContractNegotiationRequest, + ): Observable { + return this.edcApiService.initiateContractNegotiation(initiateDto); + } + + private fetchNegotiation(id: string): Observable { + return this.edcApiService.getContractNegotiation(id); + } +} diff --git a/connector-ui/src/app/core/services/data-address-mapper.ts b/connector-ui/src/app/core/services/data-address-mapper.ts new file mode 100644 index 000000000..32f7ca189 --- /dev/null +++ b/connector-ui/src/app/core/services/data-address-mapper.ts @@ -0,0 +1,23 @@ +import {Injectable} from '@angular/core'; +import {ContractAgreementTransferDialogFormValue} from '../../routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/contract-agreement-transfer-dialog-form-model'; +import {TransferDataSinkMapper} from './transfer-data-sink-mapper'; + +@Injectable({providedIn: 'root'}) +export class DataAddressMapper { + constructor(private httpRequestParamsMapper: TransferDataSinkMapper) {} + + buildDataAddressProperties( + formValue: ContractAgreementTransferDialogFormValue | undefined, + ): Record { + switch (formValue?.dataAddressType) { + case 'Custom-Data-Address-Json': + return JSON.parse(formValue.dataDestination?.trim()!!); + case 'Http': + return this.httpRequestParamsMapper.buildHttpDataAddress(formValue); + default: + throw new Error( + `Invalid Data Address Type ${formValue?.dataAddressType}`, + ); + } + } +} diff --git a/connector-ui/src/app/core/services/favicon.service.ts b/connector-ui/src/app/core/services/favicon.service.ts new file mode 100644 index 000000000..327c7191b --- /dev/null +++ b/connector-ui/src/app/core/services/favicon.service.ts @@ -0,0 +1,26 @@ +import {DOCUMENT} from '@angular/common'; +import {Inject, Injectable} from '@angular/core'; + +@Injectable({ + providedIn: 'root', +}) +export class FaviconService { + constructor(@Inject(DOCUMENT) private document: Document) {} + + setFavicon(src: string) { + const link = this.findOrCreateLink('icon'); + link.setAttribute('href', src); + } + + private findOrCreateLink(rel: string): HTMLLinkElement { + let link = document.querySelector(`link[rel~='${rel}']`) as HTMLLinkElement; + if (!link) { + link = this.document.createElement('link'); + link.setAttribute('rel', rel); + + const head = this.document.querySelector('head') as HTMLHeadElement; + head.appendChild(link); + } + return link; + } +} diff --git a/connector-ui/src/app/core/services/html-sanitizer.ts b/connector-ui/src/app/core/services/html-sanitizer.ts new file mode 100644 index 000000000..69abbdaca --- /dev/null +++ b/connector-ui/src/app/core/services/html-sanitizer.ts @@ -0,0 +1,19 @@ +import {Injectable} from '@angular/core'; +import {addHook, sanitize} from 'isomorphic-dompurify'; + +@Injectable({providedIn: 'root'}) +export class HtmlSanitizer { + constructor() { + addHook('afterSanitizeAttributes', function (node) { + // https://developer.chrome.com/docs/lighthouse/best-practices/external-anchors-use-rel-noopener/ + if ('target' in node) { + node.setAttribute('target', '_blank'); + node.setAttribute('rel', 'noopener noreferrer'); + } + }); + } + + sanitize(html: string) { + return sanitize(html); + } +} diff --git a/connector-ui/src/app/core/services/login-polling.service.ts b/connector-ui/src/app/core/services/login-polling.service.ts new file mode 100644 index 000000000..259d1928a --- /dev/null +++ b/connector-ui/src/app/core/services/login-polling.service.ts @@ -0,0 +1,38 @@ +import {HttpErrorResponse} from '@angular/common/http'; +import {Injectable, OnDestroy} from '@angular/core'; +import {EMPTY, Observable, Subject, interval} from 'rxjs'; +import {catchError, switchMap, takeUntil} from 'rxjs/operators'; +import {EdcApiService} from './api/edc-api.service'; + +@Injectable({providedIn: 'root'}) +export class LoginPollingService implements OnDestroy { + private pollInterval = 1000 * 30; + private ngOnDestroy$ = new Subject(); + + constructor(private edcApiService: EdcApiService) {} + + startPolling(): void { + interval(this.pollInterval) + .pipe( + switchMap(() => this.pollLogin()), + takeUntil(this.ngOnDestroy$), + ) + .subscribe(); + } + + private pollLogin(): Observable { + return this.edcApiService.getAssetPage().pipe( + catchError((err) => { + if (!(err instanceof HttpErrorResponse) || err.status !== 401) { + console.warn('Error while polling for session', err); + } + return EMPTY; + }), + ); + } + + ngOnDestroy() { + this.ngOnDestroy$.next(null); + this.ngOnDestroy$.complete(); + } +} diff --git a/connector-ui/src/app/core/services/mailto-link-builder.spec.ts b/connector-ui/src/app/core/services/mailto-link-builder.spec.ts new file mode 100644 index 000000000..02907ce20 --- /dev/null +++ b/connector-ui/src/app/core/services/mailto-link-builder.spec.ts @@ -0,0 +1,24 @@ +import {MailtoLinkBuilder} from './mailto-link-builder'; + +describe('mailto-link-builder', () => { + const builder = new MailtoLinkBuilder(); + + it('build link with all fields', () => { + expect( + builder.buildMailtoUrl( + 'test@test.com', + 'subject abc', + 'body', + 'cc', + 'bcc', + ), + ).toEqual( + 'mailto:test@test.com?subject=subject%20abc&body=body&cc=cc&bcc=bcc', + ); + }); + it('build link with only email', () => { + expect(builder.buildMailtoUrl('test@test.com')).toEqual( + 'mailto:test@test.com', + ); + }); +}); diff --git a/connector-ui/src/app/core/services/mailto-link-builder.ts b/connector-ui/src/app/core/services/mailto-link-builder.ts new file mode 100644 index 000000000..52795d85d --- /dev/null +++ b/connector-ui/src/app/core/services/mailto-link-builder.ts @@ -0,0 +1,29 @@ +import {Injectable} from '@angular/core'; +import {removeUndefinedValues} from '../utils/record-utils'; + +@Injectable({providedIn: 'root'}) +export class MailtoLinkBuilder { + private readonly MAILTO = 'mailto:'; + + buildMailtoUrl( + email: string, + subject?: string, + body?: string, + cc?: string, + bcc?: string, + ): string { + const queryParams = new URLSearchParams( + removeUndefinedValues({ + subject, + body, + cc, + bcc, + }), + ); + // URLSearchParams replaces spaces with '+', so we need to replace them with '%20' for the mail scenario + const queryParamsStr = queryParams.toString().replaceAll('+', '%20'); + const queryStr = queryParamsStr ? `?${queryParamsStr}` : ''; + + return `${this.MAILTO}${email}${queryStr}`; + } +} diff --git a/connector-ui/src/app/core/services/markdown-converter.ts b/connector-ui/src/app/core/services/markdown-converter.ts new file mode 100644 index 000000000..f064122ae --- /dev/null +++ b/connector-ui/src/app/core/services/markdown-converter.ts @@ -0,0 +1,19 @@ +import {Injectable} from '@angular/core'; +import {marked} from 'marked'; + +@Injectable({providedIn: 'root'}) +export class MarkdownConverter { + constructor() { + const renderer = new marked.Renderer(); + + renderer.image = function (href, title, alt) { + return `${alt}`; + }; + + marked.use({renderer}); + } + + toHtml(markdown: string) { + return marked.parse(markdown).toString(); + } +} diff --git a/connector-ui/src/app/core/services/models/asset-properties.ts b/connector-ui/src/app/core/services/models/asset-properties.ts new file mode 100644 index 000000000..c205e29a0 --- /dev/null +++ b/connector-ui/src/app/core/services/models/asset-properties.ts @@ -0,0 +1,5 @@ +const EDC = 'https://w3id.org/edc/v0.0.1/ns/'; + +export const AssetProperty = { + id: `${EDC}id`, +}; diff --git a/connector-ui/src/app/core/services/models/contract-offer.ts b/connector-ui/src/app/core/services/models/contract-offer.ts new file mode 100644 index 000000000..52d0a570b --- /dev/null +++ b/connector-ui/src/app/core/services/models/contract-offer.ts @@ -0,0 +1,6 @@ +import {UiContractOffer} from '@sovity.de/edc-client'; +import {PropertyGridField} from '../../../shared/common/property-grid/property-grid-field'; + +export type ContractOffer = UiContractOffer & { + properties: PropertyGridField[]; +}; diff --git a/connector-ui/src/app/core/services/models/data-address-properties.ts b/connector-ui/src/app/core/services/models/data-address-properties.ts new file mode 100644 index 000000000..3804e08f1 --- /dev/null +++ b/connector-ui/src/app/core/services/models/data-address-properties.ts @@ -0,0 +1,20 @@ +const EDC = 'https://w3id.org/edc/v0.0.1/ns/'; + +export const DataAddressProperty = { + authCode: `${EDC}authCode`, + authKey: `${EDC}authKey`, + baseUrl: `${EDC}baseUrl`, + body: `${EDC}body`, + header: `header`, + contentType: `${EDC}contentType`, + mediaType: `${EDC}mediaType`, + method: `${EDC}method`, + pathSegments: `${EDC}pathSegments`, + proxyBody: `${EDC}proxyBody`, + proxyMethod: `${EDC}proxyMethod`, + proxyPath: `${EDC}proxyPath`, + proxyQueryParams: `${EDC}proxyQueryParams`, + queryParams: `${EDC}queryParams`, + secretName: `${EDC}secretName`, + type: `${EDC}type`, +}; diff --git a/connector-ui/src/app/core/services/models/data-offer.ts b/connector-ui/src/app/core/services/models/data-offer.ts new file mode 100644 index 000000000..f395a31c3 --- /dev/null +++ b/connector-ui/src/app/core/services/models/data-offer.ts @@ -0,0 +1,11 @@ +import {UiDataOffer} from '@sovity.de/edc-client'; +import {ContractOffer} from './contract-offer'; +import {UiAssetMapped} from './ui-asset-mapped'; + +/** + * Contract Offer (UI Dto) + */ +export type DataOffer = Omit & { + asset: UiAssetMapped; + contractOffers: ContractOffer[]; +}; diff --git a/connector-ui/src/app/core/services/models/fetched.ts b/connector-ui/src/app/core/services/models/fetched.ts new file mode 100644 index 000000000..73a72ef02 --- /dev/null +++ b/connector-ui/src/app/core/services/models/fetched.ts @@ -0,0 +1,199 @@ +import {HttpErrorResponse} from '@angular/common/http'; +import {OperatorFunction, concat, of} from 'rxjs'; +import {catchError, map} from 'rxjs/operators'; + +/** + * Wraps potentially fetched value with state and potential errors. + */ +export class Fetched { + constructor( + public state: FetchedState, + public dataOrUndefined: T | undefined, + public errorOrUndefined: FetchError | undefined, + ) {} + + get isLoading(): boolean { + return this.state === 'loading' || this.state === 'not-loaded'; + } + + get isReady(): boolean { + return this.state === 'ready'; + } + + get isError(): boolean { + return this.state === 'error'; + } + + get data(): T { + if (this.state !== 'ready') { + throw new Error(`Fetched not ready!`); + } + return this.dataOrUndefined!; + } + + get error(): FetchError { + if (this.state !== 'error') { + throw new Error(`Fetched not in error state!`); + } + return this.errorOrUndefined!; + } + + /** + * Map data if present but keep state + * @param mapFn mapping fn + */ + map(mapFn: (t: T) => R): Fetched { + return new Fetched( + this.state, + this.isReady ? mapFn(this.data) : undefined, + this.errorOrUndefined, + ); + } + + /** + * Get data or fall back to default value + * @param defaultValue value to fall back to if no data is present + */ + orElse(defaultValue: R): T | R { + return this.isReady ? this.data : defaultValue; + } + + /** + * Run function with data if data is ready + * @param fn function + */ + ifReady(fn: (t: T) => void): void { + if (this.isReady) { + fn(this.data); + } + } + + /** + * Run function with data if data is ready or return default value + * @param fn function + * @param defaultValue defaultValue + */ + ifReadyElse(fn: (t: T) => R, defaultValue: R): R { + if (this.isReady) { + return fn(this.data); + } + return defaultValue; + } + + /** + * Map entire Fetched to a different type. + * + * @param opts functions matching the different possible states of Fetched. All need to return the return type. + * @return value of type R + */ + match(opts: { + ifOk: (value: T) => R; + ifError: (error: FetchError) => R; + ifLoading: () => R; + }): R { + if (this.isError) { + return opts.ifError(this.error); + } else if (this.isReady) { + return opts.ifOk(this.data); + } else { + return opts.ifLoading(); + } + } + + static empty(): Fetched { + return new Fetched('not-loaded', undefined, undefined); + } + + static loading(): Fetched { + return new Fetched('loading', undefined, undefined); + } + + static ready(data: T): Fetched { + return new Fetched('ready', data, undefined); + } + + static error(failureMessage: string, error: any): Fetched { + return Fetched.error2(mapFetchError(failureMessage, error)); + } + + static error2(fetchError: FetchError): Fetched { + return new Fetched('error', undefined, fetchError); + } + + /** + * RXJS Operator: Wraps request into multiple emissions that track state. + * + * @param opts adit + */ + static wrap(opts: { + failureMessage: string; + }): OperatorFunction> { + return (obs) => + concat( + of(Fetched.loading()), + obs.pipe( + map((data) => Fetched.ready(data)), + catchError((err) => { + console.error(opts.failureMessage, err); + return of(Fetched.error(opts.failureMessage, err)); + }), + ), + ); + } + + /** + * RXJS Operator: Map fetched value + * + * @param mapFn mapping fn applied to data if present + */ + static map( + mapFn: (value: T) => R, + ): OperatorFunction, Fetched> { + return (obs) => obs.pipe(map((fetched) => fetched.map(mapFn))); + } + + /** + * RXJS Operator: Get value or fall back to default value + * + * @param defaultValue value to fall back to if no data is present + */ + static orElse(defaultValue: R): OperatorFunction, T | R> { + return (obs) => obs.pipe(map((fetched) => fetched.orElse(defaultValue))); + } +} + +/** + * States a potentially fetched value can assume. + */ +export type FetchedState = 'not-loaded' | 'loading' | 'ready' | 'error'; + +/** + * Errors are paired with a user-friendly action-specific failure messages + * since stack traces might have useless technical error messages. + */ +export interface FetchError { + type: 'error' | '401'; + failureMessage: string; + failureIcon: string; + error: any; +} + +export function mapFetchError(failureMessage: string, error: any): FetchError { + if (error instanceof HttpErrorResponse) { + if (error.status === 401) { + return { + type: '401', + failureIcon: 'refresh', + failureMessage: 'Session most likely expired. Please refresh browser.', + error, + }; + } + } + + return { + type: 'error', + failureIcon: 'error', + failureMessage, + error, + }; +} diff --git a/connector-ui/src/app/core/services/models/http-data-address-params.ts b/connector-ui/src/app/core/services/models/http-data-address-params.ts new file mode 100644 index 000000000..d03c36ee4 --- /dev/null +++ b/connector-ui/src/app/core/services/models/http-data-address-params.ts @@ -0,0 +1,36 @@ +export interface HttpDataAddressParams { + /** + * (Base) URL of the request + */ + baseUrl: string; + + /** + * Http-method + */ + method: string | null; + + /** + * Header-Name ("Authorization"), where the secrets are passed into + */ + authHeaderName: string | null; + + /** + * Header-Value ("Bearer ...") + */ + authHeaderValue: string | null; + + /** + * Secret referencing API Key + */ + authHeaderSecretName: string | null; + + /** + * Additional headers to be sent + */ + headers: Record; + + /** + * Query Parameters + */ + queryParams: string | null; +} diff --git a/connector-ui/src/app/core/services/models/multi-fetched.ts b/connector-ui/src/app/core/services/models/multi-fetched.ts new file mode 100644 index 000000000..be788c34a --- /dev/null +++ b/connector-ui/src/app/core/services/models/multi-fetched.ts @@ -0,0 +1,58 @@ +import {Fetched} from './fetched'; + +/** + * Merges status for many similar fetches. + */ +export class MultiFetched { + constructor( + public numTotal: number, + public numDone: number, + public numOk: number, + public results: Fetched[], + ) {} + + get numFailed(): number { + return this.numDone - this.numOk; + } + + get isNothingReady(): boolean { + return !this.numOk; + } + + get isDone(): boolean { + return this.numDone === this.numTotal; + } + + get isSomeReady(): boolean { + return this.numOk > 0; + } + + get isSomeFailed(): boolean { + return this.numOk != this.numDone; + } + + get isAllFailed(): boolean { + return this.numTotal > 0 && this.isDone && this.isNothingReady; + } + + get data(): T[] { + return this.results.filter((it) => it.isReady).map((it) => it.data); + } + + /** + * Aggregate multiple fetched results into common tracking + * + * @param results fetched reuslts + */ + static aggregate(results: Fetched[]): MultiFetched { + const numTotal = results.length; + const numDone = results.filter((it) => !it.isLoading).length; + const numOk = results.filter((it) => it.isReady).length; + + return new MultiFetched(numTotal, numDone, numOk, results); + } + + static empty(): MultiFetched { + return this.aggregate([]); + } +} diff --git a/connector-ui/src/app/core/services/models/nav-item-group.ts b/connector-ui/src/app/core/services/models/nav-item-group.ts new file mode 100644 index 000000000..9d103c7a8 --- /dev/null +++ b/connector-ui/src/app/core/services/models/nav-item-group.ts @@ -0,0 +1,6 @@ +import {NavItem} from './nav-item'; + +export interface NavItemGroup { + title?: string; + items: NavItem[]; +} diff --git a/connector-ui/src/app/core/services/models/nav-item.ts b/connector-ui/src/app/core/services/models/nav-item.ts new file mode 100644 index 000000000..8cacc74d6 --- /dev/null +++ b/connector-ui/src/app/core/services/models/nav-item.ts @@ -0,0 +1,8 @@ +import {EdcUiFeature} from '../../config/profiles/edc-ui-feature'; + +export interface NavItem { + path: string; + title: string; + icon: string; + requiresFeature?: EdcUiFeature; +} diff --git a/connector-ui/src/app/core/services/models/ui-asset-mapped.ts b/connector-ui/src/app/core/services/models/ui-asset-mapped.ts new file mode 100644 index 000000000..3a22b72be --- /dev/null +++ b/connector-ui/src/app/core/services/models/ui-asset-mapped.ts @@ -0,0 +1,35 @@ +import {UiAsset} from '@sovity.de/edc-client'; +import {DataCategorySelectItem} from '../../../shared/form-elements/data-category-select/data-category-select-item'; +import {DataSubcategorySelectItem} from '../../../shared/form-elements/data-subcategory-select/data-subcategory-select-item'; +import {LanguageSelectItem} from '../../../shared/form-elements/language-select/language-select-item'; +import {TransportModeSelectItem} from '../../../shared/form-elements/transport-mode-select/transport-mode-select-item'; + +/** + * UiAsset with replaced fixed vocabulary items. + * + * This exists, because certain metadata has labels which are added in the UI, e.g. language. + */ +export type UiAssetMapped = Omit< + UiAsset, + 'language' | 'dataCategory' | 'dataSubcategory' | 'transportMode' +> & { + connectorEndpoint: string; + + language: LanguageSelectItem | null; + + // MDS Specific + dataCategory: DataCategorySelectItem | null; + dataSubcategory: DataSubcategorySelectItem | null; + transportMode: TransportModeSelectItem | null; + + // Unhandled Additional Properties + customJsonProperties: AdditionalAssetProperty[]; + customJsonLdProperties: AdditionalAssetProperty[]; + privateCustomJsonProperties: AdditionalAssetProperty[]; + privateCustomJsonLdProperties: AdditionalAssetProperty[]; +}; + +export interface AdditionalAssetProperty { + key: string; + value: string; +} diff --git a/connector-ui/src/app/core/services/nav-items-builder.ts b/connector-ui/src/app/core/services/nav-items-builder.ts new file mode 100644 index 000000000..f25256e37 --- /dev/null +++ b/connector-ui/src/app/core/services/nav-items-builder.ts @@ -0,0 +1,92 @@ +import {Injectable} from '@angular/core'; +import {routes} from 'src/app/routes/connector-ui/connector-ui-routing.module'; +import {ActiveFeatureSet} from '../config/active-feature-set'; +import {NavItemGroup} from './models/nav-item-group'; + +/** + * Builds NavItems from Angular routes + */ +@Injectable({ + providedIn: 'root', +}) +export class NavItemsBuilder { + private navItemGroups: NavItemGroup[] = [ + { + items: [ + {path: 'dashboard', icon: 'data_usage', title: 'Dashboard'}, + { + path: 'catalog-browser', + icon: 'sim_card', + title: 'catalog_browser_page.title', + }, + { + path: 'contracts', + icon: 'assignment_turned_in', + title: 'contract_agreement_page.title', + }, + { + path: 'transfer-history', + icon: 'assignment', + title: 'transfer_history_page.title', + }, + ], + }, + { + title: 'Provide', + items: [ + { + path: 'create-asset', + icon: 'post_add', + title: 'create_data_offer_page.title', + }, + {path: 'my-assets', icon: 'upload', title: 'asset_list_page.title'}, + { + path: 'policies', + icon: 'policy', + title: 'policy_definition_page.title', + }, + { + path: 'data-offers', + icon: 'rule', + title: 'contract_definition_page.title', + }, + ], + }, + + { + items: [ + { + path: 'logout', + icon: 'logout', + title: 'logout_page.title', + requiresFeature: 'logout-button', + }, + ], + }, + ]; + + constructor(private activeFeatureSet: ActiveFeatureSet) {} + + buildNavItemGroups(): NavItemGroup[] { + const allNavItemRoutesExist = this.navItemGroups + .flatMap((navItemGroup) => + navItemGroup.items.map((navItem) => navItem.path), + ) + .every((path) => routes.find((route) => route.path === path)); + + if (!allNavItemRoutesExist) { + throw new Error('All nav item routes must exist in the routes array'); + } + + return this.navItemGroups + .map((group) => ({ + ...group, + items: group.items.filter((item) => { + return item.requiresFeature + ? this.activeFeatureSet.has(item.requiresFeature) + : true; + }), + })) + .filter((group) => group.items.length > 0); + } +} diff --git a/connector-ui/src/app/core/services/notification.service.ts b/connector-ui/src/app/core/services/notification.service.ts new file mode 100644 index 000000000..7af76bd1f --- /dev/null +++ b/connector-ui/src/app/core/services/notification.service.ts @@ -0,0 +1,58 @@ +import {Injectable} from '@angular/core'; +import { + MatSnackBar, + MatSnackBarConfig, + MatSnackBarDismiss, +} from '@angular/material/snack-bar'; +import {Observable} from 'rxjs'; + +@Injectable({ + providedIn: 'root', +}) +export class NotificationService { + constructor(private snackBar: MatSnackBar) {} + + /** + * Shows a snackbar message with a particular text + * @param message The text to display + * @param action A string specifying the text on an action button. If left out, no action button is shown. + * If left out, and onAction is specified, "Done" is used as default. + * @param onAction A callback that is invoked when the action button is clicked. + */ + public showInfo( + message: string, + action?: string, + onAction?: () => any, + ): Observable { + if (!action && onAction) { + action = 'Done'; + } + const config: MatSnackBarConfig = { + duration: onAction ? 5000 : 3000, // no auto-cancel if an action was specified + verticalPosition: 'top', + politeness: 'polite', + horizontalPosition: 'center', + panelClass: ['snackbar-info-style'], //see styles.scss + }; + const ref = this.snackBar.open(message, action, config); + + if (onAction) { + ref.onAction().subscribe(() => onAction()); + } + + return ref.afterDismissed(); + } + + public showError(message: string): Observable { + const config: MatSnackBarConfig = { + duration: 5000, // no auto-cancel if an action was specified + verticalPosition: 'top', + politeness: 'assertive', + horizontalPosition: 'center', + panelClass: ['snackbar-error-style'], //see styles.scss + }; + + const ref = this.snackBar.open(message, undefined, config); + return ref.afterDismissed(); + } +} diff --git a/connector-ui/src/app/core/services/page-title-strategy.ts b/connector-ui/src/app/core/services/page-title-strategy.ts new file mode 100644 index 000000000..be687093d --- /dev/null +++ b/connector-ui/src/app/core/services/page-title-strategy.ts @@ -0,0 +1,42 @@ +import {Injectable} from '@angular/core'; +import {Title} from '@angular/platform-browser'; +import { + ActivatedRouteSnapshot, + RouterStateSnapshot, + TitleStrategy, +} from '@angular/router'; +import {Subject, switchMap} from 'rxjs'; +import {TranslateService} from '@ngx-translate/core'; + +@Injectable() +export class CustomPageTitleStrategy extends TitleStrategy { + rawTitle$ = new Subject(); + + constructor( + private translateService: TranslateService, + private title: Title, + ) { + super(); + this.rawTitle$ + .pipe(switchMap((title) => this.translateService.get(title))) + .subscribe((title) => this.title.setTitle(title)); + } + + override updateTitle(snapshot: RouterStateSnapshot): void { + const data = this.getRouteDataRecursively(snapshot); + const titleUntranslated = data.title ?? 'EDC Connector'; + this.rawTitle$.next(titleUntranslated); + } + + private getRouteDataRecursively( + routerStateSnapshot: RouterStateSnapshot, + ): any { + let snapshot: ActivatedRouteSnapshot | null = routerStateSnapshot.root; + let data = {}; + while (snapshot) { + data = {...data, ...snapshot.data}; + snapshot = snapshot.firstChild; + } + return data; + } +} diff --git a/connector-ui/src/app/core/services/participant-id-localization.ts b/connector-ui/src/app/core/services/participant-id-localization.ts new file mode 100644 index 000000000..ad0e2f313 --- /dev/null +++ b/connector-ui/src/app/core/services/participant-id-localization.ts @@ -0,0 +1,33 @@ +import {Inject, Injectable} from '@angular/core'; +import {TranslateService} from '@ngx-translate/core'; +import {APP_CONFIG, AppConfig} from '../config/app-config'; + +@Injectable({providedIn: 'root'}) +export class ParticipantIdLocalization { + constructor( + @Inject(APP_CONFIG) private config: AppConfig, + private translateService: TranslateService, + ) { + this.translateService + .stream([ + 'component_library.connector_id', + 'component_library.connector_id_plural', + 'component_library.participant_id', + 'component_library.participant_id_plural', + ]) + .subscribe((translations) => { + this.participantId = this.mds + ? translations['component_library.connector_id'] + : translations['component_library.participant_id']; + this.participantIdPlural = this.mds + ? translations['component_library.connector_id_plural'] + : translations['component_library.participant_id_plural']; + }); + } + private mds = this.config.features.has('mds-connector-id'); + participantId = ''; // init, will be updated by translateService + participantIdPlural = ''; + participantIdPlaceholder = this.mds + ? 'MDSL1234XX.C1234XX' + : 'other-connector-participant-id'; +} diff --git a/connector-ui/src/app/core/services/query-params-mapper.ts b/connector-ui/src/app/core/services/query-params-mapper.ts new file mode 100644 index 000000000..54377fbba --- /dev/null +++ b/connector-ui/src/app/core/services/query-params-mapper.ts @@ -0,0 +1,43 @@ +import {Injectable} from '@angular/core'; +import {HttpDatasourceQueryParamFormValue} from '../../shared/business/edit-asset-form/form/model/http-datasource-query-param-form-model'; +import { + everythingAfter, + everythingBefore, + trimOrEmpty, +} from '../utils/string-utils'; + +@Injectable({providedIn: 'root'}) +export class QueryParamsMapper { + getBaseUrlWithoutQueryParams(rawUrl: string): string { + return everythingBefore('?', trimOrEmpty(rawUrl)); + } + + /** + * Merges query params from the base URL with the additional ones. + */ + getFullQueryString( + baseUrlWithQueryParams: string, + additionalQueryParams: HttpDatasourceQueryParamFormValue[], + ): string | null { + const queryParamSegments = additionalQueryParams.map((param) => + this.encodeQueryParam(param), + ); + + return [ + everythingAfter('?', trimOrEmpty(baseUrlWithQueryParams)), + ...queryParamSegments, + ] + .filter((it) => !!it) + .join('&'); + } + + private encodeQueryParam(param: HttpDatasourceQueryParamFormValue): string { + const k = encodeURIComponent(trimOrEmpty(param.paramName)); + const v = encodeURIComponent(trimOrEmpty(param.paramValue)); + return this.buildQueryParam(k, v); + } + + private buildQueryParam(name: string, value: string) { + return `${name}=${value}`; + } +} diff --git a/connector-ui/src/app/core/services/transfer-data-sink-mapper.ts b/connector-ui/src/app/core/services/transfer-data-sink-mapper.ts new file mode 100644 index 000000000..77f6f4c05 --- /dev/null +++ b/connector-ui/src/app/core/services/transfer-data-sink-mapper.ts @@ -0,0 +1,121 @@ +import {Injectable} from '@angular/core'; +import {ContractAgreementTransferDialogFormValue} from '../../routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/contract-agreement-transfer-dialog-form-model'; +import {HttpDatasourceHeaderFormValue} from '../../shared/business/edit-asset-form/form/model/http-datasource-header-form-model'; +import {getAuthFields} from '../utils/form-value-utils'; +import {mapKeys, removeNullValues} from '../utils/record-utils'; +import {DataAddressProperty} from './models/data-address-properties'; +import {HttpDataAddressParams} from './models/http-data-address-params'; +import {UiAssetMapped} from './models/ui-asset-mapped'; +import {QueryParamsMapper} from './query-params-mapper'; + +@Injectable({providedIn: 'root'}) +export class TransferDataSinkMapper { + constructor(private queryParamsMapper: QueryParamsMapper) {} + + buildHttpDataAddress( + formValue: ContractAgreementTransferDialogFormValue, + ): Record { + const params = this.buildHttpRequestParams(formValue); + return this.encodeHttpRequestParams(params); + } + + encodeHttpProxyTransferRequestProperties( + asset: UiAssetMapped, + value: ContractAgreementTransferDialogFormValue, + ): Record { + const method = value.httpProxiedMethod?.trim() ?? ''; + const pathSegments = this.queryParamsMapper.getBaseUrlWithoutQueryParams( + value.httpProxiedPath!!, + ); + const queryParams = this.queryParamsMapper.getFullQueryString( + value.httpProxiedPath!!, + value.httpProxiedQueryParams ?? [], + ); + const body = value.httpProxiedBody?.trim() || null; + const contentType = value.httpProxiedBodyContentType?.trim() || null; + + const proxyMethod = + value.showAllHttpParameterizationFields || + asset.httpDatasourceHintsProxyMethod; + const proxyPath = + value.showAllHttpParameterizationFields || + asset.httpDatasourceHintsProxyPath; + const proxyQueryParams = + value.showAllHttpParameterizationFields || + asset.httpDatasourceHintsProxyQueryParams; + const proxyBody = + value.showAllHttpParameterizationFields || + asset.httpDatasourceHintsProxyBody; + + return removeNullValues({ + [DataAddressProperty.method]: proxyMethod && method ? method : null, + [DataAddressProperty.pathSegments]: proxyPath ? pathSegments : null, + [DataAddressProperty.queryParams]: proxyQueryParams ? queryParams : null, + [DataAddressProperty.body]: proxyBody ? body : null, + [DataAddressProperty.contentType]: proxyBody ? contentType : null, + [DataAddressProperty.mediaType]: proxyBody ? contentType : null, + }); + } + + encodeHttpRequestParams( + httpRequestParams: HttpDataAddressParams, + ): Record { + const props: Record = { + [DataAddressProperty.type]: 'HttpData', + [DataAddressProperty.baseUrl]: httpRequestParams.baseUrl, + [DataAddressProperty.method]: httpRequestParams.method, + [DataAddressProperty.authKey]: httpRequestParams.authHeaderName, + [DataAddressProperty.authCode]: httpRequestParams.authHeaderValue, + [DataAddressProperty.secretName]: httpRequestParams.authHeaderSecretName, + [DataAddressProperty.queryParams]: httpRequestParams.queryParams, + ...mapKeys(httpRequestParams.headers, (k) => { + if (k.toLowerCase() === 'content-type') { + // this is required because the EDC sends the Content-Type header if and only if provided using the special field "contentType" + return DataAddressProperty.contentType; + } + return `${DataAddressProperty.header}:${k}`; + }), + }; + return removeNullValues(props); + } + + buildHttpRequestParams( + formValue: ContractAgreementTransferDialogFormValue, + ): HttpDataAddressParams { + const {authHeaderName, authHeaderValue, authHeaderSecretName} = + getAuthFields(formValue); + + let method = formValue?.httpMethod?.trim().toUpperCase() || null; + + const baseUrl = this.queryParamsMapper.getBaseUrlWithoutQueryParams( + formValue?.httpUrl!!, + ); + const queryParams = this.queryParamsMapper.getFullQueryString( + formValue?.httpUrl!!, + [], + ); + + return { + baseUrl: baseUrl!!, + method, + authHeaderName, + authHeaderValue, + authHeaderSecretName, + queryParams, + headers: this.buildHttpHeaders(formValue?.httpHeaders ?? []), + }; + } + + private buildHttpHeaders( + headers: HttpDatasourceHeaderFormValue[], + ): Record { + return Object.fromEntries( + headers + .map((header) => [ + header.headerName?.trim() || '', + header.headerValue?.trim() || '', + ]) + .filter((a) => a[0] && a[1]), + ); + } +} diff --git a/connector-ui/src/app/core/utils/angular-utils.ts b/connector-ui/src/app/core/utils/angular-utils.ts new file mode 100644 index 000000000..f529b667c --- /dev/null +++ b/connector-ui/src/app/core/utils/angular-utils.ts @@ -0,0 +1,35 @@ +import {Input, SimpleChanges} from '@angular/core'; +import {Subject} from 'rxjs'; + +/** + * A type-safe version of {@link SimpleChanges}. + * + * Does not contain all {@link Input}s, but only simple fields. + */ +export type SimpleChangesTyped< + Component extends object, + Props = ExcludeFunctions, +> = { + [Key in keyof Props]: SimpleChangeTyped; +}; + +export type SimpleChangeTyped = { + previousValue: T; + currentValue: T; + firstChange: boolean; + isFirstChange(): boolean; +}; + +type MarkFunctionPropertyNames = { + [Key in keyof Component]: Component[Key] extends Function | Subject + ? never + : Key; +}; + +type ExcludeFunctionPropertyNames = + MarkFunctionPropertyNames[keyof T]; + +type ExcludeFunctions = Pick< + T, + ExcludeFunctionPropertyNames +>; diff --git a/connector-ui/src/app/core/utils/array-utils.spec.ts b/connector-ui/src/app/core/utils/array-utils.spec.ts new file mode 100644 index 000000000..d7d6fc6af --- /dev/null +++ b/connector-ui/src/app/core/utils/array-utils.spec.ts @@ -0,0 +1,13 @@ +import {removeOnce} from './array-utils'; + +describe('array-utils', () => { + it('should work on empty list', () => { + expect(removeOnce([], 'idk')).toEqual([]); + }); + it('should remove item', () => { + expect(removeOnce([1], 1)).toEqual([]); + }); + it('should remove only single item', () => { + expect(removeOnce([1, 2, 2, 3], 2)).toEqual([1, 2, 3]); + }); +}); diff --git a/connector-ui/src/app/core/utils/array-utils.ts b/connector-ui/src/app/core/utils/array-utils.ts new file mode 100644 index 000000000..6cd7b33e2 --- /dev/null +++ b/connector-ui/src/app/core/utils/array-utils.ts @@ -0,0 +1,21 @@ +/** + * Remove item once from list. + * + * Use this over .filter(...) to remove items on user interactions + * to prevent one click from removing many items. + * + * Returns copy. + */ +export function removeOnce(list: T[], item: T): T[] { + const index = list.indexOf(item); + if (index >= 0) { + const copy = [...list]; + copy.splice(index, 1); + return copy; + } + return list; +} + +export function filterNonNull(array: (T | null | undefined)[]): T[] { + return array.filter((it) => it != null) as T[]; +} diff --git a/connector-ui/src/app/core/utils/date-utils.ts b/connector-ui/src/app/core/utils/date-utils.ts new file mode 100644 index 000000000..e4a7a082b --- /dev/null +++ b/connector-ui/src/app/core/utils/date-utils.ts @@ -0,0 +1,70 @@ +import {addDays, subDays} from 'date-fns'; +import {format} from 'date-fns-tz'; + +/** + * Takes the year/month/day information of a local date and creates a new Date object from it. + * Hour offset context is removed. + * Can be used to ensure dates are displayed identically across different timezones when stringified in JSON payloads. + * @param date date to convert + */ +export function toGmtZeroHourDate(date: Date): Date { + return new Date(format(date, 'yyyy-MM-dd')); +} + +export function isMidnightInCurrentTz(date: Date): boolean { + return format(date, 'HH:mm:ss') === '00:00:00'; +} + +/** + * Helper for dealing with the problem: + * - Our API does date comparisons based on Date + Time + TZ + * - Our UI tries to simplify it to "select full days only" + * + * Here we try to reverse the ISO UTC DateTime String to a day, while considering different TZs: + * - We accept only midnight in the local tz + * - If the date is used for an upper bound, we subtract a day + */ +export function truncateToLocalTzDay( + date: Date, + isUpperBound: boolean, +): string { + date = truncateToLocalTzDayRaw(date, isUpperBound); + + if (isMidnightInCurrentTz(date)) { + return format(date, 'dd/MM/yyyy'); + } + + // Fallback + return format(date, 'dd/MM/yyyy HH:mm:ss'); +} + +export function truncateToLocalTzDayRaw( + date: Date, + isUpperBound: boolean, +): Date { + if (isMidnightInCurrentTz(date) && isUpperBound) { + // Transform "x <= 2000-01-02 00:00:00" to "x <= 2000-01-01" + date = subDays(date, 1); + } + return date; +} + +/** + * Helper for dealing with the problem: + * - Our API does date comparisons based on Date + Time + TZ + * - Our UI tries to simplify it to "select full days only" + * + * Here we take a local tz "day" and convert it to an ISO UTC DateTime String: + * - If the date is used for an upper bound, we add a day + */ +export function localTzDayToIsoString( + date: Date, + isUpperBound: boolean, +): string { + if (isUpperBound) { + // Transform "x <= 2000-01-01" to "x <= 2000-01-02 00:00:00" + date = addDays(date, 1); + } + + return date.toISOString(); +} diff --git a/connector-ui/src/app/core/utils/form-group-utils.ts b/connector-ui/src/app/core/utils/form-group-utils.ts new file mode 100644 index 000000000..7a32efa62 --- /dev/null +++ b/connector-ui/src/app/core/utils/form-group-utils.ts @@ -0,0 +1,202 @@ +import { + AbstractControl, + FormArray, + FormControl, + FormControlStatus, + FormGroup, +} from '@angular/forms'; +import {EMPTY, Observable, concat, distinctUntilChanged} from 'rxjs'; +import {map, switchMap} from 'rxjs/operators'; + +/** + * Enables/Disables form groups controls + */ +export function switchDisabledControls( + ctrl: FormGroup, + enabledCtrlsFn: (value: T) => Record, +) { + const checkForChanges = () => { + const enabledCtrls = enabledCtrlsFn(ctrl.value); + const enabled = new Set( + Object.entries(enabledCtrls) + .filter(([_, v]) => v) + .map(([k, _]) => k as keyof T), + ); + Object.entries(ctrl.controls).forEach(([ctrlName, ctrl]) => { + const ctrlNameTyped = ctrlName as keyof T; + + const currentlyDisabled = ctrl.disabled; + const expectedDisabled = !enabled.has(ctrlNameTyped); + if (currentlyDisabled == expectedDisabled) { + return; + } + + if (expectedDisabled) { + ctrl.disable(); + } else { + ctrl.enable(); + } + }); + }; + + status$(ctrl) + .pipe( + map((status) => status != 'DISABLED'), + distinctUntilChanged(), + switchMap((enabled) => (enabled ? value$(ctrl) : EMPTY)), + ) + .subscribe(() => checkForChanges()); +} + +/** + * Enables/Disables form controls depending on selected option. + * + * Use this when a select switches entire parts of the form. + * + * Disabling the controls will disable validation. + */ +export function switchDisabledControlsByField< + T extends {[K in keyof T]: AbstractControl}, + S extends string | number | symbol, +>(opts: { + /** + * Form Group + */ + formGroup: FormGroup; + + /** + * Select / Switch Control + */ + switchCtrl: FormControl; + + /** + * Sets of fields to activate, keyed by switch value + */ + enabledControlsByValue: {[K in S]: (keyof T)[]}; +}) { + const map = new Map( + Object.entries(opts.enabledControlsByValue) as [S, (keyof T)[]][], + ); + switchDisabledControlsByField2({...opts, enabledControlsByValue: map}); +} + +/** + * Enables/Disables form controls depending on selected option. + * + * Use this when a select switches entire parts of the form. + * + * Disabling the controls will disable validation. + */ +export function switchDisabledControlsByField2< + T extends {[K in keyof T]: AbstractControl}, + S, +>(opts: { + /** + * Form Group + */ + formGroup: FormGroup; + + /** + * Select / Switch Control + */ + switchCtrl: FormControl; + + /** + * Sets of fields to activate, keyed by switch value + */ + enabledControlsByValue: Map; +}) { + const ctrl = (key: keyof T): AbstractControl => + (opts.formGroup.controls as any)[key]; + + // Collect all affected controls + const fields = [...new Set([...opts.enabledControlsByValue.values()].flat())]; + + const updateControls = () => { + // If parent form group is disabled, don't touch disabled / enabled state + if (opts.formGroup.disabled) { + return; + } + + // Enable selected controls + const keys = opts.enabledControlsByValue.get(opts.switchCtrl.value); + keys + ?.map((it) => { + return it; + }) + ?.map(ctrl) + ?.forEach((control) => { + control.enable(); + }); + + // Disable other controls + fields + .filter((it) => !keys?.includes(it)) + ?.map((it) => { + return it; + }) + .map(ctrl) + .forEach((control) => { + control.disable(); + }); + }; + + // Update now and on future value changes + value$(opts.switchCtrl).subscribe(() => updateControls()); +} + +/** + * Flatten nested Form Groups / Form Arrays into a single list of [path, control]. + * + * Method for debugging nested angular forms + * + * @param form form group (or form array) + */ +export function flattenControls( + form: FormGroup | FormArray, +): [string, AbstractControl][] { + const results: [string, AbstractControl][] = []; + + const join = (path: string, element: string) => + path ? `${path}.${element}` : `${element}`; + + const iterate = (prefix: string, fg: FormGroup | FormArray) => { + Object.entries(fg.controls).forEach(([key, ctrl]) => { + const path = join(prefix, key); + results.push([path, ctrl]); + if (ctrl instanceof FormGroup || ctrl instanceof FormArray) { + iterate(path, ctrl); + } + }); + }; + + iterate('', form); + + return results; +} + +/** + * Control's value as observable that also emits current value. + */ +export function value$(ctrl: AbstractControl): Observable { + return concat( + new Observable((obs) => { + obs.next(ctrl.value as T); + obs.complete(); + }), + ctrl.valueChanges as Observable, + ); +} + +/** + * Control's status changes as observable that also emits current status. + */ +export function status$(ctrl: AbstractControl): Observable { + return concat( + new Observable((obs) => { + obs.next(ctrl.status); + obs.complete(); + }), + ctrl.statusChanges, + ); +} diff --git a/connector-ui/src/app/core/utils/form-value-utils.ts b/connector-ui/src/app/core/utils/form-value-utils.ts new file mode 100644 index 000000000..a67c96b7f --- /dev/null +++ b/connector-ui/src/app/core/utils/form-value-utils.ts @@ -0,0 +1,29 @@ +import {ContractAgreementTransferDialogFormValue} from '../../routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/contract-agreement-transfer-dialog-form-model'; +import {AssetDatasourceFormValue} from '../../shared/business/edit-asset-form/form/model/asset-datasource-form-model'; + +export function getAuthFields( + formValue: + | AssetDatasourceFormValue + | ContractAgreementTransferDialogFormValue + | undefined, +): { + authHeaderName: string | null; + authHeaderValue: string | null; + authHeaderSecretName: string | null; +} { + let authHeaderName: string | null = null; + if (formValue?.httpAuthHeaderType !== 'None') { + authHeaderName = formValue?.httpAuthHeaderName?.trim() || null; + } + + let authHeaderValue: string | null = null; + if (authHeaderName && formValue?.httpAuthHeaderType === 'Value') { + authHeaderValue = formValue?.httpAuthHeaderValue?.trim() || null; + } + + let authHeaderSecretName: string | null = null; + if (authHeaderName && formValue?.httpAuthHeaderType === 'Vault-Secret') { + authHeaderSecretName = formValue?.httpAuthHeaderSecretName?.trim() || null; + } + return {authHeaderName, authHeaderValue, authHeaderSecretName}; +} diff --git a/connector-ui/src/app/core/utils/i18n-utils.ts b/connector-ui/src/app/core/utils/i18n-utils.ts new file mode 100644 index 000000000..6cf6899c9 --- /dev/null +++ b/connector-ui/src/app/core/utils/i18n-utils.ts @@ -0,0 +1,12 @@ +export interface AvailableLanguage { + code: string; + name: string; +} + +export const supportedLanguages: AvailableLanguage[] = [ + {code: 'en', name: 'English'}, + {code: 'de', name: 'Deutsch'}, +]; + +export const isLanguageSupported = (value: unknown): value is string => + supportedLanguages.map((it) => it.code).includes(value as string); diff --git a/connector-ui/src/app/core/utils/lazy-utils.ts b/connector-ui/src/app/core/utils/lazy-utils.ts new file mode 100644 index 000000000..12774e261 --- /dev/null +++ b/connector-ui/src/app/core/utils/lazy-utils.ts @@ -0,0 +1,21 @@ +import {TranslateService} from '@ngx-translate/core'; + +export class LazyTranslation { + private value: T | null = null; + private language: string | null = null; + constructor( + private translateService: TranslateService, + private generate: (translationService: TranslateService) => T, + ) {} + + getValue(): T { + if ( + this.value == null || + this.language !== this.translateService.currentLang + ) { + this.language = this.translateService.currentLang; + this.value = this.generate(this.translateService); + } + return this.value; + } +} diff --git a/connector-ui/src/app/core/utils/local-storage-utils.ts b/connector-ui/src/app/core/utils/local-storage-utils.ts new file mode 100644 index 000000000..0a310c4e3 --- /dev/null +++ b/connector-ui/src/app/core/utils/local-storage-utils.ts @@ -0,0 +1,36 @@ +export class LocalStorageUtils { + saveData(key: string, value: T) { + localStorage.setItem(key, JSON.stringify(value)); + } + + getData( + key: string, + defaultValue: T, + isValidValue: (value?: unknown) => value is T, + ): T { + const data = this.getDataUnsafe(key, defaultValue); + if (isValidValue(data)) { + return data; + } + return defaultValue; + } + + private getDataUnsafe(key: string, defaultValue: any): unknown { + const storedItem = localStorage.getItem(key); + + try { + return storedItem == null ? defaultValue : JSON.parse(storedItem); + } catch (e) { + console.warn('Error parsing local storage value', key, storedItem); + return defaultValue; + } + } + + removeData(key: string) { + localStorage.removeItem(key); + } + + clearData() { + localStorage.clear(); + } +} diff --git a/connector-ui/src/app/core/utils/local-stored-value.ts b/connector-ui/src/app/core/utils/local-stored-value.ts new file mode 100644 index 000000000..b2d5dafa0 --- /dev/null +++ b/connector-ui/src/app/core/utils/local-stored-value.ts @@ -0,0 +1,29 @@ +import {LocalStorageUtils} from './local-storage-utils'; + +export class LocalStoredValue { + localStorageUtils = new LocalStorageUtils(); + cachedValue: T; + + constructor( + defaultValue: T, + private key: string, + isValidValue: (value: unknown) => value is T, + ) { + this.cachedValue = this.localStorageUtils.getData( + this.key, + defaultValue, + isValidValue, + ); + } + + get value(): T { + return this.cachedValue; + } + + set value(value: T) { + if (this.cachedValue != value) { + this.cachedValue = value; + this.localStorageUtils.saveData(this.key, value); + } + } +} diff --git a/connector-ui/src/app/core/utils/map-utils.ts b/connector-ui/src/app/core/utils/map-utils.ts new file mode 100644 index 000000000..b7c6fde61 --- /dev/null +++ b/connector-ui/src/app/core/utils/map-utils.ts @@ -0,0 +1,31 @@ +/** + * Group items by key extractor + * @param array items + * @param keyExtractor key extractor + */ +export function groupedBy( + array: T[], + keyExtractor: (it: T) => K, +): Map { + const map = new Map(); + array.forEach((it) => { + const key = keyExtractor(it); + if (!map.has(key)) { + map.set(key, []); + } + map.get(key)!.push(it); + }); + return map; +} + +/** + * Create Map with entries [keyExtractor(it), it] + * @param array items + * @param keyExtractor key extractor + */ +export function associateBy( + array: T[], + keyExtractor: (it: T) => K, +): Map { + return new Map(array.map((it) => [keyExtractor(it), it])); +} diff --git a/connector-ui/src/app/core/utils/mat-dialog-utils.ts b/connector-ui/src/app/core/utils/mat-dialog-utils.ts new file mode 100644 index 000000000..d0451ac00 --- /dev/null +++ b/connector-ui/src/app/core/utils/mat-dialog-utils.ts @@ -0,0 +1,28 @@ +import {ComponentType} from '@angular/cdk/portal'; +import {MatDialog, MatDialogConfig} from '@angular/material/dialog'; +import {Observable} from 'rxjs'; + +/** + * Method for launching Angular Material Dialogs with the lifetime of the dialog being handled by a until$ observable + * + * @param dialogService MatDialog + * @param dialog ComponentType + * @param config MatDialogConfig + * @param until$ Observable that controls the lifetime of the dialog + * @template T Type of the data passed to the dialog + * @template R Type of the data returned by the dialog + * @return afterClosed Observable + */ +export function showDialogUntil( + dialogService: MatDialog, + dialog: ComponentType, + config: MatDialogConfig, + until$: Observable, +): Observable { + const ref = dialogService.open(dialog, config); + until$.subscribe({ + next: () => ref.close(), + complete: () => ref.close(), + }); + return ref.afterClosed(); +} diff --git a/connector-ui/src/app/core/utils/object-utils.ts b/connector-ui/src/app/core/utils/object-utils.ts new file mode 100644 index 000000000..2eb3731fe --- /dev/null +++ b/connector-ui/src/app/core/utils/object-utils.ts @@ -0,0 +1,33 @@ +export type Patcher = (obj: T) => Partial; + +export function patchObj(obj: T, patcher: Patcher): T { + return {...obj, ...patcher(obj)}; +} + +/** + * Create Object with entries [keyExtractor(it), valueExtractor(it)] + * @param array items + * @param keyExtractor key extractor + * @param valueExtractor value extractor + */ +export function associateAsObj( + array: T[], + keyExtractor: (it: T) => K, + valueExtractor: (it: T) => R, +): Record { + return Object.fromEntries( + array.map((it) => [keyExtractor(it), valueExtractor(it)]), + ) as Record; +} + +export function mapObjValues( + obj: Record, + mapper: (value: T, key: K) => R, +): Record { + return Object.fromEntries( + Object.entries(obj).map(([key, value]) => [ + key, + mapper(value as T, key as K), + ]), + ) as Record; +} diff --git a/connector-ui/src/app/core/utils/record-utils.ts b/connector-ui/src/app/core/utils/record-utils.ts new file mode 100644 index 000000000..50865743a --- /dev/null +++ b/connector-ui/src/app/core/utils/record-utils.ts @@ -0,0 +1,39 @@ +/** + * Remove fields with null values from Property Records due to EDC Backend expecting non-null values + * @param obj object / record + */ +export function removeNullValues( + obj: Record, +): Record { + return Object.fromEntries( + Object.entries(obj).filter(([_, v]) => v != null) as [string, string][], + ); +} + +/** + * Remove fields with undefined values from property records + * @param obj object / record + */ +export function removeUndefinedValues( + obj: Record, +): Record { + return Object.fromEntries( + Object.entries(obj).filter(([_, v]) => v != null) as [string, string][], + ); +} + +/** + * Maps keys of a given object + * @param obj object + * @param mapFn key mapper + * @return new object with keys mapped + */ +export function mapKeys< + K extends string | number | symbol, + L extends string | number | symbol, + V, +>(obj: Record, mapFn: (key: K) => L): Record { + return Object.fromEntries( + Object.entries(obj).map(([k, v]) => [mapFn(k as K), v]), + ) as Record; +} diff --git a/connector-ui/src/app/core/utils/rxjs-utils.ts b/connector-ui/src/app/core/utils/rxjs-utils.ts new file mode 100644 index 000000000..47d47339c --- /dev/null +++ b/connector-ui/src/app/core/utils/rxjs-utils.ts @@ -0,0 +1,24 @@ +import {Observable, OperatorFunction, defer, from} from 'rxjs'; +import {filter, tap} from 'rxjs/operators'; + +/** + * Simple not null filtering RXJS Operator. + * + * The trick is that it removes the "null | undefined" from the resulting stream type signature. + */ +export function filterNotNull(): OperatorFunction { + return filter((it) => it != null) as any; +} + +export function throwIfNull( + msg: string, +): OperatorFunction { + return tap((it) => { + if (it == null) { + throw new Error(msg); + } + }) as OperatorFunction; +} + +export const toObservable = (fn: () => Promise): Observable => + defer(() => from(fn())); diff --git a/connector-ui/src/app/core/utils/search-utils.ts b/connector-ui/src/app/core/utils/search-utils.ts new file mode 100644 index 000000000..2964cf796 --- /dev/null +++ b/connector-ui/src/app/core/utils/search-utils.ts @@ -0,0 +1,34 @@ +import {UiAssetMapped} from '../services/models/ui-asset-mapped'; + +/** + * Simple search that tries to find all search query words in target strings of given items + * @param items item list + * @param query search query + * @param targetsFn search targets + */ +export function search( + items: T[], + query: string | null, + targetsFn: (item: T) => (string | null)[], +): T[] { + const words = (query ?? '') + .toLowerCase() + .split(' ') + .map((it) => it.trim()) + .filter((it) => it); + + return items.filter((item) => { + const targets = targetsFn(item) + .map((it) => it?.toLowerCase()) + .filter((it) => it) as string[]; + return words.every((word) => targets.some((value) => value.includes(word))); + }); +} + +/** + * Common code for searching assets + * @param asset + */ +export function assetSearchTargets(asset: UiAssetMapped): (string | null)[] { + return [asset.assetId, asset.title, ...(asset.keywords ?? [])]; +} diff --git a/connector-ui/src/app/core/utils/set-utils.ts b/connector-ui/src/app/core/utils/set-utils.ts new file mode 100644 index 000000000..750a9d5b3 --- /dev/null +++ b/connector-ui/src/app/core/utils/set-utils.ts @@ -0,0 +1,3 @@ +export function difference(a: Set, b: Set): Set { + return new Set([...a].filter((x) => !b.has(x))); +} diff --git a/connector-ui/src/app/core/utils/string-utils.ts b/connector-ui/src/app/core/utils/string-utils.ts new file mode 100644 index 000000000..2f41b10ce --- /dev/null +++ b/connector-ui/src/app/core/utils/string-utils.ts @@ -0,0 +1,17 @@ +export function trimOrEmpty(s: string | null | undefined): string { + return s?.trim() ?? ''; +} + +export function everythingBefore(separator: string, s: string): string { + const index = s.indexOf(separator); + return index === -1 ? s : s.substring(0, index); +} + +export function everythingAfter(separator: string, s: string): string { + const index = s.indexOf(separator); + return index === -1 ? '' : s.substring(index + separator.length); +} + +export function capitalize(s: string) { + return s.charAt(0).toUpperCase() + s.slice(1); +} diff --git a/connector-ui/src/app/core/utils/type-utils.ts b/connector-ui/src/app/core/utils/type-utils.ts new file mode 100644 index 000000000..1ff506af6 --- /dev/null +++ b/connector-ui/src/app/core/utils/type-utils.ts @@ -0,0 +1,13 @@ +/** + * Similar to "keyof T" but now you can say "KeysOfType". + */ +export type KeysOfType = { + [K in keyof O]: O[K] extends T ? K : never; +}[keyof O]; + +export function isValueOfEnum( + enumObj: {[key in keyof T]: T[key]}, + value: any, +): value is T[keyof T] { + return Object.values(enumObj).includes(value); +} diff --git a/connector-ui/src/app/core/validators/data-offer-form-validators.ts b/connector-ui/src/app/core/validators/data-offer-form-validators.ts new file mode 100644 index 000000000..eba7ccb6e --- /dev/null +++ b/connector-ui/src/app/core/validators/data-offer-form-validators.ts @@ -0,0 +1,58 @@ +import {Injectable} from '@angular/core'; +import {AbstractControl, ValidationErrors, ValidatorFn} from '@angular/forms'; +import {Observable, of} from 'rxjs'; +import {catchError, map} from 'rxjs/operators'; +import {IdAvailabilityResponse} from '@sovity.de/edc-client'; +import {EdcApiService} from '../services/api/edc-api.service'; + +@Injectable({ + providedIn: 'root', +}) +export class DataOfferFormValidators { + constructor(private edcApiService: EdcApiService) {} + + policyIdExistsValidator: ValidatorFn = ( + control: AbstractControl, + ): Observable => { + const value = control.value; + if (value) { + return this.edcApiService.isPolicyIdAvailable(value).pipe( + catchError(() => + of({id: value, available: false}), + ), + map((it) => (it.available ? null : {idAlreadyExists: true})), + ); + } + return of(null); + }; + + contractDefinitionIdExistsValidator: ValidatorFn = ( + control: AbstractControl, + ): Observable => { + const value = control.value; + if (value) { + return this.edcApiService.isContractDefinitionIdAvailable(value).pipe( + catchError(() => + of({id: value, available: false}), + ), + map((it) => (it.available ? null : {idAlreadyExists: true})), + ); + } + return of(null); + }; + + assetIdExistsValidator: ValidatorFn = ( + control: AbstractControl, + ): Observable => { + const value = control.value; + if (value) { + return this.edcApiService.isAssetIdAvailable(value).pipe( + catchError(() => + of({id: value, available: false}), + ), + map((it) => (it.available ? null : {idAlreadyExists: true})), + ); + } + return of(null); + }; +} diff --git a/connector-ui/src/app/core/validators/json-validator.ts b/connector-ui/src/app/core/validators/json-validator.ts new file mode 100644 index 000000000..023d2acaf --- /dev/null +++ b/connector-ui/src/app/core/validators/json-validator.ts @@ -0,0 +1,19 @@ +import {AbstractControl, ValidationErrors, ValidatorFn} from '@angular/forms'; + +/** + * Validates whether control's value is valid JSON. + * @param control control + */ +export const jsonValidator: ValidatorFn = ( + control: AbstractControl, +): ValidationErrors | null => { + const value = control.value; + if (value) { + try { + JSON.parse(value); + } catch (e) { + return {jsonInvalid: true}; + } + } + return null; +}; diff --git a/connector-ui/src/app/core/validators/no-whitespaces-or-colons-validator.ts b/connector-ui/src/app/core/validators/no-whitespaces-or-colons-validator.ts new file mode 100644 index 000000000..24e814dfb --- /dev/null +++ b/connector-ui/src/app/core/validators/no-whitespaces-or-colons-validator.ts @@ -0,0 +1,8 @@ +import {ValidatorFn, Validators} from '@angular/forms'; + +/** + * Validates whether value contains whitespaces + * @param control control + */ +export const noWhitespacesOrColonsValidator: ValidatorFn = + Validators.pattern(/^[^\s:]*$/); diff --git a/connector-ui/src/app/core/validators/url-validator.ts b/connector-ui/src/app/core/validators/url-validator.ts new file mode 100644 index 000000000..3c4812b4f --- /dev/null +++ b/connector-ui/src/app/core/validators/url-validator.ts @@ -0,0 +1,19 @@ +import {AbstractControl, ValidationErrors, ValidatorFn} from '@angular/forms'; + +export const validUrlPattern = /^(http|https):\/\/[^ "]+$/; + +/** + * Validates whether control's value is a valid URL. + * @param control control + */ +export const urlValidator: ValidatorFn = ( + control: AbstractControl, +): ValidationErrors | null => { + const value: string = control.value; + + if (!value?.length || validUrlPattern.test(value)) { + return null; + } + + return {url: true}; +}; diff --git a/connector-ui/src/app/core/validators/valid-date-range-optional-end.ts b/connector-ui/src/app/core/validators/valid-date-range-optional-end.ts new file mode 100644 index 000000000..fb44c209c --- /dev/null +++ b/connector-ui/src/app/core/validators/valid-date-range-optional-end.ts @@ -0,0 +1,22 @@ +import {AbstractControl, ValidationErrors, ValidatorFn} from '@angular/forms'; +import {DateRange} from '@angular/material/datepicker'; + +export const validDateRangeOptionalEnd: ValidatorFn = ( + control: AbstractControl, +): ValidationErrors | null => { + const value: DateRange = control.value; + if (!value?.start || (value?.end && value.start > value.end)) { + return {required: true}; + } + return null; +}; + +export const validDateRange: ValidatorFn = ( + control: AbstractControl, +): ValidationErrors | null => { + const value: DateRange = control.value; + if (!value?.start || !value?.end || value.start > value.end) { + return {required: true}; + } + return null; +}; diff --git a/connector-ui/src/app/core/validators/valid-optional-date-range.ts b/connector-ui/src/app/core/validators/valid-optional-date-range.ts new file mode 100644 index 000000000..7f03c787d --- /dev/null +++ b/connector-ui/src/app/core/validators/valid-optional-date-range.ts @@ -0,0 +1,16 @@ +import {AbstractControl, ValidationErrors, ValidatorFn} from '@angular/forms'; +import {DateRange} from '@angular/material/datepicker'; + +/** + * Validates end date is set after start date but does not require any dates to be set. + * @param control control + */ +export const validOptionalDateRange: ValidatorFn = ( + control: AbstractControl, +): ValidationErrors | null => { + const value: DateRange = control.value; + if (value?.start && value?.end && value.start > value.end) { + return {required: true}; + } + return null; +}; diff --git a/connector-ui/src/app/core/validators/valid-query-param.ts b/connector-ui/src/app/core/validators/valid-query-param.ts new file mode 100644 index 000000000..3eea2a134 --- /dev/null +++ b/connector-ui/src/app/core/validators/valid-query-param.ts @@ -0,0 +1,18 @@ +import {AbstractControl, ValidationErrors, ValidatorFn} from '@angular/forms'; + +/** + * Validates that param string does not contain "=" or "&" characters + * Temporary solution until EDC double encoding issue is resolved + * See https://github.com/sovity/edc-extensions/issues/582 + * @param control control + */ +export const validQueryParam: ValidatorFn = ( + control: AbstractControl, +): ValidationErrors | null => { + const value: string = control.value; + if (value?.includes('=') || value?.includes('&')) { + return {invalidQueryParam: true}; + } + + return null; +}; diff --git a/connector-ui/src/app/core/validators/validation-messages.ts b/connector-ui/src/app/core/validators/validation-messages.ts new file mode 100644 index 000000000..1fa70a3df --- /dev/null +++ b/connector-ui/src/app/core/validators/validation-messages.ts @@ -0,0 +1,14 @@ +import {Injectable} from '@angular/core'; + +@Injectable({providedIn: 'root'}) +export class ValidationMessages { + invalidEmailMessage = 'Must be a valid E-Mail address.'; + invalidUrlMessage = 'Must be valid URL, e.g. https://example.com'; + invalidJsonMessage = 'Must be valid JSON'; + invalidWhitespacesOrColonsMessage = 'Must not contain whitespaces or colons.'; + invalidPrefix = (field: string, prefix: string): string => + `${field} must start with "${prefix}".`; + invalidDateRangeMessage = 'Need valid date range.'; + idExistsErrorMessage = 'ID already exists.'; + invalidQueryParam = "Must not contain '=' or '&' characters."; +} diff --git a/connector-ui/src/app/routes/connector-ui/asset-edit-page/asset-edit-page.module.ts b/connector-ui/src/app/routes/connector-ui/asset-edit-page/asset-edit-page.module.ts new file mode 100644 index 000000000..39a28a6a9 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/asset-edit-page/asset-edit-page.module.ts @@ -0,0 +1,20 @@ +import {CommonModule} from '@angular/common'; +import {NgModule} from '@angular/core'; +import {RouterModule} from '@angular/router'; +import {SharedModule} from '../../../shared/shared.module'; +import {AssetEditPageComponent} from './asset-edit-page/asset-edit-page.component'; + +@NgModule({ + imports: [ + // Angular + CommonModule, + RouterModule, + + // EDC UI Modules + SharedModule, + ], + declarations: [AssetEditPageComponent], + exports: [AssetEditPageComponent], + providers: [], +}) +export class AssetEditPageModule {} diff --git a/connector-ui/src/app/routes/connector-ui/asset-edit-page/asset-edit-page/asset-edit-page.component.html b/connector-ui/src/app/routes/connector-ui/asset-edit-page/asset-edit-page/asset-edit-page.component.html new file mode 100644 index 000000000..8ca26da4c --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/asset-edit-page/asset-edit-page/asset-edit-page.component.html @@ -0,0 +1,15 @@ +
+
+ + +
+ +
diff --git a/connector-ui/src/app/routes/connector-ui/asset-edit-page/asset-edit-page/asset-edit-page.component.ts b/connector-ui/src/app/routes/connector-ui/asset-edit-page/asset-edit-page/asset-edit-page.component.ts new file mode 100644 index 000000000..a8a944d04 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/asset-edit-page/asset-edit-page/asset-edit-page.component.ts @@ -0,0 +1,162 @@ +import {Component, OnInit} from '@angular/core'; +import {ActivatedRoute, Router} from '@angular/router'; +import {EMPTY, Observable, catchError, finalize, tap} from 'rxjs'; +import { + DataOfferCreationRequestPolicyEnum, + IdResponseDto, + UiAssetEditRequest, +} from '@sovity.de/edc-client'; +import {EdcApiService} from 'src/app/core/services/api/edc-api.service'; +import {AssetRequestBuilder} from 'src/app/core/services/asset-request-builder'; +import {AssetService} from 'src/app/core/services/asset.service'; +import {Fetched} from 'src/app/core/services/models/fetched'; +import {UiAssetMapped} from 'src/app/core/services/models/ui-asset-mapped'; +import {NotificationService} from 'src/app/core/services/notification.service'; +import {editAssetFormRequiredViewProviders} from '../../../../shared/business/edit-asset-form/edit-asset-form-required-providers'; +import {EditAssetForm} from '../../../../shared/business/edit-asset-form/form/edit-asset-form'; +import {EditAssetFormInitializer} from '../../../../shared/business/edit-asset-form/form/edit-asset-form-initializer'; +import {EditAssetFormValue} from '../../../../shared/business/edit-asset-form/form/model/edit-asset-form-model'; +import {ExpressionFormHandler} from '../../../../shared/business/policy-editor/editor/expression-form-handler'; + +@Component({ + selector: 'asset-edit-page', + templateUrl: './asset-edit-page.component.html', + providers: [EditAssetFormInitializer, AssetRequestBuilder], + viewProviders: editAssetFormRequiredViewProviders, +}) +export class AssetEditPageComponent implements OnInit { + asset: Fetched = new Fetched( + 'loading', + undefined, + undefined, + ); + isLoading = false; + + constructor( + private editAssetFormInitializer: EditAssetFormInitializer, + private form: EditAssetForm, + private assetRequestBuilder: AssetRequestBuilder, + private edcApiService: EdcApiService, + private assetServiceMapped: AssetService, + private notificationService: NotificationService, + private router: Router, + private route: ActivatedRoute, + private expressionFormHandler: ExpressionFormHandler, + ) {} + + ngOnInit(): void { + this.route.params.subscribe((params) => { + if (params.id) { + this.assetServiceMapped + .fetchAssets() + .pipe( + Fetched.wrap({ + failureMessage: 'Failed fetching asset list.', + }), + ) + .pipe( + Fetched.map((assets): UiAssetMapped | undefined => + assets.find((asset) => asset.assetId === params.id), + ), + ) + .subscribe((asset) => { + this.asset = asset; + + if (asset.isReady) { + this.form.reset( + this.editAssetFormInitializer.forEdit(asset.data!), + ); + } + }); + } else { + this.form.reset(this.editAssetFormInitializer.forCreate()); + this.asset.state = 'ready'; + } + }); + } + + onSubmit() { + const formValue = this.form.value; + + // Workaround around disabled controls not being included in the form value + if (formValue.mode !== 'CREATE') { + formValue.general!.id = this.form.general.controls.id.getRawValue(); + } + + this.form.all.disable(); + this.isLoading = true; + + this._saveRequest(formValue) + .pipe( + tap(() => { + this.notificationService.showInfo('Successfully saved asset'); + }), + catchError((error) => { + console.error('Failed saving asset!', error); + this.notificationService.showError('Failed saving asset!'); + this.form.all.enable(); + return EMPTY; + }), + finalize(() => { + this.isLoading = false; + }), + ) + .subscribe(() => this.router.navigate(['my-assets'])); + } + + private _saveRequest( + formValue: EditAssetFormValue, + ): Observable { + const assetId = formValue.general!.id!; + const mode = this.form.mode; + const publishMode = formValue.publishMode!; + + if (mode === 'CREATE') { + const assetCreateRequest = + this.assetRequestBuilder.buildAssetCreateRequest(formValue); + + if (publishMode === 'PUBLISH_UNRESTRICTED') { + return this.edcApiService.createDataOffer({ + dataOfferCreationRequest: { + uiAssetCreateRequest: assetCreateRequest, + policy: DataOfferCreationRequestPolicyEnum.PublishUnrestricted, + uiPolicyExpression: + this.expressionFormHandler.toUiPolicyExpression(), + }, + }); + } else if (publishMode === 'PUBLISH_RESTRICTED') { + return this.edcApiService.createDataOffer({ + dataOfferCreationRequest: { + uiAssetCreateRequest: assetCreateRequest, + policy: DataOfferCreationRequestPolicyEnum.PublishRestricted, + uiPolicyExpression: + this.expressionFormHandler.toUiPolicyExpression(), + }, + }); + } else { + return this.edcApiService.createDataOffer({ + dataOfferCreationRequest: { + uiAssetCreateRequest: assetCreateRequest, + policy: DataOfferCreationRequestPolicyEnum.DontPublish, + }, + }); + } + } + + if (mode === 'EDIT') { + const asset = this.asset.data; + + const editRequest: UiAssetEditRequest = { + ...this.assetRequestBuilder.buildAssetEditRequest(formValue), + customJsonAsString: asset?.customJsonAsString, + customJsonLdAsString: asset?.customJsonLdAsString, + privateCustomJsonAsString: asset?.privateCustomJsonAsString, + privateCustomJsonLdAsString: asset?.privateCustomJsonLdAsString, + }; + + return this.edcApiService.editAsset(assetId, editRequest); + } + + throw new Error(`Unsupported mode: ${mode}`); + } +} diff --git a/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-cards/asset-cards.component.html b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-cards/asset-cards.component.html new file mode 100644 index 000000000..f2d8d9f37 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-cards/asset-cards.component.html @@ -0,0 +1,27 @@ + + + {{ + asset.dataSourceAvailability === 'LIVE' ? 'upload' : 'contact_page' + }} + + + {{ asset.title }} + + + {{ asset.creatorOrganizationName }} + + + + + + + + + diff --git a/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-cards/asset-cards.component.ts b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-cards/asset-cards.component.ts new file mode 100644 index 000000000..705e5cb8f --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-cards/asset-cards.component.ts @@ -0,0 +1,25 @@ +import { + Component, + EventEmitter, + HostBinding, + Input, + Output, +} from '@angular/core'; +import {UiAssetMapped} from '../../../../core/services/models/ui-asset-mapped'; + +@Component({ + selector: 'asset-cards', + templateUrl: './asset-cards.component.html', +}) +export class AssetCardsComponent { + @HostBinding('class.flex') + @HostBinding('class.flex-wrap') + @HostBinding('class.gap-[10px]') + cls = true; + + @Input() + assets: UiAssetMapped[] = []; + + @Output() + assetClick = new EventEmitter(); +} diff --git a/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/asset-create-dialog-data.ts b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/asset-create-dialog-data.ts new file mode 100644 index 000000000..fac8c22aa --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/asset-create-dialog-data.ts @@ -0,0 +1,5 @@ +import {AssetCreateDialogFormValue} from './form/model/asset-create-dialog-form-model'; + +export interface AssetCreateDialogData { + initialFormValue: AssetCreateDialogFormValue; +} diff --git a/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/asset-create-dialog-mode.ts b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/asset-create-dialog-mode.ts new file mode 100644 index 000000000..f5b88fb2a --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/asset-create-dialog-mode.ts @@ -0,0 +1 @@ +export type AssetCreateDialogMode = 'CREATE' | 'EDIT'; diff --git a/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/asset-create-dialog-result.ts b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/asset-create-dialog-result.ts new file mode 100644 index 000000000..acfc47fff --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/asset-create-dialog-result.ts @@ -0,0 +1,13 @@ +import {UiAssetMapped} from '../../../../core/services/models/ui-asset-mapped'; + +export interface AssetCreateDialogResult { + /** + * Updated asset list for the asset page + */ + refreshedList: UiAssetMapped[]; + + /** + * The updated / created asset + */ + asset: UiAssetMapped; +} diff --git a/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/asset-create-dialog.component.html b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/asset-create-dialog.component.html new file mode 100644 index 000000000..67a141917 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/asset-create-dialog.component.html @@ -0,0 +1,1081 @@ +

+ {{ 'asset_list_page.create_asset' | translate }} +

+ + + + +
+ {{ + 'create_data_offer_page.general_information' | translate + }} +
+
+ + + {{ 'general.title' | translate }} + + + + + + + {{ + 'create_data_offer_page.version' | translate + }} + + + +
+ + + + {{ + 'create_data_offer_page.asset_id' | translate + }} + + + + {{ validationMessages.invalidWhitespacesOrColonsMessage }} + + + {{ validationMessages.invalidPrefix('ID', 'urn:artifact') }} + + + {{ validationMessages.idExistsErrorMessage }} + + + + + + + {{ + 'create_data_offer_page.description' | translate + }} + + + {{ 'create_data_offer_page.description_supports' | translate }} + Markdown syntax + + + +
+ + + + + + + +
+ + + {{ + 'create_data_offer_page.content_type' | translate + }} + + + {{ 'create_asset.content_type_hint' | translate }} + common types + + +
+ + + + {{ + 'create_data_offer_page.endpoint_documentation' | translate + }} + + + + {{ validationMessages.invalidUrlMessage }} + + + +
+ + + {{ + 'create_data_offer_page.publisher' | translate + }} + + + + {{ validationMessages.invalidUrlMessage }} + + + + + + {{ + 'create_data_offer_page.standard_license_label' | translate + }} + + + + {{ validationMessages.invalidUrlMessage }} + + +
+
+
+
+ + +
+ {{ + 'general.ad_inf' | translate + }} +
+
+ + + + + +
+ + + + {{ + 'create_data_offer_page.data_model' | translate + }} + + + + +
+ + + + + + {{ + 'create_data_offer_page.geo_reference_method' | translate + }} + + + +
+ + + + {{ + 'create_data_offer_page.sovereign_label' | translate + }} + + + + + +
+ + {{ + 'create_data_offer_page.data_update_frequency_label' | translate + }} + + + + + + + {{ + 'create_data_offer_page.geo_location_label' | translate + }} + + + +
+ + +
+
+ {{ 'create_data_offer_page.nuts_locations_label' | translate }} +
+ +
+
+ + {{ + 'create_data_offer_page.nuts_location' | translate + }} + + + + +
+ +
+ +
+ + +
+
+ {{ 'create_data_offer_page.data_samples' | translate }} +
+ +
+
+ + URL + + + + +
+ +
+ +
+ + +
+
+ {{ 'create_data_offer_page.reference_files' | translate }} +
+ + +
+
+ + URL + + + + +
+ +
+ +
+ + + + {{ + 'create_data_offer_page.reference_files_description_label' + | translate + }} + + {{ + 'create_data_offer_page.reference_files_description' | translate + }} + + Markdown syntax + + + + + + {{ + 'create_data_offer_page.temporal_coverage' | translate + }} + + + + + {{ 'create_data_offer_page.temporal_coverage_hint' | translate }} + + + + {{ validationMessages.invalidDateRangeMessage }} + + + + + + {{ + 'create_data_offer_page.conditions_for_use_label' | translate + }} + + {{ + 'create_data_offer_page.conditions_for_use_description_hint' + | translate + }} + Markdown syntax + + +
+
+
+ + +
+ {{ + 'create_data_offer_page.datasource_information' | translate + }} +
+ {{ 'create_data_offer_page.datasource' | translate }} +
+
+ + + + + + {{ + 'create_data_offer_page.custom_datasource_config_json_label' + | translate + }} + + + {{ validationMessages.invalidJsonMessage }} + + + + + + + + + {{ + 'create_data_offer_page.contact_email' | translate + }} + + + + {{ validationMessages.invalidEmailMessage }} + + + + + + + {{ + 'create_data_offer_page.email_subject' | translate + }} + + + + + + + +
+ {{ 'general.method' | translate }} +
+ +
+ {{ 'create_data_offer_page.custom_http_method_hint' | translate }} +
+ + + + + + {{ form.proxyMethod ? 'Default' : '' }} + {{ 'general.method' | translate }} + + + {{ method }} + + + + + + +
+ +
+ +
URL
+ +
+ {{ + 'create_data_offer_page.custom_http_subpath_hint' | translate + }} +
+ + + + URL + Base URL + + + {{ validationMessages.invalidUrlMessage }} + + + + +
+ +
+ +
+ {{ form.proxyQueryParams ? 'Default' : '' }} + {{ 'create_data_offer_page.query_params' | translate }} +
+ +
+ + + {{ + 'create_data_offer_page.query_param_name' | translate + }} + + {{ header.errors }} + {{ validationMessages.invalidQueryParam }} + + + + + + {{ 'general.value' | translate }} + + {{ validationMessages.invalidQueryParam }} + + + + + +
+ +
+ {{ 'asset_list_page.default_query' | translate }} +
+ +
+ + + + + +
+ +
+ {{ 'create_data_offer_page.request_body' | translate }} +
+ +
+ {{ 'create_data_offer_page.request_body_hint' | translate }} +
+ + +
+ +
+ +
+ {{ 'general.auth' | translate }} +
+ + +
+ +
+ + + + {{ 'general.type' | translate }} + + + {{ + 'create_data_offer_page.header_with_vault_secret' + | translate + }} + + {{ + 'create_data_offer_page.header_with_value' | translate + }} + + +
+ + + {{ + 'create_data_offer_page.auth_header_name' | translate + }} + + + + + + {{ 'general.auth_value' | translate }} + + + + + + {{ + 'create_data_offer_page.vault_secret_name' | translate + }} + + +
+ + +
+ +
+ +
+ {{ 'create_data_offer_page.additional_headers' | translate }} +
+ +
+ + + {{ 'general.header_name' | translate }} + + + + + + {{ 'general.header_value' | translate }} + + + + + +
+ + +
+ +
+
+
+
+
+
+
+ + + + + + diff --git a/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/asset-create-dialog.component.ts b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/asset-create-dialog.component.ts new file mode 100644 index 000000000..4af0aa3a8 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/asset-create-dialog.component.ts @@ -0,0 +1,107 @@ +import {Component, Inject, OnDestroy} from '@angular/core'; +import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog'; +import {EMPTY, Observable, Subject, switchMap} from 'rxjs'; +import {catchError, finalize, map, takeUntil, tap} from 'rxjs/operators'; +import {IdResponseDto} from '@sovity.de/edc-client'; +import {EdcApiService} from '../../../../core/services/api/edc-api.service'; +import {AssetRequestBuilderLegacy} from '../../../../core/services/asset-request-builder-legacy'; +import {AssetService} from '../../../../core/services/asset.service'; +import {NotificationService} from '../../../../core/services/notification.service'; +import {ValidationMessages} from '../../../../core/validators/validation-messages'; +import {AssetCreateDialogData} from './asset-create-dialog-data'; +import {AssetCreateDialogResult} from './asset-create-dialog-result'; +import {AssetAdvancedFormBuilder} from './form/asset-advanced-form-builder'; +import {AssetCreateDialogForm} from './form/asset-create-dialog-form'; +import {AssetDatasourceFormBuilder} from './form/asset-datasource-form-builder'; +import {AssetMetadataFormBuilder} from './form/asset-metadata-form-builder'; +import {DATA_SOURCE_HTTP_METHODS} from './form/http-methods'; +import {AssetCreateDialogFormValue} from './form/model/asset-create-dialog-form-model'; + +@Component({ + selector: 'asset-create-dialog', + templateUrl: './asset-create-dialog.component.html', + providers: [ + AssetAdvancedFormBuilder, + AssetDatasourceFormBuilder, + AssetCreateDialogForm, + AssetRequestBuilderLegacy, + AssetMetadataFormBuilder, + ], +}) +export class AssetCreateDialogComponent implements OnDestroy { + loading = false; + + methods = DATA_SOURCE_HTTP_METHODS; + + constructor( + private edcApiService: EdcApiService, + private assetService: AssetService, + public form: AssetCreateDialogForm, + public validationMessages: ValidationMessages, + private assetEntryBuilder: AssetRequestBuilderLegacy, + private notificationService: NotificationService, + private dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) private data: AssetCreateDialogData, + ) { + this.form.reset(this.data.initialFormValue); + } + + onSave() { + const formValue = this.form.value; + + this.form.all.disable(); + this.loading = true; + this._saveRequest(formValue) + .pipe( + // Save Asset + takeUntil(this.ngOnDestroy$), + tap(() => { + this.notificationService.showInfo('Successfully saved asset'); + }), + catchError((error) => { + console.error('Failed saving asset!', error); + this.notificationService.showError('Failed saving asset!'); + this.form.all.enable(); + return EMPTY; + }), + switchMap(() => this.assetService.fetchAssets()), + map( + (assets): AssetCreateDialogResult => ({ + refreshedList: assets, + asset: assets?.find( + (it) => it.assetId === this.form.value.metadata?.id, + )!, + }), + ), + finalize(() => { + this.loading = false; + }), + ) + .subscribe({ + next: (result: AssetCreateDialogResult) => this.close(result), + error: (error) => { + console.error('Failed refreshing asset list!', error); + this.notificationService.showError('Failed refreshing asset list!'); + }, + }); + } + + private _saveRequest( + formValue: AssetCreateDialogFormValue, + ): Observable { + const createRequest = + this.assetEntryBuilder.buildAssetCreateRequestLegacy(formValue); + return this.edcApiService.createAsset(createRequest); + } + + private close(params: AssetCreateDialogResult) { + this.dialogRef.close(params); + } + + ngOnDestroy$ = new Subject(); + + ngOnDestroy(): void { + this.ngOnDestroy$.next(null); + this.ngOnDestroy$.complete(); + } +} diff --git a/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/asset-create-dialog.service.ts b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/asset-create-dialog.service.ts new file mode 100644 index 000000000..aa35307d8 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/asset-create-dialog.service.ts @@ -0,0 +1,35 @@ +import {Injectable} from '@angular/core'; +import {MatDialog} from '@angular/material/dialog'; +import {NEVER, Observable} from 'rxjs'; +import {showDialogUntil} from '../../../../core/utils/mat-dialog-utils'; +import {AssetCreateDialogData} from './asset-create-dialog-data'; +import {AssetCreateDialogResult} from './asset-create-dialog-result'; +import {AssetCreateDialogComponent} from './asset-create-dialog.component'; +import {AssetCreateDialogFormMapper} from './form/asset-create-dialog-form-mapper'; + +@Injectable() +export class AssetCreateDialogService { + constructor( + private dialog: MatDialog, + private assetCreateDialogFormMapper: AssetCreateDialogFormMapper, + ) {} + + showCreateDialog( + until$: Observable = NEVER, + ): Observable { + const initialFormValue = this.assetCreateDialogFormMapper.forCreate(); + return this._open({initialFormValue}, until$); + } + + private _open( + data: AssetCreateDialogData, + until$: Observable, + ): Observable { + return showDialogUntil( + this.dialog, + AssetCreateDialogComponent, + {data}, + until$, + ); + } +} diff --git a/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/assets-id-validator-builder.ts b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/assets-id-validator-builder.ts new file mode 100644 index 000000000..bfef34c99 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/assets-id-validator-builder.ts @@ -0,0 +1,35 @@ +import {Injectable} from '@angular/core'; +import { + AbstractControl, + AsyncValidatorFn, + ValidationErrors, +} from '@angular/forms'; +import {Observable} from 'rxjs'; +import {map} from 'rxjs/operators'; +import {AssetService} from '../../../../core/services/asset.service'; + +@Injectable({ + providedIn: 'root', +}) +export class AssetsIdValidatorBuilder { + constructor(private assetServiceMapped: AssetService) {} + + assetIdDoesNotExistsValidator(): AsyncValidatorFn { + return (control: AbstractControl): Observable => { + return this.fetchAssetIds().pipe( + map((assetIds) => { + if (assetIds.has(control.value)) { + return {idAlreadyExists: true}; + } + return null; + }), + ); + }; + } + + private fetchAssetIds(): Observable> { + return this.assetServiceMapped + .fetchAssets() + .pipe(map((assets) => new Set(assets.map((asset) => asset.assetId)))); + } +} diff --git a/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/form/asset-advanced-form-builder.ts b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/form/asset-advanced-form-builder.ts new file mode 100644 index 000000000..a3ae32035 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/form/asset-advanced-form-builder.ts @@ -0,0 +1,55 @@ +import {Injectable} from '@angular/core'; +import {FormBuilder, FormControl, FormGroup, Validators} from '@angular/forms'; +import {urlValidator} from 'src/app/core/validators/url-validator'; +import {validOptionalDateRange} from 'src/app/core/validators/valid-optional-date-range'; +import {AssetAdvancedFormModel} from './model/asset-advanced-form-model'; +import {AssetCreateDialogFormValue} from './model/asset-create-dialog-form-model'; + +@Injectable() +export class AssetAdvancedFormBuilder { + constructor(private formBuilder: FormBuilder) {} + + buildFormGroup( + initial: AssetCreateDialogFormValue['advanced'], + ): FormGroup { + return this.formBuilder.nonNullable.group({ + dataModel: initial?.dataModel!, + dataCategory: [initial?.dataCategory || null, Validators.required], + dataSubcategory: initial?.dataSubcategory || null, + transportMode: initial?.transportMode || null, + geoReferenceMethod: initial?.geoReferenceMethod!, + sovereignLegalName: initial?.sovereignLegalName!, + geoLocation: initial?.geoLocation!, + nutsLocations: this.formBuilder.nonNullable.array( + initial?.nutsLocations?.map((x) => this.buildRequiredString(x)) ?? [], + ), + dataSampleUrls: this.formBuilder.array( + initial?.dataSampleUrls?.map((x) => this.buildRequiredUrl(x)) ?? [], + ), + referenceFileUrls: this.formBuilder.nonNullable.array( + initial?.referenceFileUrls?.map((x) => this.buildRequiredUrl(x)) ?? [], + ), + referenceFilesDescription: initial?.referenceFilesDescription!, + conditionsForUse: initial?.conditionsForUse!, + dataUpdateFrequency: initial?.dataUpdateFrequency!, + temporalCoverage: this.formBuilder.group( + { + from: initial?.temporalCoverage?.from || null, + toInclusive: initial?.temporalCoverage?.toInclusive || null, + }, + {validators: validOptionalDateRange}, + ), + }); + } + + buildRequiredString(initial: string): FormControl { + return this.formBuilder.nonNullable.control(initial, Validators.required); + } + + buildRequiredUrl(initial: string): FormControl { + return this.formBuilder.nonNullable.control(initial, [ + Validators.required, + urlValidator, + ]); + } +} diff --git a/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/form/asset-create-dialog-form-mapper.ts b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/form/asset-create-dialog-form-mapper.ts new file mode 100644 index 000000000..8ed74aa76 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/form/asset-create-dialog-form-mapper.ts @@ -0,0 +1,60 @@ +import {Injectable} from '@angular/core'; +import {LanguageSelectItemService} from '../../../../../shared/form-elements/language-select/language-select-item.service'; +import {AssetCreateDialogFormValue} from './model/asset-create-dialog-form-model'; +import {AssetDatasourceFormValue} from './model/asset-datasource-form-model'; + +/** + * Handles AngularForms for AssetCreateDialog + */ +@Injectable() +export class AssetCreateDialogFormMapper { + constructor(private languageSelectItemService: LanguageSelectItemService) {} + + forCreate(): AssetCreateDialogFormValue { + return { + metadata: { + id: '', + title: '', + version: '', + contentType: '', + description: '', + keywords: [], + language: this.languageSelectItemService.english(), + publisher: '', + standardLicense: '', + endpointDocumentation: '', + }, + advanced: { + dataModel: '', + dataCategory: null, + dataSubcategory: null, + transportMode: null, + geoReferenceMethod: '', + }, + datasource: this.emptyHttpDatasource(), + }; + } + + private emptyHttpDatasource(): AssetDatasourceFormValue { + return { + dataAddressType: 'Http', + dataDestination: '', + + httpUrl: '', + httpMethod: 'GET', + httpAuthHeaderType: 'None', + httpAuthHeaderName: '', + httpAuthHeaderValue: '', + httpAuthHeaderSecretName: '', + httpQueryParams: [], + + httpDefaultPath: '', + httpProxyMethod: false, + httpProxyPath: false, + httpProxyQueryParams: false, + httpProxyBody: false, + + httpHeaders: [], + }; + } +} diff --git a/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/form/asset-create-dialog-form.ts b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/form/asset-create-dialog-form.ts new file mode 100644 index 000000000..438976f3f --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/form/asset-create-dialog-form.ts @@ -0,0 +1,166 @@ +import {Injectable} from '@angular/core'; +import {FormBuilder, FormGroup} from '@angular/forms'; +import {ActiveFeatureSet} from '../../../../../core/config/active-feature-set'; +import {DataAddressType} from '../../../../../shared/form-elements/data-address-type-select/data-address-type'; +import {DataCategorySelectItem} from '../../../../../shared/form-elements/data-category-select/data-category-select-item'; +import {AssetAdvancedFormBuilder} from './asset-advanced-form-builder'; +import {AssetDatasourceFormBuilder} from './asset-datasource-form-builder'; +import {AssetMetadataFormBuilder} from './asset-metadata-form-builder'; +import {AssetAdvancedFormModel} from './model/asset-advanced-form-model'; +import { + AssetCreateDialogFormModel, + AssetCreateDialogFormValue, +} from './model/asset-create-dialog-form-model'; +import {AssetDatasourceFormModel} from './model/asset-datasource-form-model'; +import {AssetMetadataFormModel} from './model/asset-metadata-form-model'; + +/** + * Handles AngularForms for AssetCreateDialog + */ +@Injectable() +export class AssetCreateDialogForm { + all!: FormGroup; + + /** + * FormGroup for stepper step "Metadata" + */ + metadata!: AssetCreateDialogFormModel['metadata']; + + /** + * FormGroup for stepper step "Advanced" + */ + advanced!: AssetCreateDialogFormModel['advanced']; + + /** + * FormGroup for stepper step "Data Source" + */ + datasource!: AssetCreateDialogFormModel['datasource']; + + /** + * Quick access to selected data address type + */ + get dataAddressType(): DataAddressType | null { + return this.datasource!.controls.dataAddressType.value; + } + + /** + * Quick access to selected data category + */ + get dataCategory(): DataCategorySelectItem | null { + return this.advanced!.controls.dataCategory.value; + } + + /** + * Quick access to full value + */ + get value(): AssetCreateDialogFormValue { + return this.all.value; + } + + get proxyMethod(): boolean { + return this.datasource!.controls.httpProxyMethod.value; + } + + get proxyPath(): boolean { + return this.datasource!.controls.httpProxyPath.value; + } + + get proxyQueryParams(): boolean { + return this.datasource!.controls.httpProxyQueryParams.value; + } + + constructor( + private formBuilder: FormBuilder, + private activeFeatureSet: ActiveFeatureSet, + private assetMetadataFormBuilder: AssetMetadataFormBuilder, + private assetAdvancedFormBuilder: AssetAdvancedFormBuilder, + private assetDatasourceFormBuilder: AssetDatasourceFormBuilder, + ) {} + + reset(initial: AssetCreateDialogFormValue) { + this.all = this.buildFormGroup(initial); + this.metadata = this.all.controls.metadata; + this.advanced = this.all.controls.advanced; + this.datasource = this.all.controls.datasource; + } + + buildFormGroup( + initial: AssetCreateDialogFormValue, + ): FormGroup { + const metadata: FormGroup = + this.assetMetadataFormBuilder.buildFormGroup(initial.metadata!); + + const datasource: FormGroup = + this.assetDatasourceFormBuilder.buildFormGroup(initial.datasource!); + + const formGroup: FormGroup = + this.formBuilder.nonNullable.group({ + metadata, + datasource, + }); + + if (this.activeFeatureSet.hasMdsFields()) { + const advanced: FormGroup = + this.assetAdvancedFormBuilder.buildFormGroup(initial.advanced); + formGroup.addControl('advanced', advanced); + } + + return formGroup; + } + + onHttpHeadersAddClick() { + this.datasource!.controls.httpHeaders.push( + this.assetDatasourceFormBuilder.buildHeaderFormGroup({ + headerName: '', + headerValue: '', + }), + ); + } + + onHttpHeadersRemoveClick(index: number) { + this.datasource!.controls.httpHeaders.removeAt(index); + } + + onHttpQueryParamsAddClick() { + this.datasource!.controls.httpQueryParams.push( + this.assetDatasourceFormBuilder.buildQueryParamFormGroup({ + paramName: '', + paramValue: '', + }), + ); + } + + onHttpQueryParamsRemoveClick(index: number) { + this.datasource!.controls.httpQueryParams.removeAt(index); + } + + onNutsLocationsAddClick() { + this.advanced!.controls.nutsLocations.push( + this.assetAdvancedFormBuilder.buildRequiredString(''), + ); + } + + onNutsLocationsRemoveClick(index: number) { + this.advanced!.controls.nutsLocations.removeAt(index); + } + + onDataSampleUrlsAddClick() { + this.advanced!.controls.dataSampleUrls.push( + this.assetAdvancedFormBuilder.buildRequiredUrl(''), + ); + } + + onDataSampleUrlsRemoveClick(index: number) { + this.advanced!.controls.dataSampleUrls.removeAt(index); + } + + onReferenceFileUrlsAddClick() { + this.advanced!.controls.referenceFileUrls.push( + this.assetAdvancedFormBuilder.buildRequiredUrl(''), + ); + } + + onReferenceFileUrlsRemoveClick(index: number) { + this.advanced!.controls.referenceFileUrls.removeAt(index); + } +} diff --git a/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/form/asset-datasource-form-builder.ts b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/form/asset-datasource-form-builder.ts new file mode 100644 index 000000000..0fafca5dc --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/form/asset-datasource-form-builder.ts @@ -0,0 +1,105 @@ +import {Injectable} from '@angular/core'; +import {FormBuilder, FormGroup, Validators} from '@angular/forms'; +import {validQueryParam} from 'src/app/core/validators/valid-query-param'; +import {switchDisabledControls} from '../../../../../core/utils/form-group-utils'; +import {jsonValidator} from '../../../../../core/validators/json-validator'; +import {urlValidator} from '../../../../../core/validators/url-validator'; +import {assetDatasourceFormEnabledCtrls} from './model/asset-datasource-form-enabled-ctrls'; +import { + AssetDatasourceFormModel, + AssetDatasourceFormValue, +} from './model/asset-datasource-form-model'; +import { + HttpDatasourceHeaderFormModel, + HttpDatasourceHeaderFormValue, +} from './model/http-datasource-header-form-model'; +import { + HttpDatasourceQueryParamFormModel, + HttpDatasourceQueryParamFormValue, +} from './model/http-datasource-query-param-form-model'; + +@Injectable() +export class AssetDatasourceFormBuilder { + constructor(private formBuilder: FormBuilder) {} + + buildFormGroup( + initial: AssetDatasourceFormValue, + ): FormGroup { + const datasource: FormGroup = + this.formBuilder.nonNullable.group({ + dataAddressType: initial?.dataAddressType!, + dataDestination: [ + initial?.dataDestination!, + [Validators.required, jsonValidator], + ], + + // On-Request + contactEmail: [ + initial?.contactEmail!, + [Validators.required, Validators.email], + ], + contactPreferredEmailSubject: [ + initial?.contactPreferredEmailSubject!, + Validators.required, + ], + + // Http Datasource Fields + httpUrl: [initial?.httpUrl!, [Validators.required, urlValidator]], + httpMethod: [initial?.httpMethod!, Validators.required], + + httpAuthHeaderType: [initial?.httpAuthHeaderType!], + httpAuthHeaderName: [initial?.httpAuthHeaderName!, Validators.required], + httpAuthHeaderValue: [ + initial?.httpAuthHeaderValue!, + Validators.required, + ], + httpAuthHeaderSecretName: [ + initial?.httpAuthHeaderSecretName!, + Validators.required, + ], + httpQueryParams: this.formBuilder.array( + initial?.httpQueryParams?.map( + (param: HttpDatasourceQueryParamFormValue) => + this.buildQueryParamFormGroup(param), + ) ?? [], + ), + + httpDefaultPath: [initial?.httpDefaultPath!], + httpProxyMethod: [initial?.httpProxyMethod!], + httpProxyPath: [initial?.httpProxyPath!], + httpProxyQueryParams: [initial?.httpProxyQueryParams!], + httpProxyBody: [initial?.httpProxyBody!], + + httpHeaders: this.formBuilder.array( + initial?.httpHeaders?.map((header: HttpDatasourceHeaderFormValue) => + this.buildHeaderFormGroup(header), + ) ?? [], + ), + }); + + switchDisabledControls( + datasource, + assetDatasourceFormEnabledCtrls, + ); + + return datasource; + } + + buildHeaderFormGroup( + initial: HttpDatasourceHeaderFormValue, + ): FormGroup { + return this.formBuilder.nonNullable.group({ + headerName: [initial.headerName!, Validators.required], + headerValue: [initial.headerValue!, Validators.required], + }); + } + + buildQueryParamFormGroup( + initial: HttpDatasourceQueryParamFormValue, + ): FormGroup { + return this.formBuilder.nonNullable.group({ + paramName: [initial.paramName!, [Validators.required, validQueryParam]], + paramValue: [initial.paramValue!, [validQueryParam]], + }); + } +} diff --git a/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/form/asset-metadata-form-builder.ts b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/form/asset-metadata-form-builder.ts new file mode 100644 index 000000000..d4021e297 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/form/asset-metadata-form-builder.ts @@ -0,0 +1,88 @@ +import {Injectable} from '@angular/core'; +import {FormBuilder, FormControl, FormGroup, Validators} from '@angular/forms'; +import {combineLatest, distinctUntilChanged, pairwise} from 'rxjs'; +import {map} from 'rxjs/operators'; +import {value$} from '../../../../../core/utils/form-group-utils'; +import {noWhitespacesOrColonsValidator} from '../../../../../core/validators/no-whitespaces-or-colons-validator'; +import {urlValidator} from '../../../../../core/validators/url-validator'; +import {AssetsIdValidatorBuilder} from '../assets-id-validator-builder'; +import { + AssetMetadataFormModel, + AssetMetadataFormValue, +} from './model/asset-metadata-form-model'; + +@Injectable() +export class AssetMetadataFormBuilder { + constructor( + private formBuilder: FormBuilder, + private assetsIdValidatorBuilder: AssetsIdValidatorBuilder, + ) {} + + buildFormGroup( + initial: AssetMetadataFormValue, + ): FormGroup { + const metadata: FormGroup = + this.formBuilder.nonNullable.group({ + id: [ + initial?.id!, + [Validators.required, noWhitespacesOrColonsValidator], + [this.assetsIdValidatorBuilder.assetIdDoesNotExistsValidator()], + ], + title: [initial?.title!, Validators.required], + version: [initial?.version!], + contentType: [initial?.contentType!], + description: [initial?.description!], + keywords: [initial?.keywords!], + language: [initial?.language ?? null], + publisher: [initial?.publisher!, urlValidator], + standardLicense: [initial?.standardLicense!, urlValidator], + endpointDocumentation: [initial?.endpointDocumentation!, urlValidator], + }); + + // generate id from name and version(if available) + this.initIdGeneration( + metadata.controls.id, + metadata.controls.title, + metadata.controls.version, + ); + + return metadata; + } + + private initIdGeneration( + idCtrl: FormControl, + titleCtrl: FormControl, + versionCtrl: FormControl, + ) { + combineLatest([ + value$(titleCtrl).pipe(distinctUntilChanged()), + value$(versionCtrl).pipe(distinctUntilChanged()), + ]) + .pipe( + map(([title, version]) => this.generateId(title, version)), + pairwise(), + ) + .subscribe(([previousId, currentId]) => { + if (!idCtrl.value || idCtrl.value === previousId) { + idCtrl.setValue(currentId); + } + }); + } + + private generateId(title: string | null, version: string | null) { + if (!title) { + return ''; + } + const titleClean = this.cleanIdComponent(title); + const versionClean = this.cleanIdComponent(version); + return version ? `${titleClean}-${versionClean}` : titleClean; + } + + private cleanIdComponent(s: string | null) { + return (s ?? '') + .trim() + .replace(':', '-') + .replaceAll(' ', '-') + .toLowerCase(); + } +} diff --git a/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/form/http-methods.ts b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/form/http-methods.ts new file mode 100644 index 000000000..ac48610ce --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/form/http-methods.ts @@ -0,0 +1,11 @@ +export const DATA_SOURCE_HTTP_METHODS = [ + 'GET', + 'POST', + 'PUT', + 'PATCH', + 'DELETE', + 'OPTIONS', +]; +export const DATA_SINK_HTTP_METHODS = DATA_SOURCE_HTTP_METHODS.filter( + (it) => it !== 'GET', +); diff --git a/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/form/model/asset-advanced-form-model.ts b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/form/model/asset-advanced-form-model.ts new file mode 100644 index 000000000..8776f2fcb --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/form/model/asset-advanced-form-model.ts @@ -0,0 +1,36 @@ +import { + FormArray, + FormControl, + FormGroup, + ɵFormGroupValue, +} from '@angular/forms'; +import {DataCategorySelectItem} from '../../../../../../shared/form-elements/data-category-select/data-category-select-item'; +import {DataSubcategorySelectItem} from '../../../../../../shared/form-elements/data-subcategory-select/data-subcategory-select-item'; +import {TransportModeSelectItem} from '../../../../../../shared/form-elements/transport-mode-select/transport-mode-select-item'; +import {TemporalCoverageFormModel} from './temporal-coverage-form-model'; + +/** + * Form Model for AssetCreateDialog > Advanced + * (MDS Properties) + */ +export interface AssetAdvancedFormModel { + dataCategory: FormControl; + dataSubcategory: FormControl; + dataModel: FormControl; + geoReferenceMethod: FormControl; + transportMode: FormControl; + sovereignLegalName: FormControl; + geoLocation: FormControl; + nutsLocations: FormArray>; + dataSampleUrls: FormArray>; + referenceFileUrls: FormArray>; + referenceFilesDescription: FormControl; + conditionsForUse: FormControl; + dataUpdateFrequency: FormControl; + temporalCoverage: FormGroup; +} + +/** + * Form Value for AssetCreateDialog > Advanced + */ +export type AssetAdvancedFormValue = ɵFormGroupValue; diff --git a/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/form/model/asset-create-dialog-form-model.ts b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/form/model/asset-create-dialog-form-model.ts new file mode 100644 index 000000000..4a60ec3f3 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/form/model/asset-create-dialog-form-model.ts @@ -0,0 +1,19 @@ +import {FormGroup, ɵFormGroupValue} from '@angular/forms'; +import {AssetAdvancedFormModel} from './asset-advanced-form-model'; +import {AssetDatasourceFormModel} from './asset-datasource-form-model'; +import {AssetMetadataFormModel} from './asset-metadata-form-model'; + +/** + * Form Model for AssetCreateDialog + */ +export interface AssetCreateDialogFormModel { + metadata: FormGroup; + datasource: FormGroup; + advanced?: FormGroup; +} + +/** + * Form Value for AssetCreateDialog + */ +export type AssetCreateDialogFormValue = + ɵFormGroupValue; diff --git a/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/form/model/asset-datasource-form-enabled-ctrls.ts b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/form/model/asset-datasource-form-enabled-ctrls.ts new file mode 100644 index 000000000..3767c8248 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/form/model/asset-datasource-form-enabled-ctrls.ts @@ -0,0 +1,48 @@ +import { + AssetDatasourceFormModel, + AssetDatasourceFormValue, +} from './asset-datasource-form-model'; + +export const assetDatasourceFormEnabledCtrls = ( + value: AssetDatasourceFormValue, +): Record => { + const customDataAddressJson = + value.dataAddressType === 'Custom-Data-Address-Json'; + + const onRequest = value.dataAddressType === 'On-Request'; + + const http = value.dataAddressType === 'Http'; + const httpAuth = value.httpAuthHeaderType !== 'None'; + const httpAuthByValue = value.httpAuthHeaderType === 'Value'; + const httpAuthByVault = value.httpAuthHeaderType === 'Vault-Secret'; + const proxyPath = !!value.httpProxyPath; + + return { + dataAddressType: true, + + // Custom Datasource JSON + dataDestination: customDataAddressJson, + + // On Request Datasource + contactEmail: onRequest, + contactPreferredEmailSubject: onRequest, + + // Http Datasource Fields + httpUrl: http, + httpMethod: http, + + httpAuthHeaderType: http, + httpAuthHeaderName: http && httpAuth, + httpAuthHeaderValue: http && httpAuthByValue, + httpAuthHeaderSecretName: http && httpAuthByVault, + httpQueryParams: http, + + httpDefaultPath: http && proxyPath, + httpProxyMethod: http, + httpProxyPath: http, + httpProxyQueryParams: http, + httpProxyBody: http, + + httpHeaders: http, + }; +}; diff --git a/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/form/model/asset-datasource-form-model.ts b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/form/model/asset-datasource-form-model.ts new file mode 100644 index 000000000..1626a1da4 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/form/model/asset-datasource-form-model.ts @@ -0,0 +1,47 @@ +import { + FormArray, + FormControl, + FormGroup, + ɵFormGroupValue, +} from '@angular/forms'; +import {UiDataSourceHttpDataMethod} from '@sovity.de/edc-client'; +import {DataAddressType} from '../../../../../../shared/form-elements/data-address-type-select/data-address-type'; +import {HttpDatasourceAuthHeaderType} from './http-datasource-auth-header-type'; +import {HttpDatasourceHeaderFormModel} from './http-datasource-header-form-model'; +import {HttpDatasourceQueryParamFormModel} from './http-datasource-query-param-form-model'; + +/** + * Form Model for AssetCreateDialog > Datasource + */ +export interface AssetDatasourceFormModel { + dataAddressType: FormControl; + + // Custom Datasource JSON + dataDestination: FormControl; + + // On-Request Datasource + contactEmail: FormControl; + contactPreferredEmailSubject: FormControl; + + // Http Datasource + httpUrl: FormControl; + httpMethod: FormControl; + + httpAuthHeaderType: FormControl; + httpAuthHeaderName: FormControl; + httpAuthHeaderValue: FormControl; + httpAuthHeaderSecretName: FormControl; + httpHeaders: FormArray>; + httpQueryParams: FormArray>; + httpProxyMethod: FormControl; + httpProxyPath: FormControl; + httpProxyQueryParams: FormControl; + httpProxyBody: FormControl; + httpDefaultPath: FormControl; +} + +/** + * Form Value for AssetCreateDialog > Datasource + */ +export type AssetDatasourceFormValue = + ɵFormGroupValue; diff --git a/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/form/model/asset-metadata-form-model.ts b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/form/model/asset-metadata-form-model.ts new file mode 100644 index 000000000..1476396ea --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/form/model/asset-metadata-form-model.ts @@ -0,0 +1,23 @@ +import {FormControl, ɵFormGroupValue} from '@angular/forms'; +import {LanguageSelectItem} from '../../../../../../shared/form-elements/language-select/language-select-item'; + +/** + * Form Model for AssetCreateDialog > Metadata + */ +export interface AssetMetadataFormModel { + id: FormControl; + title: FormControl; + version: FormControl; + contentType: FormControl; + description: FormControl; + keywords: FormControl; + language: FormControl; + publisher: FormControl; + standardLicense: FormControl; + endpointDocumentation: FormControl; +} + +/** + * Form Value for AssetCreateDialog > Metadata + */ +export type AssetMetadataFormValue = ɵFormGroupValue; diff --git a/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/form/model/http-datasource-auth-header-type.ts b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/form/model/http-datasource-auth-header-type.ts new file mode 100644 index 000000000..c208d6fd0 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/form/model/http-datasource-auth-header-type.ts @@ -0,0 +1 @@ +export type HttpDatasourceAuthHeaderType = 'None' | 'Value' | 'Vault-Secret'; diff --git a/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/form/model/http-datasource-header-form-model.ts b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/form/model/http-datasource-header-form-model.ts new file mode 100644 index 000000000..5b0c0e694 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/form/model/http-datasource-header-form-model.ts @@ -0,0 +1,15 @@ +import {FormControl, ɵFormGroupValue} from '@angular/forms'; + +/** + * Form Model for AssetCreateDialog > Datasource > HTTP/REST > Header + */ +export interface HttpDatasourceHeaderFormModel { + headerName: FormControl; + headerValue: FormControl; +} + +/** + * Form Value for AssetCreateDialog > Datasource > HTTP/REST > Header + */ +export type HttpDatasourceHeaderFormValue = + ɵFormGroupValue; diff --git a/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/form/model/http-datasource-query-param-form-model.ts b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/form/model/http-datasource-query-param-form-model.ts new file mode 100644 index 000000000..deb3a3bd5 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/form/model/http-datasource-query-param-form-model.ts @@ -0,0 +1,15 @@ +import {FormControl, ɵFormGroupValue} from '@angular/forms'; + +/** + * Form Model for AssetCreateDialog > Datasource > HTTP/REST > Header + */ +export interface HttpDatasourceQueryParamFormModel { + paramName: FormControl; + paramValue: FormControl; +} + +/** + * Form Value for AssetCreateDialog > Datasource > HTTP/REST > QueryParam + */ +export type HttpDatasourceQueryParamFormValue = + ɵFormGroupValue; diff --git a/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/form/model/temporal-coverage-form-model.ts b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/form/model/temporal-coverage-form-model.ts new file mode 100644 index 000000000..0d02477e9 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-create-dialog/form/model/temporal-coverage-form-model.ts @@ -0,0 +1,15 @@ +import {FormControl, ɵFormGroupValue} from '@angular/forms'; + +/** + * Form Model for AssetCreateDialog > Advanced > Temporal Coverage + */ +export interface TemporalCoverageFormModel { + from: FormControl; + toInclusive: FormControl; +} + +/** + * Form Value for AssetCreateDialog > Advanced > Temporal Coverage + */ +export type TemporalCoverageFormValue = + ɵFormGroupValue; diff --git a/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-list-page.module.ts b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-list-page.module.ts new file mode 100644 index 000000000..369387622 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-list-page.module.ts @@ -0,0 +1,31 @@ +import {CommonModule} from '@angular/common'; +import {NgModule} from '@angular/core'; +import {FormsModule, ReactiveFormsModule} from '@angular/forms'; +import {RouterModule} from '@angular/router'; +import {SharedModule} from '../../../shared/shared.module'; +import {AssetCardsComponent} from './asset-cards/asset-cards.component'; +import {AssetCreateDialogComponent} from './asset-create-dialog/asset-create-dialog.component'; +import {AssetCreateDialogService} from './asset-create-dialog/asset-create-dialog.service'; +import {AssetCreateDialogFormMapper} from './asset-create-dialog/form/asset-create-dialog-form-mapper'; +import {AssetListPageComponent} from './asset-list-page/asset-list-page.component'; + +@NgModule({ + imports: [ + // Angular + CommonModule, + FormsModule, + ReactiveFormsModule, + RouterModule, + + // EDC UI Modules + SharedModule, + ], + declarations: [ + AssetCardsComponent, + AssetListPageComponent, + AssetCreateDialogComponent, + ], + providers: [AssetCreateDialogService, AssetCreateDialogFormMapper], + exports: [AssetListPageComponent], +}) +export class AssetListPageModule {} diff --git a/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-list-page/asset-list-page.component.html b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-list-page/asset-list-page.component.html new file mode 100644 index 000000000..9bcb147d8 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-list-page/asset-list-page.component.html @@ -0,0 +1,70 @@ +
+
+ + + {{ 'asset_list_page.search_assets' | translate }} + + search + + + + + + + +
+ + + + +
+ +
+ + + +
+ +
+ +
+
diff --git a/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-list-page/asset-list-page.component.scss b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-list-page/asset-list-page.component.scss new file mode 100644 index 000000000..08f88ff46 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-list-page/asset-list-page.component.scss @@ -0,0 +1,14 @@ +#wrapper { + margin: 20px; +} + +.contract-definition-card { + width: 500px; + margin-right: 15px; + margin-bottom: 15px; +} + +.search-form-field { + min-width: 200px; + width: 30%; +} diff --git a/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-list-page/asset-list-page.component.ts b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-list-page/asset-list-page.component.ts new file mode 100644 index 000000000..1d6827968 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/asset-list-page/asset-list-page/asset-list-page.component.ts @@ -0,0 +1,100 @@ +import {Component, OnDestroy, OnInit} from '@angular/core'; +import {Router} from '@angular/router'; +import {BehaviorSubject, Subject} from 'rxjs'; +import {filter, switchMap} from 'rxjs/operators'; +import {AssetService} from '../../../../core/services/asset.service'; +import {Fetched} from '../../../../core/services/models/fetched'; +import {UiAssetMapped} from '../../../../core/services/models/ui-asset-mapped'; +import {OnAssetEditClickFn} from '../../../../shared/business/asset-detail-dialog/asset-detail-dialog-data'; +import {AssetDetailDialogDataService} from '../../../../shared/business/asset-detail-dialog/asset-detail-dialog-data.service'; +import {AssetDetailDialogService} from '../../../../shared/business/asset-detail-dialog/asset-detail-dialog.service'; +import {AssetCreateDialogService} from '../asset-create-dialog/asset-create-dialog.service'; + +export interface AssetList { + filteredAssets: UiAssetMapped[]; + numTotalAssets: number; +} + +@Component({ + selector: 'asset-list-page', + templateUrl: './asset-list-page.component.html', + styleUrls: ['./asset-list-page.component.scss'], +}) +export class AssetListPageComponent implements OnInit, OnDestroy { + assetList: Fetched = Fetched.empty(); + searchText = ''; + private fetch$ = new BehaviorSubject(null); + + constructor( + private assetServiceMapped: AssetService, + private assetDetailDialogDataService: AssetDetailDialogDataService, + private assetDetailDialogService: AssetDetailDialogService, + private assetCreateDialogService: AssetCreateDialogService, + private router: Router, + ) {} + + ngOnInit(): void { + this.fetch$ + .pipe( + switchMap(() => this.assetServiceMapped.fetchAssets()), + Fetched.wrap({ + failureMessage: 'Failed fetching asset list.', + }), + ) + .pipe( + Fetched.map( + (assets): AssetList => ({ + filteredAssets: assets.filter((asset) => + asset.title + ?.toLowerCase() + .includes(this.searchText.toLowerCase()), + ), + numTotalAssets: assets.length, + }), + ), + ) + .subscribe((assetList) => (this.assetList = assetList)); + } + + onSearch() { + this.refresh(); + } + + onAssetClick(asset: UiAssetMapped) { + const onAssetEditClick: OnAssetEditClickFn = (asset) => { + this.router.navigate(['/my-assets', asset.assetId, 'edit']); + }; + + const buildDialogData = (asset: UiAssetMapped) => + this.assetDetailDialogDataService.assetDetailsEditable(asset, { + onAssetEditClick, + }); + + const data = buildDialogData(asset); + this.assetDetailDialogService + .open(data, this.ngOnDestroy$) + .pipe(filter((it) => !!it?.refreshList)) + .subscribe(() => this.refresh()); + } + + onCreate() { + this.assetCreateDialogService + .showCreateDialog(this.ngOnDestroy$) + .subscribe((result) => { + if (result?.refreshedList) { + this.refresh(); + } + }); + } + + private refresh() { + this.fetch$.next(null); + } + + ngOnDestroy$ = new Subject(); + + ngOnDestroy() { + this.ngOnDestroy$.next(null); + this.ngOnDestroy$.complete(); + } +} diff --git a/connector-ui/src/app/routes/connector-ui/catalog-browser-page/catalog-browser-fetch-detail-dialog/catalog-browser-fetch-detail-dialog.component.html b/connector-ui/src/app/routes/connector-ui/catalog-browser-page/catalog-browser-fetch-detail-dialog/catalog-browser-fetch-detail-dialog.component.html new file mode 100644 index 000000000..f1ce90db8 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/catalog-browser-page/catalog-browser-fetch-detail-dialog/catalog-browser-fetch-detail-dialog.component.html @@ -0,0 +1,36 @@ +
+ view_timeline +
+
+ {{ 'catalog_browser_page.fetch' | translate }} +
+
+ {{ 'catalog_browser_page.endpoint_catalogs' | translate }} +
+
+
+ +
+
    +
  • + {{ request.url }} +
      +
    • + {{ request.data.state }} +
    • +
    • + {{ errorMessage }} +
    • +
    +
  • +
+
+ +
+ + +
diff --git a/connector-ui/src/app/routes/connector-ui/catalog-browser-page/catalog-browser-fetch-detail-dialog/catalog-browser-fetch-detail-dialog.component.ts b/connector-ui/src/app/routes/connector-ui/catalog-browser-page/catalog-browser-fetch-detail-dialog/catalog-browser-fetch-detail-dialog.component.ts new file mode 100644 index 000000000..92c233c01 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/catalog-browser-page/catalog-browser-fetch-detail-dialog/catalog-browser-fetch-detail-dialog.component.ts @@ -0,0 +1,82 @@ +import {HttpErrorResponse} from '@angular/common/http'; +import {Component, Inject, OnDestroy, OnInit} from '@angular/core'; +import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog'; +import {Subject} from 'rxjs'; +import {takeUntil} from 'rxjs/operators'; +import {DataOffer} from '../../../../core/services/models/data-offer'; +import {Fetched} from '../../../../core/services/models/fetched'; +import {CatalogBrowserPageData} from '../catalog-browser-page/catalog-browser-page.data'; +import {CatalogBrowserFetchDetailDialogData} from './catalog-browser-fetch-detail-dialog.data'; + +@Component({ + selector: 'app-catalog-browser-fetch-detail-dialog', + templateUrl: './catalog-browser-fetch-detail-dialog.component.html', +}) +export class CatalogBrowserFetchDetailDialogComponent + implements OnInit, OnDestroy +{ + data: CatalogBrowserPageData | null = null; + + constructor( + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) + public dialogData: CatalogBrowserFetchDetailDialogData, + ) {} + + ngOnInit() { + this.dialogData.data$ + .pipe(takeUntil(this.ngOnDestroy$)) + .subscribe((data) => { + this.data = data; + }); + } + + ngOnDestroy$ = new Subject(); + + ngOnDestroy(): void { + this.ngOnDestroy$.next(null); + this.ngOnDestroy$.complete(); + } + + errorMessages(data: Fetched): string[] { + if (!data.isError) { + return []; + } + + const error = data.errorOrUndefined?.error; + if (error instanceof HttpErrorResponse) { + if (!error.status) { + return [ + 'Could not reach EDC backend. Please check your internet connection.', + ]; + } else if (error.status === 502) { + return [ + `Status ${error.status}`, + `EDC Backend failed fetching other connector catalog.`, + `Backend message: ${this.httpErrorResponseMessage(error)}`, + ]; + } else { + return [ + `Status ${error.status}`, + `EDC Backend Error`, + `Backend message: ${this.httpErrorResponseMessage(error)}`, + ]; + } + } else { + return [error?.message ?? 'Unknown UI error.']; + } + } + + private httpErrorResponseMessage(error: HttpErrorResponse) { + const childError = error.error; + if (childError?.message) { + return childError.message; + } else if (childError) { + return JSON.stringify(childError); + } else if (error.message) { + return error.message; + } else { + return error.statusText; + } + } +} diff --git a/connector-ui/src/app/routes/connector-ui/catalog-browser-page/catalog-browser-fetch-detail-dialog/catalog-browser-fetch-detail-dialog.data.ts b/connector-ui/src/app/routes/connector-ui/catalog-browser-page/catalog-browser-fetch-detail-dialog/catalog-browser-fetch-detail-dialog.data.ts new file mode 100644 index 000000000..387663d0f --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/catalog-browser-page/catalog-browser-fetch-detail-dialog/catalog-browser-fetch-detail-dialog.data.ts @@ -0,0 +1,7 @@ +import {Observable} from 'rxjs'; +import {CatalogBrowserPageData} from '../catalog-browser-page/catalog-browser-page.data'; + +export interface CatalogBrowserFetchDetailDialogData { + data$: Observable; + refresh: () => void; +} diff --git a/connector-ui/src/app/routes/connector-ui/catalog-browser-page/catalog-browser-page.module.ts b/connector-ui/src/app/routes/connector-ui/catalog-browser-page/catalog-browser-page.module.ts new file mode 100644 index 000000000..fcb2e29f4 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/catalog-browser-page/catalog-browser-page.module.ts @@ -0,0 +1,31 @@ +import {CommonModule} from '@angular/common'; +import {HttpClientModule} from '@angular/common/http'; +import {NgModule} from '@angular/core'; +import {FormsModule, ReactiveFormsModule} from '@angular/forms'; +import {RouterModule} from '@angular/router'; +import {SharedModule} from '../../../shared/shared.module'; +import {CatalogBrowserFetchDetailDialogComponent} from './catalog-browser-fetch-detail-dialog/catalog-browser-fetch-detail-dialog.component'; +import {CatalogBrowserPageService} from './catalog-browser-page/catalog-browser-page-service'; +import {CatalogBrowserPageComponent} from './catalog-browser-page/catalog-browser-page.component'; +import {DataOfferBuilder} from './catalog-browser-page/data-offer-builder'; + +@NgModule({ + imports: [ + // Angular + CommonModule, + HttpClientModule, + FormsModule, + ReactiveFormsModule, + RouterModule, + + // EDC UI Modules + SharedModule, + ], + declarations: [ + CatalogBrowserPageComponent, + CatalogBrowserFetchDetailDialogComponent, + ], + exports: [CatalogBrowserPageComponent], + providers: [CatalogBrowserPageService, DataOfferBuilder, DataOfferBuilder], +}) +export class CatalogBrowserPageModule {} diff --git a/connector-ui/src/app/routes/connector-ui/catalog-browser-page/catalog-browser-page/catalog-api-url.service.ts b/connector-ui/src/app/routes/connector-ui/catalog-browser-page/catalog-browser-page/catalog-api-url.service.ts new file mode 100644 index 000000000..a9f0797e8 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/catalog-browser-page/catalog-browser-page/catalog-api-url.service.ts @@ -0,0 +1,61 @@ +import {Inject, Injectable} from '@angular/core'; +import {APP_CONFIG, AppConfig} from '../../../../core/config/app-config'; + +/** + * Builds Catalog fetch URLs. Stores preset and user configured Connector Endpoints. + */ +@Injectable({ + providedIn: 'root', +}) +export class CatalogApiUrlService { + /** + * Preset Connector Endpoints to be used in catalog. + * + * From app-config.json, not user editable + */ + private readonly presetProviders = new Array(); + + /** + * User-added Connector Endpoints + */ + private customProviders = new Array(); + + constructor(@Inject(APP_CONFIG) private config: AppConfig) { + this.presetProviders = this.splitUrls(this.config.catalogUrls); + } + + /** + * Get all configured catalog URLs + */ + getAllProviders() { + return this.distinct([...this.presetProviders, ...this.customProviders]); + } + + /** + * Get preset catalog API URLs + */ + getPresetProviders(): string[] { + return this.presetProviders; + } + + setCustomProvidersAsString(connectorIdsCommaSeparated: string) { + this.setCustomProviders(this.splitUrls(connectorIdsCommaSeparated)); + } + + setCustomProviders(connectorIds: string[]) { + this.customProviders = connectorIds; + } + + private splitUrls(commaJoinedUrls?: string | null): string[] { + return ( + commaJoinedUrls + ?.split(',') + ?.map((url) => url.trim()) + ?.filter((url) => url.length) ?? [] + ); + } + + private distinct(array: T[]): T[] { + return [...new Set(array)]; + } +} diff --git a/connector-ui/src/app/routes/connector-ui/catalog-browser-page/catalog-browser-page/catalog-browser-page-service.ts b/connector-ui/src/app/routes/connector-ui/catalog-browser-page/catalog-browser-page/catalog-browser-page-service.ts new file mode 100644 index 000000000..db5c98d8e --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/catalog-browser-page/catalog-browser-page/catalog-browser-page-service.ts @@ -0,0 +1,104 @@ +import {Injectable} from '@angular/core'; +import {Observable, combineLatest} from 'rxjs'; +import {map, switchMap} from 'rxjs/operators'; +import {EdcApiService} from '../../../../core/services/api/edc-api.service'; +import {DataOffer} from '../../../../core/services/models/data-offer'; +import {Fetched} from '../../../../core/services/models/fetched'; +import {MultiFetched} from '../../../../core/services/models/multi-fetched'; +import {assetSearchTargets, search} from '../../../../core/utils/search-utils'; +import {CatalogApiUrlService} from './catalog-api-url.service'; +import { + CatalogBrowserPageData, + ContractOfferRequest, +} from './catalog-browser-page.data'; +import {DataOfferBuilder} from './data-offer-builder'; + +@Injectable() +export class CatalogBrowserPageService { + constructor( + private edcApiService: EdcApiService, + private catalogApiUrlService: CatalogApiUrlService, + private dataOfferBuilder: DataOfferBuilder, + ) {} + + contractOfferPageData$( + refresh$: Observable, + searchText$: Observable, + ): Observable { + return combineLatest([ + refresh$.pipe(switchMap(() => this.fetchCatalogs())), + searchText$, + ]).pipe( + map(([data, searchText]): CatalogBrowserPageData => { + // Merge fetch results + const contractOffers = data.requestTotals.data.flat(); + // Apply filter + const filteredContractOffers = this.filterContractOffers( + contractOffers, + searchText, + ); + + return { + requests: data.requests, + requestTotals: data.requestTotals, + filteredDataOffers: filteredContractOffers, + numTotalContractOffers: contractOffers.length, + }; + }), + ); + } + + filterContractOffers( + dataOffers: DataOffer[], + searchText: string, + ): DataOffer[] { + return search(dataOffers, searchText, (dataOffer) => + assetSearchTargets(dataOffer.asset), + ); + } + + fetchCatalogs(): Observable< + Pick + > { + // Prepare to fetch individual Catalogs + const urls = this.catalogApiUrlService.getAllProviders(); + const sources = urls.map((it) => + this.fetchDataOffers(it).pipe( + Fetched.wrap({failureMessage: 'Failed fetching catalog.'}), + ), + ); + + return combineLatest(sources).pipe( + map((results) => MultiFetched.aggregate(results)), + map( + ( + requestTotals: MultiFetched, + ): Pick => { + const presetUrls = this.catalogApiUrlService.getPresetProviders(); + return { + requestTotals, + requests: requestTotals.results.map( + (data, index): ContractOfferRequest => ({ + url: urls[index], + isPresetUrl: presetUrls.includes(urls[index]), + data, + }), + ), + }; + }, + ), + ); + } + + private fetchDataOffers(endpoint: string) { + return this.edcApiService + .getCatalogPageDataOffers(endpoint) + .pipe( + map((dataOffers) => + dataOffers.map((dataOffer) => + this.dataOfferBuilder.buildDataOffer(dataOffer), + ), + ), + ); + } +} diff --git a/connector-ui/src/app/routes/connector-ui/catalog-browser-page/catalog-browser-page/catalog-browser-page.component.html b/connector-ui/src/app/routes/connector-ui/catalog-browser-page/catalog-browser-page/catalog-browser-page.component.html new file mode 100644 index 000000000..f08c5c141 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/catalog-browser-page/catalog-browser-page/catalog-browser-page.component.html @@ -0,0 +1,120 @@ +
+
+ + + {{ 'catalog_browser_page.search' | translate }} + search + + + + + + + {{ + 'catalog_browser_page.con_endpoints' | translate + }} + link + + {{ presetProvidersMessage }} + + + + + + +
+ + + + +
+
+ + + + +
+
+ +
+
diff --git a/connector-ui/src/app/routes/connector-ui/catalog-browser-page/catalog-browser-page/catalog-browser-page.component.scss b/connector-ui/src/app/routes/connector-ui/catalog-browser-page/catalog-browser-page/catalog-browser-page.component.scss new file mode 100644 index 000000000..dae33aa22 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/catalog-browser-page/catalog-browser-page/catalog-browser-page.component.scss @@ -0,0 +1,13 @@ +#wrapper { + margin: 20px; +} + +.search-form-field { + min-width: 200px; + width: 30%; +} + +mat-paginator { + display: inline-block; + background-color: transparent; +} diff --git a/connector-ui/src/app/routes/connector-ui/catalog-browser-page/catalog-browser-page/catalog-browser-page.component.ts b/connector-ui/src/app/routes/connector-ui/catalog-browser-page/catalog-browser-page/catalog-browser-page.component.ts new file mode 100644 index 000000000..8309766f2 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/catalog-browser-page/catalog-browser-page/catalog-browser-page.component.ts @@ -0,0 +1,121 @@ +import {Component, OnDestroy, OnInit} from '@angular/core'; +import {FormControl} from '@angular/forms'; +import {MatDialog} from '@angular/material/dialog'; +import { + BehaviorSubject, + Observable, + Subject, + distinctUntilChanged, + sampleTime, + switchMap, +} from 'rxjs'; +import {filter, map, takeUntil} from 'rxjs/operators'; +import {TranslateService} from '@ngx-translate/core'; +import {ConnectorLimitsService} from '../../../../core/services/connector-limits.service'; +import {DataOffer} from '../../../../core/services/models/data-offer'; +import {value$} from '../../../../core/utils/form-group-utils'; +import {AssetDetailDialogDataService} from '../../../../shared/business/asset-detail-dialog/asset-detail-dialog-data.service'; +import {AssetDetailDialogService} from '../../../../shared/business/asset-detail-dialog/asset-detail-dialog.service'; +import {CatalogBrowserFetchDetailDialogComponent} from '../catalog-browser-fetch-detail-dialog/catalog-browser-fetch-detail-dialog.component'; +import {CatalogBrowserFetchDetailDialogData} from '../catalog-browser-fetch-detail-dialog/catalog-browser-fetch-detail-dialog.data'; +import {CatalogApiUrlService} from './catalog-api-url.service'; +import {CatalogBrowserPageService} from './catalog-browser-page-service'; +import {emptyCatalogBrowserPageData} from './catalog-browser-page.data'; + +@Component({ + selector: 'catalog-browser-page', + templateUrl: './catalog-browser-page.component.html', + styleUrls: ['./catalog-browser-page.component.scss'], +}) +export class CatalogBrowserPageComponent implements OnInit, OnDestroy { + data = emptyCatalogBrowserPageData(); + data$ = new BehaviorSubject(this.data); + searchText = new FormControl(''); + customProviders = ''; + presetProvidersMessage = ''; + private fetch$ = new BehaviorSubject(null); + + constructor( + private assetDetailDialogDataService: AssetDetailDialogDataService, + private assetDetailDialogService: AssetDetailDialogService, + private catalogBrowserPageService: CatalogBrowserPageService, + private catalogApiUrlService: CatalogApiUrlService, + private matDialog: MatDialog, + private connectorLimitsService: ConnectorLimitsService, + private translateService: TranslateService, + ) {} + + ngOnInit(): void { + this.catalogBrowserPageService + .contractOfferPageData$( + this.fetch$.pipe(sampleTime(200)), + this.searchText$(), + ) + .subscribe((data) => { + this.data = data; + this.data$.next(data); + }); + this.startBuildingPresetCatalogUrlsMessage(); + } + + onDataOfferClick(dataOffer: DataOffer) { + this.connectorLimitsService + .isConsumingAgreementLimitExceeded() + .pipe( + switchMap((isConsumingLimitsExceeded) => { + const data = this.assetDetailDialogDataService.dataOfferDetails( + dataOffer, + isConsumingLimitsExceeded, + ); + return this.assetDetailDialogService.open(data, this.ngOnDestroy$); + }), + filter((it) => !!it?.refreshList), + ) + .subscribe(() => this.fetch$.next(null)); + } + + onShowFetchDetails() { + const data: CatalogBrowserFetchDetailDialogData = { + data$: this.data$, + refresh: () => this.fetch$.next(null), + }; + this.matDialog.open(CatalogBrowserFetchDetailDialogComponent, {data}); + } + + onCatalogUrlsChange(): void { + this.catalogApiUrlService.setCustomProvidersAsString(this.customProviders); + this.fetch$.next(null); + } + + private startBuildingPresetCatalogUrlsMessage() { + this.translateService + .get(['catalog_browser_page.usage']) + .pipe(takeUntil(this.ngOnDestroy$)) + .subscribe((strings) => { + const urls = this.catalogApiUrlService.getPresetProviders(); + const usage = strings['catalog_browser_page.usage']; + this.presetProvidersMessage = !urls.length + ? '' + : `${usage} ${ + urls.length > 1 ? ` (${urls.length})` : '' + }: ${urls.join(', ')}`; + }); + } + + private searchText$(): Observable { + return (value$(this.searchText) as Observable).pipe( + map((it) => (it ?? '').trim()), + distinctUntilChanged(), + ); + } + + ngOnDestroy$ = new Subject(); + + ngOnDestroy() { + this.ngOnDestroy$.next(null); + this.ngOnDestroy$.complete(); + + // Reset selected Connector Endpoints + this.catalogApiUrlService.setCustomProviders([]); + } +} diff --git a/connector-ui/src/app/routes/connector-ui/catalog-browser-page/catalog-browser-page/catalog-browser-page.data.ts b/connector-ui/src/app/routes/connector-ui/catalog-browser-page/catalog-browser-page/catalog-browser-page.data.ts new file mode 100644 index 000000000..d5aab9bf6 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/catalog-browser-page/catalog-browser-page/catalog-browser-page.data.ts @@ -0,0 +1,25 @@ +import {DataOffer} from '../../../../core/services/models/data-offer'; +import {Fetched} from '../../../../core/services/models/fetched'; +import {MultiFetched} from '../../../../core/services/models/multi-fetched'; + +export interface CatalogBrowserPageData { + requestTotals: MultiFetched; + requests: ContractOfferRequest[]; + filteredDataOffers: DataOffer[]; + numTotalContractOffers: number; +} + +export function emptyCatalogBrowserPageData(): CatalogBrowserPageData { + return { + requests: [], + requestTotals: MultiFetched.empty(), + filteredDataOffers: [], + numTotalContractOffers: 0, + }; +} + +export interface ContractOfferRequest { + url: string; + isPresetUrl: boolean; + data: Fetched; +} diff --git a/connector-ui/src/app/routes/connector-ui/catalog-browser-page/catalog-browser-page/data-offer-builder.ts b/connector-ui/src/app/routes/connector-ui/catalog-browser-page/catalog-browser-page/data-offer-builder.ts new file mode 100644 index 000000000..782299344 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/catalog-browser-page/catalog-browser-page/data-offer-builder.ts @@ -0,0 +1,61 @@ +import {Injectable} from '@angular/core'; +import {TranslateService} from '@ngx-translate/core'; +import {UiContractOffer, UiDataOffer} from '@sovity.de/edc-client'; +import {AssetBuilder} from '../../../../core/services/asset-builder'; +import {ContractOffer} from '../../../../core/services/models/contract-offer'; +import {DataOffer} from '../../../../core/services/models/data-offer'; +import {UiAssetMapped} from '../../../../core/services/models/ui-asset-mapped'; +import {PolicyPropertyFieldBuilder} from '../../../../shared/business/asset-detail-dialog/policy-property-field-builder'; + +@Injectable() +export class DataOfferBuilder { + constructor( + private assetBuilder: AssetBuilder, + private policyPropertyFieldBuilder: PolicyPropertyFieldBuilder, + private translateService: TranslateService, + ) {} + buildDataOffer(dataOffer: UiDataOffer): DataOffer { + const asset = this.assetBuilder.buildAsset(dataOffer.asset); + return { + ...dataOffer, + asset, + contractOffers: dataOffer.contractOffers.map( + (contractOffer, iContractOffer): ContractOffer => { + return this.buildContractOffer( + dataOffer, + asset, + contractOffer, + iContractOffer, + ); + }, + ), + }; + } + + private buildContractOffer( + dataOffer: UiDataOffer, + asset: UiAssetMapped, + contractOffer: UiContractOffer, + iContractOffer: number, + ): ContractOffer { + const groupLabel = this.getGroupLabel( + iContractOffer, + dataOffer.contractOffers.length, + ); + return { + ...contractOffer, + properties: this.policyPropertyFieldBuilder.buildPolicyPropertyFields( + contractOffer.policy, + `${groupLabel} Contract Policy JSON-LD`, + asset.title, + ), + }; + } + + private getGroupLabel(i: number, total: number) { + const contract = this.translateService.instant( + 'catalog_browser_page.contract', + ); + return `${contract} ${total > 1 ? i : ''}`; + } +} diff --git a/connector-ui/src/app/routes/connector-ui/connector-ui-routing.module.ts b/connector-ui/src/app/routes/connector-ui/connector-ui-routing.module.ts new file mode 100644 index 000000000..04d609117 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/connector-ui-routing.module.ts @@ -0,0 +1,86 @@ +import {NgModule} from '@angular/core'; +import {RouterModule, Routes} from '@angular/router'; +import {AssetEditPageComponent} from './asset-edit-page/asset-edit-page/asset-edit-page.component'; +import {AssetListPageComponent} from './asset-list-page/asset-list-page/asset-list-page.component'; +import {CatalogBrowserPageComponent} from './catalog-browser-page/catalog-browser-page/catalog-browser-page.component'; +import {ConnectorUiComponent} from './connector-ui.component'; +import {ContractAgreementPageComponent} from './contract-agreement-page/contract-agreement-page/contract-agreement-page.component'; +import {ContractDefinitionPageComponent} from './contract-definition-page/contract-definition-page/contract-definition-page.component'; +import {DashboardPageComponent} from './dashboard-page/dashboard-page/dashboard-page.component'; +import {LogoutPageComponent} from './logout-page/logout-page.component'; +import {PolicyDefinitionCreatePageComponent} from './policy-definition-create-page/policy-definition-create-page/policy-definition-create-page.component'; +import {PolicyDefinitionPageComponent} from './policy-definition-page/policy-definition-page/policy-definition-page.component'; +import {TransferHistoryPageComponent} from './transfer-history-page/transfer-history-page/transfer-history-page.component'; + +export const routes: Routes = [ + { + path: '', + redirectTo: 'dashboard', + pathMatch: 'full', + }, + { + path: 'dashboard', + component: DashboardPageComponent, + data: {title: 'dashboard_page.title'}, + }, + { + path: 'create-asset', + component: AssetEditPageComponent, + data: {title: 'create_data_offer_page.title'}, + }, + { + path: 'catalog-browser', + component: CatalogBrowserPageComponent, + data: {title: 'catalog_browser_page.title'}, + }, + { + path: 'contracts', + component: ContractAgreementPageComponent, + data: {title: 'contract_agreement_page.title'}, + }, + { + path: 'transfer-history', + component: TransferHistoryPageComponent, + data: {title: 'transfer_history_page.title'}, + }, + { + path: 'my-assets', // must not be "assets" to prevent conflict with assets directory + component: AssetListPageComponent, + data: {title: 'asset_list_page.title'}, + }, + { + path: 'my-assets/:id/edit', + component: AssetEditPageComponent, + data: {title: 'edit_asset_page.title'}, + }, + { + path: 'policies', + component: PolicyDefinitionPageComponent, + data: {title: 'policy_definition_page.title'}, + }, + { + path: 'policies/create', + component: PolicyDefinitionCreatePageComponent, + data: {title: 'create_policy_page.title'}, + }, + { + path: 'data-offers', + component: ContractDefinitionPageComponent, + data: {title: 'contract_definition_page.title'}, + }, + { + path: 'logout', + component: LogoutPageComponent, + data: {title: 'logout_page.title'}, + }, +]; + +@NgModule({ + imports: [ + RouterModule.forChild([ + {path: '', component: ConnectorUiComponent, children: routes}, + ]), + ], + exports: [RouterModule], +}) +export class ConnectorUiRoutingModule {} diff --git a/connector-ui/src/app/routes/connector-ui/connector-ui.component.html b/connector-ui/src/app/routes/connector-ui/connector-ui.component.html new file mode 100644 index 000000000..9b31b4cfb --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/connector-ui.component.html @@ -0,0 +1,84 @@ + + + + Logo + +
+ +
+
+ Do you have feedback or need help? +

Select  SAML  login on following page

+ + CREATE TICKET + + + TUTORIAL & FAQ + +
+
+ powered by + sovity logo +
+
+ + + + {{ + titleService.getTitle() || '' | translate + }} + +
+ +
+
+
diff --git a/connector-ui/src/app/routes/connector-ui/connector-ui.component.scss b/connector-ui/src/app/routes/connector-ui/connector-ui.component.scss new file mode 100644 index 000000000..67fc9a63d --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/connector-ui.component.scss @@ -0,0 +1,36 @@ +.sidenav-container { + height: 100%; +} + +.sidenav { + width: 300px; +} + +.sidenav .mat-toolbar { + background: inherit; +} + +.mat-toolbar.mat-primary { + position: sticky; + top: 0; + z-index: 1; +} + +#contact-form { + text-align: center; + background: #ffffff; +} + +#contact-form a { + display: block; + margin: 16px 16px; +} + +.custom-toolbar { + margin: 3px 3px; +} + +.is-active { + cursor: pointer; + background-color: #dedede; +} diff --git a/connector-ui/src/app/routes/connector-ui/connector-ui.component.ts b/connector-ui/src/app/routes/connector-ui/connector-ui.component.ts new file mode 100644 index 000000000..80952b8e1 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/connector-ui.component.ts @@ -0,0 +1,55 @@ +import {BreakpointObserver, Breakpoints} from '@angular/cdk/layout'; +import {Component, Inject, OnInit} from '@angular/core'; +import {Title} from '@angular/platform-browser'; +import {Observable} from 'rxjs'; +import {map, shareReplay} from 'rxjs/operators'; +import {TranslateService} from '@ngx-translate/core'; +import {NavItemGroup} from 'src/app/core/services/models/nav-item-group'; +import {NavItemsBuilder} from 'src/app/core/services/nav-items-builder'; +import {isLanguageSupported} from 'src/app/core/utils/i18n-utils'; +import {LocalStoredValue} from 'src/app/core/utils/local-stored-value'; +import {APP_CONFIG, AppConfig} from '../../core/config/app-config'; +import {LoginPollingService} from '../../core/services/login-polling.service'; + +@Component({ + selector: 'connector-ui-page-layout', + templateUrl: './connector-ui.component.html', + styleUrls: ['./connector-ui.component.scss'], +}) +export class ConnectorUiComponent implements OnInit { + isHandset$: Observable = this.breakpointObserver + .observe(Breakpoints.Handset) + .pipe( + map((result) => result.matches), + shareReplay(), + ); + + navItemGroups: NavItemGroup[] = []; + + selectedLanguage = new LocalStoredValue( + 'en', + 'selectedLanguage', + isLanguageSupported, + ); + + constructor( + @Inject(APP_CONFIG) public config: AppConfig, + public titleService: Title, + private breakpointObserver: BreakpointObserver, + private loginPollingService: LoginPollingService, + private navItemsBuilder: NavItemsBuilder, + private translateService: TranslateService, + ) { + this.navItemGroups = this.navItemsBuilder.buildNavItemGroups(); + this.translateService.setDefaultLang('en'); + this.translateService.use(this.selectedLanguage.value); + } + + ngOnInit() { + this.startLoginPolling(); + } + + private startLoginPolling() { + this.loginPollingService.startPolling(); + } +} diff --git a/connector-ui/src/app/routes/connector-ui/connector-ui.module.ts b/connector-ui/src/app/routes/connector-ui/connector-ui.module.ts new file mode 100644 index 000000000..1419ee622 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/connector-ui.module.ts @@ -0,0 +1,52 @@ +import {CommonModule} from '@angular/common'; +import {HttpClient} from '@angular/common/http'; +import {NgModule} from '@angular/core'; +import {SharedModule} from '../../shared/shared.module'; +import {AssetEditPageModule} from './asset-edit-page/asset-edit-page.module'; +import {AssetListPageModule} from './asset-list-page/asset-list-page.module'; +import {CatalogBrowserPageModule} from './catalog-browser-page/catalog-browser-page.module'; +import {ConnectorUiRoutingModule} from './connector-ui-routing.module'; +import {ConnectorUiComponent} from './connector-ui.component'; +import {ContractAgreementPageModule} from './contract-agreement-page/contract-agreement-page.module'; +import {ContractDefinitionPageModule} from './contract-definition-page/contract-definition-page.module'; +import {DashboardPageModule} from './dashboard-page/dashboard-page.module'; +import {LocationHistoryUtils} from './logout-page/location-history-utils'; +import {LogoutPageModule} from './logout-page/logout-page.module'; +import {PreviousRouteListener} from './logout-page/previous-route-listener'; +import {PageNotFoundPageModule} from './page-not-found-page/page-not-found-page.module'; +import {PolicyDefinitionCreatePageModule} from './policy-definition-create-page/policy-definition-create-page.module'; +import {PolicyDefinitionPageModule} from './policy-definition-page/policy-definition-page.module'; +import {TransferHistoryPageModule} from './transfer-history-page/transfer-history-page.module'; + +@NgModule({ + imports: [ + // Angular + CommonModule, + + // Features + SharedModule, + + // Pages + AssetListPageModule, + AssetEditPageModule, + CatalogBrowserPageModule, + ContractAgreementPageModule, + ContractDefinitionPageModule, + DashboardPageModule, + LogoutPageModule, + PolicyDefinitionPageModule, + PolicyDefinitionCreatePageModule, + TransferHistoryPageModule, + PageNotFoundPageModule, + + // Routing + ConnectorUiRoutingModule, + ], + declarations: [ConnectorUiComponent], + providers: [PreviousRouteListener, LocationHistoryUtils, HttpClient], +}) +export class ConnectorUiModule { + constructor(previousRouteListener: PreviousRouteListener) { + // Ensure PreviousRouteListener is instantiated + } +} diff --git a/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-cards/contract-agreement-card-mapped.service.ts b/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-cards/contract-agreement-card-mapped.service.ts new file mode 100644 index 000000000..16178463c --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-cards/contract-agreement-card-mapped.service.ts @@ -0,0 +1,64 @@ +import {Injectable} from '@angular/core'; +import {ContractAgreementCard} from '@sovity.de/edc-client'; +import {AssetBuilder} from '../../../../core/services/asset-builder'; +import {assetSearchTargets, search} from '../../../../core/utils/search-utils'; +import {ContractAgreementCardMapped} from './contract-agreement-card-mapped'; + +@Injectable({providedIn: 'root'}) +export class ContractAgreementCardMappedService { + constructor(private assetBuilder: AssetBuilder) {} + + /** + * Replace the asset with the parsed asset and add the other required fields of the UI model + * + * {@link ContractAgreementCardMapped} + * @param contractAgreement {@link ContractAgreementCard} + * @returns {@link ContractAgreementCardMapped} + */ + buildContractAgreementCardMapped( + contractAgreement: ContractAgreementCard, + ): ContractAgreementCardMapped { + const asset = this.assetBuilder.buildAsset(contractAgreement.asset); + const isTerminated = contractAgreement.terminationStatus === 'TERMINATED'; + + return { + ...contractAgreement, + asset, + isInProgress: contractAgreement.transferProcesses.some( + (it) => it.state.simplifiedState === 'RUNNING', + ), + isConsumingLimitsEnforced: false, + isTerminated: isTerminated, + searchTargets: [ + contractAgreement.contractAgreementId, + contractAgreement.counterPartyId, + contractAgreement.counterPartyAddress, + ...assetSearchTargets(asset), + ], + }; + } + + filter( + cards: ContractAgreementCardMapped[], + searchText: string, + ): ContractAgreementCardMapped[] { + return search(cards, searchText, (card) => card.searchTargets); + } + + withEnforcedLimits( + maxConsumingContracts: number, + agreements: ContractAgreementCardMapped[], + ): ContractAgreementCardMapped[] { + return agreements.map((it) => { + if (it.isTerminated) { + return it; + } + + const modifiedAgreement = { + ...it, + isConsumingLimitsEnforced: true, + }; + return modifiedAgreement; + }); + } +} diff --git a/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-cards/contract-agreement-card-mapped.ts b/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-cards/contract-agreement-card-mapped.ts new file mode 100644 index 000000000..c544ab0e8 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-cards/contract-agreement-card-mapped.ts @@ -0,0 +1,13 @@ +import {ContractAgreementCard} from '@sovity.de/edc-client'; +import {UiAssetMapped} from '../../../../core/services/models/ui-asset-mapped'; + +export type ContractAgreementCardMapped = Omit< + ContractAgreementCard, + 'asset' +> & { + asset: UiAssetMapped; + isInProgress: boolean; + isConsumingLimitsEnforced: boolean; + isTerminated: boolean; + searchTargets: (string | null)[]; +}; diff --git a/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-cards/contract-agreement-cards.component.html b/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-cards/contract-agreement-cards.component.html new file mode 100644 index 000000000..e5c8e1bc4 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-cards/contract-agreement-cards.component.html @@ -0,0 +1,96 @@ +
+ + + + {{ + card.direction === 'PROVIDING' + ? card.isTerminated + ? 'file_upload_off' + : 'file_upload' + : card.isTerminated + ? 'file_download_off' + : 'file_download' + }} + + + + {{ card.asset.title }} + + + + {{ card.asset.creatorOrganizationName }} + + + + + + +
+ +
+
+ {{ 'general.signed' | translate }} +
+
+ +
+
+ + +
+
+ Transfers +
+
+ {{ card.transferProcesses.length | number }} +
+
+
+
+ +
+
+ {{ 'general.oth_connector' | translate }} +
+ +
+ +
+
+ Status +
+
+ {{ card.isTerminated ? 'Terminated' : 'Active' }} +
+
+
+ + + + + + +
+
+
diff --git a/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-cards/contract-agreement-cards.component.ts b/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-cards/contract-agreement-cards.component.ts new file mode 100644 index 000000000..21e1f48a2 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-cards/contract-agreement-cards.component.ts @@ -0,0 +1,29 @@ +import { + Component, + EventEmitter, + HostBinding, + Input, + Output, +} from '@angular/core'; +import {ContractAgreementCardMapped} from './contract-agreement-card-mapped'; + +@Component({ + selector: 'contract-agreement-cards', + templateUrl: './contract-agreement-cards.component.html', +}) +export class ContractAgreementCardsComponent { + @HostBinding('class.flex') + @HostBinding('class.flex-wrap') + @HostBinding('class.gap-[10px]') + cls = true; + + @Input() + contractAgreements: ContractAgreementCardMapped[] = []; + + @Output() + contractAgreementClick = new EventEmitter(); + + onContractAgreementClick(contractAgreement: ContractAgreementCardMapped) { + this.contractAgreementClick.emit(contractAgreement); + } +} diff --git a/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-page.module.ts b/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-page.module.ts new file mode 100644 index 000000000..c5e4e945d --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-page.module.ts @@ -0,0 +1,30 @@ +import {CommonModule} from '@angular/common'; +import {NgModule} from '@angular/core'; +import {FormsModule, ReactiveFormsModule} from '@angular/forms'; +import {RouterModule} from '@angular/router'; +import {SharedModule} from '../../../shared/shared.module'; +import {ContractAgreementCardsComponent} from './contract-agreement-cards/contract-agreement-cards.component'; +import {ContractAgreementPageComponent} from './contract-agreement-page/contract-agreement-page.component'; +import {ContractAgreementTerminationDialogComponent} from './contract-agreement-termination-dialog/contract-agreement-termination-dialog.component'; +import {ContractAgreementTransferDialogComponent} from './contract-agreement-transfer-dialog/contract-agreement-transfer-dialog.component'; + +@NgModule({ + imports: [ + // Angular + CommonModule, + FormsModule, + ReactiveFormsModule, + RouterModule, + + // EDC UI Modules + SharedModule, + ], + declarations: [ + ContractAgreementPageComponent, + ContractAgreementCardsComponent, + ContractAgreementTerminationDialogComponent, + ContractAgreementTransferDialogComponent, + ], + exports: [ContractAgreementPageComponent], +}) +export class ContractAgreementPageModule {} diff --git a/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-page/contract-agreement-page.component.html b/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-page/contract-agreement-page.component.html new file mode 100644 index 000000000..33762b650 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-page/contract-agreement-page.component.html @@ -0,0 +1,109 @@ +
+
+ + + {{ + 'contract_agreement_page.search_agree' | translate + }} + + search + + + + +
+ + Active Contracts + Terminated Contracts + All Contracts + +
+ + +
+ + + +
+ +
+ + + + +
+ +
+ + + + + + + + + + + + + +
+
diff --git a/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-page/contract-agreement-page.component.scss b/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-page/contract-agreement-page.component.scss new file mode 100644 index 000000000..bad3801ef --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-page/contract-agreement-page.component.scss @@ -0,0 +1,13 @@ +#wrapper { + margin: 20px; +} + +mat-paginator { + display: inline-block; + background-color: transparent; +} + +.search-form-field { + min-width: 200px; + width: 30%; +} diff --git a/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-page/contract-agreement-page.component.ts b/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-page/contract-agreement-page.component.ts new file mode 100644 index 000000000..5d8495fdb --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-page/contract-agreement-page.component.ts @@ -0,0 +1,151 @@ +import {Component, OnDestroy, OnInit} from '@angular/core'; +import {FormControl} from '@angular/forms'; +import { + BehaviorSubject, + EMPTY, + Observable, + Subject, + combineLatest, + concat, + distinctUntilChanged, + interval, + merge, + of, + share, + switchMap, +} from 'rxjs'; +import {catchError, filter, map, takeUntil} from 'rxjs/operators'; +import {ContractTerminationStatus} from '@sovity.de/edc-client'; +import {EdcApiService} from '../../../../core/services/api/edc-api.service'; +import {Fetched} from '../../../../core/services/models/fetched'; +import {value$} from '../../../../core/utils/form-group-utils'; +import {filterNotNull} from '../../../../core/utils/rxjs-utils'; +import {AssetDetailDialogDataService} from '../../../../shared/business/asset-detail-dialog/asset-detail-dialog-data.service'; +import {AssetDetailDialogService} from '../../../../shared/business/asset-detail-dialog/asset-detail-dialog.service'; +import {ContractAgreementCardMapped} from '../contract-agreement-cards/contract-agreement-card-mapped'; +import {ContractAgreementCardMappedService} from '../contract-agreement-cards/contract-agreement-card-mapped.service'; +import {ContractAgreementPageData} from './contract-agreement-page.data'; +import {ContractAgreementPageService} from './contract-agreement-page.service'; + +@Component({ + selector: 'app-contract-agreement-page', + templateUrl: './contract-agreement-page.component.html', + styleUrls: ['./contract-agreement-page.component.scss'], +}) +export class ContractAgreementPageComponent implements OnInit, OnDestroy { + page: Fetched = Fetched.empty(); + page$: Observable> = new Subject(); + searchText = new FormControl(''); + terminationFilterControl: FormControl = new FormControl('ONGOING'); + + terminationFilter: ContractTerminationStatus | null = 'ONGOING'; + + private fetch$ = new BehaviorSubject(null); + + constructor( + private assetDetailDialogDataService: AssetDetailDialogDataService, + private assetDetailDialogService: AssetDetailDialogService, + private contractAgreementPageService: ContractAgreementPageService, + private contractAgreementCardMappedService: ContractAgreementCardMappedService, + private apiService: EdcApiService, + ) {} + + ngOnInit(): void { + this.fetchContracts(); + } + + fetchContracts() { + this.page$ = this.contractAgreementPageService + .contractAgreementPageData$( + this.fetch$, + 5000, + this.searchText$(), + this.terminationFilter, + ) + .pipe(takeUntil(this.ngOnDestroy$), share()); + this.page$.subscribe((contractAgreementList) => { + this.page = contractAgreementList; + }); + } + + onTerminationFilterChange() { + if (this.terminationFilterControl.value === 'all') { + this.terminationFilter = null; + } else { + this.terminationFilter = this.terminationFilterControl + .value as ContractTerminationStatus; + } + + this.fetchContracts(); + } + + onContractAgreementClick(card: ContractAgreementCardMapped) { + const refresh$ = new Subject(); + + const cardUpdates$ = merge(interval(5000), refresh$).pipe( + switchMap(() => + this.apiService + .getContractAgreementById(card.contractAgreementId) + .pipe(catchError(() => EMPTY)), + ), + map((it) => + this.contractAgreementCardMappedService.buildContractAgreementCardMapped( + it, + ), + ), + ); + + const cardUpdatesWithCorrectActive$ = combineLatest([ + this.card$(card.contractAgreementId), + cardUpdates$, + ]).pipe( + map(([oldCard, newCard]) => ({ + ...newCard, + isConsumingLimitsEnforced: oldCard.isConsumingLimitsEnforced, + })), + ); + + const dialogData$ = merge(of(card), cardUpdatesWithCorrectActive$).pipe( + map((card) => + this.assetDetailDialogDataService.contractAgreementDetails(card, () => + refresh$.next(undefined), + ), + ), + ); + + return this.assetDetailDialogService.open(dialogData$, this.ngOnDestroy$); + } + + private card$( + contractAgreementId: string, + ): Observable { + return concat(of(this.page), this.page$).pipe( + filter((fetched) => fetched.isReady), + map((fetched) => fetched.data), + map((page) => + page.contractAgreements.find( + (it) => it.contractAgreementId === contractAgreementId, + ), + ), + filterNotNull(), + ); + } + + refresh() { + this.fetch$.next(null); + } + + private searchText$(): Observable { + return (value$(this.searchText) as Observable).pipe( + map((it) => (it ?? '').trim()), + distinctUntilChanged(), + ); + } + + ngOnDestroy$ = new Subject(); + + ngOnDestroy() { + this.ngOnDestroy$.next(null); + this.ngOnDestroy$.complete(); + } +} diff --git a/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-page/contract-agreement-page.data.ts b/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-page/contract-agreement-page.data.ts new file mode 100644 index 000000000..6856e2263 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-page/contract-agreement-page.data.ts @@ -0,0 +1,12 @@ +import {ContractAgreementPage} from '@sovity.de/edc-client'; +import {ContractAgreementCardMapped} from '../contract-agreement-cards/contract-agreement-card-mapped'; + +export type ContractAgreementPageData = Omit< + ContractAgreementPage, + 'contractAgreements' +> & { + contractAgreements: ContractAgreementCardMapped[]; + consumingContractAgreements: ContractAgreementCardMapped[]; + providingContractAgreements: ContractAgreementCardMapped[]; + numTotalContractAgreements: number; +}; diff --git a/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-page/contract-agreement-page.service.ts b/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-page/contract-agreement-page.service.ts new file mode 100644 index 000000000..4c127f0b7 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-page/contract-agreement-page.service.ts @@ -0,0 +1,154 @@ +import {Injectable} from '@angular/core'; +import {Observable, combineLatest, concat, interval, of} from 'rxjs'; +import {filter, map, switchMap} from 'rxjs/operators'; +import { + ConnectorLimits, + ContractAgreementCard, + ContractAgreementPage, + ContractTerminationStatus, + GetContractAgreementPageRequest, +} from '@sovity.de/edc-client'; +import {ActiveFeatureSet} from '../../../../core/config/active-feature-set'; +import {EdcApiService} from '../../../../core/services/api/edc-api.service'; +import {Fetched} from '../../../../core/services/models/fetched'; +import {ContractAgreementCardMapped} from '../contract-agreement-cards/contract-agreement-card-mapped'; +import {ContractAgreementCardMappedService} from '../contract-agreement-cards/contract-agreement-card-mapped.service'; +import {ContractAgreementPageData} from './contract-agreement-page.data'; + +@Injectable({providedIn: 'root'}) +export class ContractAgreementPageService { + constructor( + private edcApiService: EdcApiService, + private contractAgreementCardMappedService: ContractAgreementCardMappedService, + private activeFeatureSet: ActiveFeatureSet, + ) {} + + activeTerminationFilter?: ContractTerminationStatus | null; + + contractAgreementPageData$( + refresh$: Observable, + silentPollingInterval: number, + searchText$: Observable, + terminationStatusFilter?: ContractTerminationStatus | null, + ): Observable> { + this.activeTerminationFilter = terminationStatusFilter; + return combineLatest([ + refresh$.pipe( + switchMap(() => + concat( + this.fetchData(), + this.silentRefreshing(silentPollingInterval), + ), + ), + ), + searchText$, + ]).pipe( + map(([fetchedData, searchText]) => + fetchedData.map((contractAgreementPage) => + this.filterContractAgreementPage(contractAgreementPage, searchText), + ), + ), + ); + } + + private fetchData(): Observable> { + const requestBody: GetContractAgreementPageRequest = { + contractAgreementPageQuery: { + terminationStatus: this.activeTerminationFilter ?? undefined, + }, + }; + + return combineLatest([ + this.edcApiService.getContractAgreementPage(requestBody), + this.fetchLimits(), + ]).pipe( + map(([contractAgreementPage, connectorLimits]) => + this.buildContractAgreementPageData( + contractAgreementPage, + connectorLimits, + ), + ), + Fetched.wrap({failureMessage: 'Failed fetching Contract Agreement Page'}), + ); + } + + private buildContractAgreementPageData( + contractAgreementPage: ContractAgreementPage, + connectorLimits: ConnectorLimits | null, + ): ContractAgreementPageData { + const contractAgreements = this.mapContractAgreements( + contractAgreementPage.contractAgreements, + ); + + let consumingContractAgreements = contractAgreements.filter( + (it) => it.direction === 'CONSUMING', + ); + const providingContractAgreements = contractAgreements.filter( + (it) => it.direction === 'PROVIDING', + ); + + const isConsumingLimitsEnforced = + connectorLimits?.maxActiveConsumingContractAgreements != null && + connectorLimits.maxActiveConsumingContractAgreements >= 0; + if (isConsumingLimitsEnforced) { + consumingContractAgreements = + this.contractAgreementCardMappedService.withEnforcedLimits( + connectorLimits?.maxActiveConsumingContractAgreements!, + consumingContractAgreements, + ); + } + + return { + contractAgreements: [ + ...providingContractAgreements, + ...consumingContractAgreements, + ], + consumingContractAgreements, + providingContractAgreements, + numTotalContractAgreements: contractAgreements.length, + }; + } + + private mapContractAgreements( + contractAgreements: ContractAgreementCard[], + ): ContractAgreementCardMapped[] { + return contractAgreements.map((contractAgreement) => + this.contractAgreementCardMappedService.buildContractAgreementCardMapped( + contractAgreement, + ), + ); + } + + private filterContractAgreementPage( + contractAgreementPage: ContractAgreementPageData, + searchText: string, + ): ContractAgreementPageData { + return { + ...contractAgreementPage, + consumingContractAgreements: + this.contractAgreementCardMappedService.filter( + contractAgreementPage.consumingContractAgreements, + searchText, + ), + providingContractAgreements: + this.contractAgreementCardMappedService.filter( + contractAgreementPage.providingContractAgreements, + searchText, + ), + }; + } + + private silentRefreshing(silentPollingInterval: number) { + return interval(silentPollingInterval).pipe( + switchMap(() => this.fetchData()), + filter((it) => it.isReady), + ); + } + + private fetchLimits(): Observable { + if (this.activeFeatureSet.hasConnectorLimits()) { + return this.edcApiService.getEnterpriseEditionConnectorLimits(); + } + return of(null); + } +} diff --git a/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-termination-dialog/contract-agreement-termination-dialog-data.ts b/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-termination-dialog/contract-agreement-termination-dialog-data.ts new file mode 100644 index 000000000..244defa73 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-termination-dialog/contract-agreement-termination-dialog-data.ts @@ -0,0 +1,6 @@ +import {UiAssetMapped} from '../../../../core/services/models/ui-asset-mapped'; + +export interface ContractAgreementTerminationDialogData { + contractId: string; + asset: UiAssetMapped; +} diff --git a/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-termination-dialog/contract-agreement-termination-dialog-form-model.ts b/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-termination-dialog/contract-agreement-termination-dialog-form-model.ts new file mode 100644 index 000000000..747fcdd36 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-termination-dialog/contract-agreement-termination-dialog-form-model.ts @@ -0,0 +1,15 @@ +import {FormControl, ɵFormGroupValue} from '@angular/forms'; + +/** + * Form Value Type + */ +export type ContractAgreementTransferDialogFormValue = + ɵFormGroupValue; + +/** + * Form Group Template Type + */ +export interface ContractAgreementTerminationDialogFormModel { + shortReason: FormControl; + detailedReason: FormControl; +} diff --git a/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-termination-dialog/contract-agreement-termination-dialog-form.ts b/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-termination-dialog/contract-agreement-termination-dialog-form.ts new file mode 100644 index 000000000..aaf2af2d5 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-termination-dialog/contract-agreement-termination-dialog-form.ts @@ -0,0 +1,32 @@ +import {Injectable} from '@angular/core'; +import {FormBuilder, FormGroup, Validators} from '@angular/forms'; +import { + ContractAgreementTerminationDialogFormModel, + ContractAgreementTransferDialogFormValue, +} from './contract-agreement-termination-dialog-form-model'; + +/** + * Handles AngularForms for ContractAgreementTerminationDialog + */ +@Injectable() +export class ContractAgreementTerminationDialogForm { + all = this.buildFormGroup(); + + /** + * Quick access to full value + */ + get value(): ContractAgreementTransferDialogFormValue { + return this.all.value; + } + + constructor(private formBuilder: FormBuilder) {} + + buildFormGroup(): FormGroup { + const formGroup = this.formBuilder.nonNullable.group({ + shortReason: ['Terminated by user', Validators.required], + detailedReason: ['', [Validators.required, Validators.maxLength(1000)]], + }); + formGroup.controls.shortReason.disable(); + return formGroup; + } +} diff --git a/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-termination-dialog/contract-agreement-termination-dialog-result.ts b/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-termination-dialog/contract-agreement-termination-dialog-result.ts new file mode 100644 index 000000000..753cd2db8 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-termination-dialog/contract-agreement-termination-dialog-result.ts @@ -0,0 +1,4 @@ +export interface ContractAgreementTerminationDialogResult { + contractId: string; + lastUpdatedTime: Date | null; +} diff --git a/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-termination-dialog/contract-agreement-termination-dialog.component.html b/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-termination-dialog/contract-agreement-termination-dialog.component.html new file mode 100644 index 000000000..64cd56a8f --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-termination-dialog/contract-agreement-termination-dialog.component.html @@ -0,0 +1,71 @@ +

Terminate Contract Agreement

+ +
+
+
+ By clicking the 'Terminate Contract' button, you will terminate the + contract agreement. New data transfers via this agreement will no longer + be possible and current transfer processes will be canceled. +
+
This action is irreversible.
+
+ +
+
Termination Details
+ + + Reason + + + + + Detailed reason + + {{ form.all.controls.detailedReason.value.length }}/1000 + characters + + Maximum 1000 characters allowed. + + + Field is required. + + +
+
+ + +
+ + I understand the consequences of terminating a contract. + +
+
+ + + + + + diff --git a/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-termination-dialog/contract-agreement-termination-dialog.component.ts b/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-termination-dialog/contract-agreement-termination-dialog.component.ts new file mode 100644 index 000000000..84a32dc84 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-termination-dialog/contract-agreement-termination-dialog.component.ts @@ -0,0 +1,88 @@ +import {Component, Inject, OnDestroy} from '@angular/core'; +import {MatCheckboxChange} from '@angular/material/checkbox'; +import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog'; +import {Observable, Subject} from 'rxjs'; +import {finalize} from 'rxjs/operators'; +import {IdResponseDto} from '@sovity.de/edc-client'; +import {EdcApiService} from '../../../../core/services/api/edc-api.service'; +import {NotificationService} from '../../../../core/services/notification.service'; +import {ContractAgreementTerminationDialogData} from './contract-agreement-termination-dialog-data'; +import {ContractAgreementTerminationDialogForm} from './contract-agreement-termination-dialog-form'; +import {ContractAgreementTerminationDialogResult} from './contract-agreement-termination-dialog-result'; + +@Component({ + selector: 'contract-agreement-transfer-dialog', + templateUrl: './contract-agreement-termination-dialog.component.html', + providers: [ContractAgreementTerminationDialogForm], +}) +export class ContractAgreementTerminationDialogComponent implements OnDestroy { + loading = false; + checkboxChecked = false; + + constructor( + public form: ContractAgreementTerminationDialogForm, + private dialogRef: MatDialogRef, + private edcApiService: EdcApiService, + private notificationService: NotificationService, + @Inject(MAT_DIALOG_DATA) + public data: ContractAgreementTerminationDialogData, + ) {} + + public onCheckboxChange($event: MatCheckboxChange) { + this.checkboxChecked = $event.checked; + } + + onSave() { + if (this.loading && !this.form.all.valid) { + return; + } + + this.initiateTermination(); + } + + private initiateTermination() { + this.loading = true; + this.form.all.disable(); + + const value = this.form.value; + let request$: Observable; + request$ = this.edcApiService.terminateContractAgreement({ + contractAgreementId: this.data.contractId, + contractTerminationRequest: { + reason: value.shortReason!, + detail: value.detailedReason!, + }, + }); + + request$ + .pipe( + finalize(() => { + this.loading = false; + this.form.all.enable(); + }), + ) + .subscribe({ + next: (response) => { + this.close({ + contractId: response.id, + lastUpdatedTime: response.lastUpdatedDate, + }); + }, + error: (error) => { + this.notificationService.showError('Contract termination failed!'); + console.error('Contract termination failed', error); + }, + }); + } + + private close(params: ContractAgreementTerminationDialogResult) { + this.dialogRef.close(params); + } + + ngOnDestroy$ = new Subject(); + + ngOnDestroy(): void { + this.ngOnDestroy$.next(null); + this.ngOnDestroy$.complete(); + } +} diff --git a/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/contract-agreement-transfer-dialog-data.ts b/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/contract-agreement-transfer-dialog-data.ts new file mode 100644 index 000000000..7efa74e0f --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/contract-agreement-transfer-dialog-data.ts @@ -0,0 +1,6 @@ +import {UiAssetMapped} from '../../../../core/services/models/ui-asset-mapped'; + +export interface ContractAgreementTransferDialogData { + contractId: string; + asset: UiAssetMapped; +} diff --git a/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/contract-agreement-transfer-dialog-form-model.ts b/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/contract-agreement-transfer-dialog-form-model.ts new file mode 100644 index 000000000..65b6da841 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/contract-agreement-transfer-dialog-form-model.ts @@ -0,0 +1,50 @@ +import { + FormArray, + FormControl, + FormGroup, + ɵFormGroupValue, +} from '@angular/forms'; +import {HttpDatasourceQueryParamFormModel} from '../../../../shared/business/edit-asset-form/form/model/http-datasource-query-param-form-model'; +import {DataAddressType} from '../../../../shared/form-elements/data-address-type-select/data-address-type'; +import {HttpDatasinkAuthHeaderType} from './http-datasink-auth-header-type'; +import {HttpDatasinkHeaderFormModel} from './http-datasink-header-form-model'; + +/** + * Form Value Type + */ +export type ContractAgreementTransferDialogFormValue = + ɵFormGroupValue; + +/** + * Form Group Template Type + */ +export interface ContractAgreementTransferDialogFormModel { + dataAddressType: FormControl; + + // Custom Datasink JSON + dataDestination: FormControl; + + // Custom Transfer Process Request JSON + transferProcessRequest: FormControl; + + // Http Datasink + httpUrl: FormControl; + httpMethod: FormControl; + + showAllHttpParameterizationFields: FormControl; + + httpAuthHeaderType: FormControl; + httpAuthHeaderName: FormControl; + httpAuthHeaderValue: FormControl; + httpAuthHeaderSecretName: FormControl; + httpHeaders: FormArray>; + + // Http Datasource Parameterization + httpProxiedPath: FormControl; + httpProxiedMethod: FormControl; + httpProxiedQueryParams: FormArray< + FormGroup + >; + httpProxiedBody: FormControl; + httpProxiedBodyContentType: FormControl; +} diff --git a/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/contract-agreement-transfer-dialog-form.ts b/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/contract-agreement-transfer-dialog-form.ts new file mode 100644 index 000000000..288b34753 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/contract-agreement-transfer-dialog-form.ts @@ -0,0 +1,147 @@ +import {Injectable} from '@angular/core'; +import {FormBuilder, FormGroup, Validators} from '@angular/forms'; +import {switchDisabledControls} from '../../../../core/utils/form-group-utils'; +import {jsonValidator} from '../../../../core/validators/json-validator'; +import {urlValidator} from '../../../../core/validators/url-validator'; +import {HttpDatasourceAuthHeaderType} from '../../../../shared/business/edit-asset-form/form/model/http-datasource-auth-header-type'; +import {HttpDatasourceQueryParamFormModel} from '../../../../shared/business/edit-asset-form/form/model/http-datasource-query-param-form-model'; +import {DataAddressType} from '../../../../shared/form-elements/data-address-type-select/data-address-type'; +import { + ContractAgreementTransferDialogFormModel, + ContractAgreementTransferDialogFormValue, +} from './contract-agreement-transfer-dialog-form-model'; +import {HttpDatasinkHeaderFormModel} from './http-datasink-header-form-model'; + +/** + * Handles AngularForms for ContractAgreementTransferDialog + */ +@Injectable() +export class ContractAgreementTransferDialogForm { + all = this.buildFormGroup(); + + /** + * Quick access to selected data address type + */ + get dataAddressType(): DataAddressType | null { + return this.all.controls.dataAddressType.value; + } + + /** + * Quick access to full value + */ + get value(): ContractAgreementTransferDialogFormValue { + return this.all.value; + } + + constructor(private formBuilder: FormBuilder) {} + + buildFormGroup(): FormGroup { + const all: FormGroup = + this.formBuilder.nonNullable.group({ + dataAddressType: 'Http' as DataAddressType, + dataDestination: ['', [Validators.required, jsonValidator]], + transferProcessRequest: ['', [Validators.required, jsonValidator]], + + // Http Datasink Fields + httpUrl: ['', [Validators.required, urlValidator]], + httpMethod: ['POST', Validators.required], + + httpAuthHeaderType: ['None' as HttpDatasourceAuthHeaderType], + httpAuthHeaderName: ['', Validators.required], + httpAuthHeaderValue: ['', Validators.required], + httpAuthHeaderSecretName: ['', Validators.required], + + httpHeaders: this.formBuilder.array( + new Array>(), + ), + + showAllHttpParameterizationFields: [false], + + httpProxiedPath: [''], + httpProxiedMethod: ['', Validators.required], + httpProxiedQueryParams: this.formBuilder.array( + new Array>(), + ), + httpProxiedBody: [''], + httpProxiedBodyContentType: [''], + }); + + switchDisabledControls( + all, + (value) => { + const customDataAddressJson = + value.dataAddressType === 'Custom-Data-Address-Json'; + + const customTransferProcessRequest = + value.dataAddressType === 'Custom-Transfer-Process-Request'; + + const http = value.dataAddressType === 'Http'; + const httpAuth = value.httpAuthHeaderType !== 'None'; + const httpAuthByValue = value.httpAuthHeaderType === 'Value'; + const httpAuthByVault = value.httpAuthHeaderType === 'Vault-Secret'; + + const httpProxiedMethod = !!value.showAllHttpParameterizationFields; + + return { + dataAddressType: true, + + // Custom Datasink JSON + dataDestination: customDataAddressJson, + transferProcessRequest: customTransferProcessRequest, + + // Http Datasink Fields + httpUrl: http, + httpMethod: http, + + httpAuthHeaderType: http, + httpAuthHeaderName: http && httpAuth, + httpAuthHeaderValue: http && httpAuthByValue, + httpAuthHeaderSecretName: http && httpAuthByVault, + + httpHeaders: http, + + showAllHttpParameterizationFields: !customTransferProcessRequest, + + httpProxiedPath: !customTransferProcessRequest, + httpProxiedMethod: !customTransferProcessRequest && httpProxiedMethod, + httpProxiedQueryParams: !customTransferProcessRequest, + httpProxiedBody: !customTransferProcessRequest, + httpProxiedBodyContentType: !customTransferProcessRequest, + }; + }, + ); + return all; + } + + buildHeaderFormGroup(): FormGroup { + return this.formBuilder.nonNullable.group({ + headerName: ['', Validators.required], + headerValue: ['', Validators.required], + }); + } + + buildQueryParamFormGroup(): FormGroup { + return this.formBuilder.nonNullable.group({ + paramName: ['', Validators.required], + paramValue: [''], + }); + } + + onHttpHeadersAddClick() { + this.all.controls.httpHeaders.push(this.buildHeaderFormGroup()); + } + + onHttpHeadersRemoveClick(index: number) { + this.all.controls.httpHeaders.removeAt(index); + } + + onHttpQueryParamsAddClick() { + this.all.controls.httpProxiedQueryParams.push( + this.buildQueryParamFormGroup(), + ); + } + + onHttpQueryParamsRemoveClick(index: number) { + this.all.controls.httpProxiedQueryParams.removeAt(index); + } +} diff --git a/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/contract-agreement-transfer-dialog-result.ts b/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/contract-agreement-transfer-dialog-result.ts new file mode 100644 index 000000000..db4068420 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/contract-agreement-transfer-dialog-result.ts @@ -0,0 +1,4 @@ +export interface ContractAgreementTransferDialogResult { + transferProcessId: string; + contractId: string; +} diff --git a/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/contract-agreement-transfer-dialog.component.html b/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/contract-agreement-transfer-dialog.component.html new file mode 100644 index 000000000..1414fa2cb --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/contract-agreement-transfer-dialog.component.html @@ -0,0 +1,410 @@ +

+ {{ 'contract_agreement_page.ini_transfer' | translate }} +

+ +
+
+
+ {{ 'contract_agreement_page.datasink' | translate }} +
+ + + + + + + {{ + 'contract_agreement_page.cus_datasink' | translate + }} + + + {{ validationMessages.invalidJsonMessage }} + + + + + + {{ + 'contract_agreement_page.cus_transfer' | translate + }} + + + {{ validationMessages.invalidJsonMessage }} + + + {{ 'contract_agreement_page.json_hint' | translate }} + + + + + + +
+ + + {{ 'general.method' | translate }} + + {{ method }} + + + + URL + + + {{ validationMessages.invalidUrlMessage }} + + +
+ +
{{ 'general.auth' | translate }}
+ + +
+ +
+ + + + Type + + + {{ 'general.header_sec' | translate }} + + {{ + 'general.header_val' | translate + }} + + +
+ + + {{ 'general.auth_header' | translate }} + + + + + + {{ 'general.auth_value' | translate }} + + + + + + {{ 'general.vault_secret' | translate }} + + +
+ + +
+ +
+ +
+ {{ 'general.add_header' | translate }} +
+ +
+ + + {{ 'general.header_name' | translate }} + + + + + + {{ 'general.header_value' | translate }} + + + + + +
+ + +
+ +
+
+ + +
+ {{ 'contract_agreement_page.http_para' | translate }} +
+ +
+ {{ 'contract_agreement_page.http_message' | translate }} +
+ +
+ {{ 'contract_agreement_page.res_url' | translate }} + {{ + '{baseUrl}{customSubPath}?{baseQueryParams}&{customQueryParams}' + }} +
+ +
+ + + + {{ + 'contract_agreement_page.cus_meth' | translate + }} + + + {{ method }} + + + {{ + 'contract_agreement_page.proxy_method' | translate + }} + + + + + + + {{ + 'contract_agreement_page.cus_sub' | translate + }} + + {{ + 'contract_agreement_page.proxy_path' | translate + }} + + +
+ + +
+ + + {{ + 'contract_agreement_page.cus_query' | translate + }} + + + {{ 'contract_agreement_page.proxy_query' | translate }} + + + + + + {{ 'general.value' | translate }} + + + + + +
+ +
+ + +
+
+ + + + + {{ + 'contract_agreement_page.req_cont' | translate + }} + + + {{ 'contract_agreement_page.proxy_body' | translate }} + + + + + + {{ + 'contract_agreement_page.req_body' | translate + }} + + + {{ 'contract_agreement_page.proxy_body' | translate }} + + + + +
+ +
+
+
+
+
+ + + + + + diff --git a/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/contract-agreement-transfer-dialog.component.ts b/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/contract-agreement-transfer-dialog.component.ts new file mode 100644 index 000000000..c2ae2e7c2 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/contract-agreement-transfer-dialog.component.ts @@ -0,0 +1,167 @@ +import {Component, Inject, OnDestroy} from '@angular/core'; +import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog'; +import {Observable, Subject} from 'rxjs'; +import {finalize} from 'rxjs/operators'; +import { + IdResponseDto, + InitiateCustomTransferRequest, + InitiateTransferRequest, +} from '@sovity.de/edc-client'; +import {EdcApiService} from '../../../../core/services/api/edc-api.service'; +import {DataAddressMapper} from '../../../../core/services/data-address-mapper'; +import {NotificationService} from '../../../../core/services/notification.service'; +import {TransferDataSinkMapper} from '../../../../core/services/transfer-data-sink-mapper'; +import {ValidationMessages} from '../../../../core/validators/validation-messages'; +import { + DATA_SINK_HTTP_METHODS, + DATA_SOURCE_HTTP_METHODS, +} from '../../../../shared/business/edit-asset-form/form/http-methods'; +import {ContractAgreementTransferDialogData} from './contract-agreement-transfer-dialog-data'; +import {ContractAgreementTransferDialogForm} from './contract-agreement-transfer-dialog-form'; +import {ContractAgreementTransferDialogFormValue} from './contract-agreement-transfer-dialog-form-model'; +import {ContractAgreementTransferDialogResult} from './contract-agreement-transfer-dialog-result'; + +@Component({ + selector: 'contract-agreement-transfer-dialog', + templateUrl: './contract-agreement-transfer-dialog.component.html', + providers: [ContractAgreementTransferDialogForm], +}) +export class ContractAgreementTransferDialogComponent implements OnDestroy { + loading = false; + + dataSinkMethods = DATA_SINK_HTTP_METHODS; + dataSourceMethods = DATA_SOURCE_HTTP_METHODS; + + get proxyMethod(): boolean { + return ( + this.showAllHttpParameterizationFields || + this.data.asset.httpDatasourceHintsProxyMethod === true + ); + } + + get proxyPath(): boolean { + return ( + this.showAllHttpParameterizationFields || + this.data.asset.httpDatasourceHintsProxyPath === true + ); + } + + get proxyQueryParams(): boolean { + return ( + this.showAllHttpParameterizationFields || + this.data.asset.httpDatasourceHintsProxyQueryParams === true + ); + } + + get proxyBody(): boolean { + return ( + this.showAllHttpParameterizationFields || + this.data.asset.httpDatasourceHintsProxyBody === true + ); + } + + get showHttpParameterizationToggleButton(): boolean { + return ( + this.data.asset.httpDatasourceHintsProxyMethod !== true || + this.data.asset.httpDatasourceHintsProxyPath !== true || + this.data.asset.httpDatasourceHintsProxyQueryParams !== true || + this.data.asset.httpDatasourceHintsProxyBody !== true + ); + } + + get showAllHttpParameterizationFields(): boolean { + return this.form.all.controls.showAllHttpParameterizationFields.value; + } + + constructor( + public form: ContractAgreementTransferDialogForm, + public validationMessages: ValidationMessages, + private dialogRef: MatDialogRef, + private edcApiService: EdcApiService, + private notificationService: NotificationService, + private httpRequestParamsMapper: TransferDataSinkMapper, + private dataAddressMapper: DataAddressMapper, + @Inject(MAT_DIALOG_DATA) public data: ContractAgreementTransferDialogData, + ) {} + + onSave() { + if (this.loading && !this.form.all.valid) { + return; + } + + this.initiateTransfer(); + } + + private initiateTransfer() { + this.loading = true; + this.form.all.disable(); + + const value = this.form.value; + let request$: Observable; + if (value.dataAddressType === 'Custom-Transfer-Process-Request') { + const request = this.buildCustomTransferRequest(value); + request$ = this.edcApiService.initiateCustomTransfer(request); + } else { + const request = this.buildTransferRequest(value); + request$ = this.edcApiService.initiateTransfer(request); + } + + request$ + .pipe( + finalize(() => { + this.loading = false; + this.form.all.enable(); + }), + ) + .subscribe({ + next: (response) => + this.close({ + transferProcessId: response.id!, + contractId: this.data.contractId, + }), + error: (err) => { + this.notificationService.showError('Failed initiating transfer!'); + console.error('Failed initiating transfer', err); + }, + }); + } + + private close(params: ContractAgreementTransferDialogResult) { + this.dialogRef.close(params); + } + + ngOnDestroy$ = new Subject(); + + ngOnDestroy(): void { + this.ngOnDestroy$.next(null); + this.ngOnDestroy$.complete(); + } + + private buildTransferRequest( + value: ContractAgreementTransferDialogFormValue, + ): InitiateTransferRequest { + const transferProcessProperties = + this.httpRequestParamsMapper.encodeHttpProxyTransferRequestProperties( + this.data.asset, + value, + ); + + const dataSinkProperties = + this.dataAddressMapper.buildDataAddressProperties(value) ?? {}; + + return { + contractAgreementId: this.data.contractId, + transferProcessProperties, + dataSinkProperties, + }; + } + + private buildCustomTransferRequest( + value: ContractAgreementTransferDialogFormValue, + ): InitiateCustomTransferRequest { + return { + contractAgreementId: this.data.contractId, + transferProcessRequestJsonLd: value.transferProcessRequest!, + }; + } +} diff --git a/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/http-datasink-auth-header-type.ts b/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/http-datasink-auth-header-type.ts new file mode 100644 index 000000000..af3bcb614 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/http-datasink-auth-header-type.ts @@ -0,0 +1 @@ +export type HttpDatasinkAuthHeaderType = 'None' | 'Value' | 'Vault-Secret'; diff --git a/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/http-datasink-header-form-model.ts b/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/http-datasink-header-form-model.ts new file mode 100644 index 000000000..0ce1303e2 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/http-datasink-header-form-model.ts @@ -0,0 +1,15 @@ +import {FormControl, ɵFormGroupValue} from '@angular/forms'; + +/** + * Form Model for ContractAgreementTransferDialog > Datasink > HTTP/REST > Header + */ +export interface HttpDatasinkHeaderFormModel { + headerName: FormControl; + headerValue: FormControl; +} + +/** + * Form Value for ContractAgreementTransferDialog > Datasink > HTTP/REST > Header + */ +export type HttpDatasinkHeaderFormValue = + ɵFormGroupValue; diff --git a/connector-ui/src/app/routes/connector-ui/contract-definition-page/asset-select/asset-select.component.html b/connector-ui/src/app/routes/connector-ui/contract-definition-page/asset-select/asset-select.component.html new file mode 100644 index 000000000..78b49f38e --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/contract-definition-page/asset-select/asset-select.component.html @@ -0,0 +1,27 @@ +
+ + {{ label }} + + +
+ {{ asset.title }} +
+
+
+
+ +
diff --git a/connector-ui/src/app/routes/connector-ui/contract-definition-page/asset-select/asset-select.component.ts b/connector-ui/src/app/routes/connector-ui/contract-definition-page/asset-select/asset-select.component.ts new file mode 100644 index 000000000..500c812b3 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/contract-definition-page/asset-select/asset-select.component.ts @@ -0,0 +1,38 @@ +import {Component, Input, OnDestroy} from '@angular/core'; +import {FormControl} from '@angular/forms'; +import {Subject} from 'rxjs'; +import {UiAssetMapped} from '../../../../core/services/models/ui-asset-mapped'; +import {AssetDetailDialogDataService} from '../../../../shared/business/asset-detail-dialog/asset-detail-dialog-data.service'; +import {AssetDetailDialogService} from '../../../../shared/business/asset-detail-dialog/asset-detail-dialog.service'; + +@Component({ + selector: 'asset-select', + templateUrl: './asset-select.component.html', +}) +export class AssetSelectComponent implements OnDestroy { + @Input() + label!: string; + + @Input() + control!: FormControl; + + @Input() + assets: UiAssetMapped[] = []; + + constructor( + private assetDetailDialogDataService: AssetDetailDialogDataService, + private assetDetailDialogService: AssetDetailDialogService, + ) {} + + onAssetClick(asset: UiAssetMapped) { + const data = this.assetDetailDialogDataService.assetDetailsReadonly(asset); + this.assetDetailDialogService.open(data, this.ngOnDestroy$); + } + + ngOnDestroy$ = new Subject(); + + ngOnDestroy() { + this.ngOnDestroy$.next(null); + this.ngOnDestroy$.complete(); + } +} diff --git a/connector-ui/src/app/routes/connector-ui/contract-definition-page/contract-definition-cards/contract-definition-card-builder.ts b/connector-ui/src/app/routes/connector-ui/contract-definition-page/contract-definition-cards/contract-definition-card-builder.ts new file mode 100644 index 000000000..57793559e --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/contract-definition-page/contract-definition-cards/contract-definition-card-builder.ts @@ -0,0 +1,126 @@ +import {Injectable} from '@angular/core'; +import { + ContractDefinitionEntry, + ContractDefinitionPage, + PolicyDefinitionDto, + UiCriterion, +} from '@sovity.de/edc-client'; +import {CRITERION_OPERATOR_SYMBOLS} from '../../../../core/services/api/model/criterion-type-ext'; +import {AssetProperty} from '../../../../core/services/models/asset-properties'; +import {UiAssetMapped} from '../../../../core/services/models/ui-asset-mapped'; +import {associateBy} from '../../../../core/utils/map-utils'; +import {assetSearchTargets} from '../../../../core/utils/search-utils'; +import { + ContractDefinitionCard, + ContractDefinitionCardCriterionValue, + ContractDefinitionCardPolicy, +} from './contract-definition-card'; + +@Injectable({providedIn: 'root'}) +export class ContractDefinitionCardBuilder { + buildContractDefinitionCards( + contractDefinitionPage: ContractDefinitionPage, + assets: UiAssetMapped[], + policyDefinitions: PolicyDefinitionDto[], + ): ContractDefinitionCard[] { + const assetById = associateBy(assets, (asset) => asset.assetId); + const policyDefinitionById = associateBy( + policyDefinitions, + (policyDefinition) => policyDefinition.policyDefinitionId, + ); + + return contractDefinitionPage.contractDefinitions.map( + (contractDefinition) => + this.buildContractDefinitionCard( + contractDefinition, + assetById, + policyDefinitionById, + ), + ); + } + + buildContractDefinitionCard( + contractDefinition: ContractDefinitionEntry, + assetById: Map, + policyDefinitionById: Map, + ): ContractDefinitionCard { + return { + id: contractDefinition.contractDefinitionId, + contractPolicy: this.extractPolicy( + contractDefinition.contractPolicyId, + policyDefinitionById, + ), + accessPolicy: this.extractPolicy( + contractDefinition.accessPolicyId, + policyDefinitionById, + ), + + criteria: contractDefinition.assetSelector.map((criterion) => ({ + label: this.extractCriterionOperation(criterion), + values: this.extractCriterionValues(criterion, assetById), + })), + detailJsonObj: contractDefinition, + }; + } + + private extractPolicy( + policyDefinitionId: string, + policyDefinitionsById: Map, + ): ContractDefinitionCardPolicy { + return { + policyDefinitionId: policyDefinitionId, + policyDefinition: policyDefinitionsById.get(policyDefinitionId) || null, + }; + } + + private extractCriterionOperation(criterion: UiCriterion): string { + const {operandLeft, operator} = criterion; + if ( + operandLeft === AssetProperty.id && + (operator === 'EQ' || operator === 'IN') + ) { + return 'Assets'; + } + + const operatorStr = CRITERION_OPERATOR_SYMBOLS[operator] ?? operator; + return `${operandLeft} ${operatorStr}`; + } + + private extractCriterionValues( + criterion: UiCriterion, + assetsById: Map, + ): ContractDefinitionCardCriterionValue[] { + const {operandLeft, operandRight} = criterion; + + let values: string[] = []; + if (operandRight.type === 'VALUE_LIST') { + values = operandRight.valueList ?? []; + } else { + values = [operandRight.value!!]; + } + + return values.map((it) => { + const stringType: ContractDefinitionCardCriterionValue = { + type: 'string', + value: it, + searchTargets: [it], + }; + + // Try to find asset + if (operandLeft === AssetProperty.id) { + const asset = assetsById.get(it); + if (asset) { + return { + type: 'asset', + asset, + searchTargets: assetSearchTargets(asset), + }; + } + + return stringType; + } + + return stringType; + }); + } +} diff --git a/connector-ui/src/app/routes/connector-ui/contract-definition-page/contract-definition-cards/contract-definition-card.ts b/connector-ui/src/app/routes/connector-ui/contract-definition-page/contract-definition-cards/contract-definition-card.ts new file mode 100644 index 000000000..ed1113e23 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/contract-definition-page/contract-definition-cards/contract-definition-card.ts @@ -0,0 +1,32 @@ +import { + ContractDefinitionEntry, + PolicyDefinitionDto, +} from '@sovity.de/edc-client'; +import {UiAssetMapped} from '../../../../core/services/models/ui-asset-mapped'; + +export interface ContractDefinitionCard { + id: string; + criteria: ContractDefinitionCardCriterion[]; + contractPolicy: ContractDefinitionCardPolicy; + accessPolicy: ContractDefinitionCardPolicy; + + detailJsonObj: ContractDefinitionEntry; +} + +export interface ContractDefinitionCardPolicy { + policyDefinitionId: string; + policyDefinition: PolicyDefinitionDto | null; +} + +export interface ContractDefinitionCardCriterion { + label: string; + values: ContractDefinitionCardCriterionValue[]; +} + +export interface ContractDefinitionCardCriterionValue { + type: 'string' | 'asset' | 'json'; + searchTargets: (string | null)[]; + value?: string; + asset?: UiAssetMapped; + json?: any; +} diff --git a/connector-ui/src/app/routes/connector-ui/contract-definition-page/contract-definition-cards/contract-definition-cards.component.html b/connector-ui/src/app/routes/connector-ui/contract-definition-page/contract-definition-cards/contract-definition-cards.component.html new file mode 100644 index 000000000..f221f9ea0 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/contract-definition-page/contract-definition-cards/contract-definition-cards.component.html @@ -0,0 +1,76 @@ +
+ + + policy + + + {{ card.id }} + + + {{ 'general.con_def' | translate }} + + +
+
+ {{ 'general.access_pol' | translate }} +
+
+ + {{ card.accessPolicy.policyDefinitionId }} + + + {{ card.accessPolicy.policyDefinitionId }} + +
+
+
+
+ {{ 'general.contract_policy' | translate }} +
+
+ + {{ card.contractPolicy.policyDefinitionId }} + + + {{ card.contractPolicy.policyDefinitionId }} + +
+
+
+
+ {{ criterion.label }} +
+
+ {{ + value.value + }} + {{ + value.json | json + }} + + {{ value.asset!.title }} + +
+
+
+
+
diff --git a/connector-ui/src/app/routes/connector-ui/contract-definition-page/contract-definition-cards/contract-definition-cards.component.ts b/connector-ui/src/app/routes/connector-ui/contract-definition-page/contract-definition-cards/contract-definition-cards.component.ts new file mode 100644 index 000000000..60b44cbaf --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/contract-definition-page/contract-definition-cards/contract-definition-cards.component.ts @@ -0,0 +1,111 @@ +import { + Component, + EventEmitter, + HostBinding, + Input, + OnDestroy, + Output, +} from '@angular/core'; +import {MatDialog, MatDialogRef} from '@angular/material/dialog'; +import {EMPTY, Subject} from 'rxjs'; +import {catchError, filter, tap} from 'rxjs/operators'; +import {TranslateService} from '@ngx-translate/core'; +import {PolicyDefinitionDto} from '@sovity.de/edc-client'; +import {EdcApiService} from '../../../../core/services/api/edc-api.service'; +import {UiAssetMapped} from '../../../../core/services/models/ui-asset-mapped'; +import {NotificationService} from '../../../../core/services/notification.service'; +import {AssetDetailDialogDataService} from '../../../../shared/business/asset-detail-dialog/asset-detail-dialog-data.service'; +import {AssetDetailDialogService} from '../../../../shared/business/asset-detail-dialog/asset-detail-dialog.service'; +import {ConfirmDialogModel} from '../../../../shared/common/confirmation-dialog/confirmation-dialog.component'; +import {JsonDialogComponent} from '../../../../shared/common/json-dialog/json-dialog.component'; +import {JsonDialogData} from '../../../../shared/common/json-dialog/json-dialog.data'; +import {ContractDefinitionCard} from './contract-definition-card'; + +@Component({ + selector: 'contract-definition-cards', + templateUrl: './contract-definition-cards.component.html', +}) +export class ContractDefinitionCardsComponent implements OnDestroy { + @HostBinding('class.flex') + @HostBinding('class.flex-wrap') + @HostBinding('class.gap-[10px]') + cls = true; + + @Input() + contractDefinitionCards: ContractDefinitionCard[] = []; + + @Input() + deleteBusy = false; + + @Output() + deleteDone = new EventEmitter(); + + constructor( + private edcApiService: EdcApiService, + private assetDetailDialogDataService: AssetDetailDialogDataService, + private assetDetailDialogService: AssetDetailDialogService, + private matDialog: MatDialog, + private notificationService: NotificationService, + private translateService: TranslateService, + ) {} + + onPolicyClick(policyDefinition: PolicyDefinitionDto) { + const data: JsonDialogData = { + title: policyDefinition.policyDefinitionId, + subtitle: 'Policy', + icon: 'policy', + objectForJson: JSON.parse(policyDefinition.policy.policyJsonLd), + }; + this.matDialog.open(JsonDialogComponent, {data}); + } + + onAssetClick(asset: UiAssetMapped) { + const data = this.assetDetailDialogDataService.assetDetailsReadonly(asset); + this.assetDetailDialogService + .open(data, this.ngOnDestroy$) + .pipe(filter((it) => !!it?.refreshList)) + .subscribe(() => this.deleteDone.emit()); + } + + onContractDefinitionClick(card: ContractDefinitionCard) { + let dialogRef: MatDialogRef; + const data: JsonDialogData = { + title: card.id, + subtitle: 'Data Offer', + icon: 'policy', + objectForJson: card.detailJsonObj, + toolbarButton: { + text: 'Delete', + icon: 'delete', + confirmation: ConfirmDialogModel.forDelete( + 'general.con_def', + card.id, + this.translateService, + ), + action: () => + this.edcApiService.deleteContractDefinition(card.id).pipe( + tap(() => { + this.notificationService.showInfo('Data Offer deleted!'); + this.deleteDone.emit(); + dialogRef?.close(); + }), + catchError((err) => { + const msg = `Failed deleting data offer with id ${card.id}`; + console.error(msg, err); + this.notificationService.showError(msg); + return EMPTY; + }), + ), + }, + }; + + dialogRef = this.matDialog.open(JsonDialogComponent, {data}); + } + + ngOnDestroy$ = new Subject(); + + ngOnDestroy() { + this.ngOnDestroy$.next(null); + this.ngOnDestroy$.complete(); + } +} diff --git a/connector-ui/src/app/routes/connector-ui/contract-definition-page/contract-definition-editor-dialog/contract-definition-editor-dialog-form-model.ts b/connector-ui/src/app/routes/connector-ui/contract-definition-page/contract-definition-editor-dialog/contract-definition-editor-dialog-form-model.ts new file mode 100644 index 000000000..188bfdf77 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/contract-definition-page/contract-definition-editor-dialog/contract-definition-editor-dialog-form-model.ts @@ -0,0 +1,19 @@ +import {FormControl, ɵFormGroupValue} from '@angular/forms'; +import {PolicyDefinitionDto} from '@sovity.de/edc-client'; +import {UiAssetMapped} from '../../../../core/services/models/ui-asset-mapped'; + +/** + * Form Value Type + */ +export type ContractDefinitionEditorDialogFormValue = + ɵFormGroupValue; + +/** + * Form Group Template Type + */ +export interface ContractDefinitionEditorDialogFormModel { + id: FormControl; + accessPolicy: FormControl; + contractPolicy: FormControl; + assets: FormControl; +} diff --git a/connector-ui/src/app/routes/connector-ui/contract-definition-page/contract-definition-editor-dialog/contract-definition-editor-dialog-form.ts b/connector-ui/src/app/routes/connector-ui/contract-definition-page/contract-definition-editor-dialog/contract-definition-editor-dialog-form.ts new file mode 100644 index 000000000..8cdc29a37 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/contract-definition-page/contract-definition-editor-dialog/contract-definition-editor-dialog-form.ts @@ -0,0 +1,43 @@ +import {Injectable} from '@angular/core'; +import {FormBuilder, FormGroup, Validators} from '@angular/forms'; +import {PolicyDefinitionDto} from '@sovity.de/edc-client'; +import {DataOfferFormValidators} from 'src/app/core/validators/data-offer-form-validators'; +import {UiAssetMapped} from '../../../../core/services/models/ui-asset-mapped'; +import {noWhitespacesOrColonsValidator} from '../../../../core/validators/no-whitespaces-or-colons-validator'; +import { + ContractDefinitionEditorDialogFormModel, + ContractDefinitionEditorDialogFormValue, +} from './contract-definition-editor-dialog-form-model'; + +/** + * Handles AngularForms for ContractDefinitionEditorDialog + */ +@Injectable() +export class ContractDefinitionEditorDialogForm { + group = this.buildFormGroup(); + + /** + * Quick access to full value + */ + get value(): ContractDefinitionEditorDialogFormValue { + return this.group.value; + } + + constructor( + private formBuilder: FormBuilder, + private validators: DataOfferFormValidators, + ) {} + + buildFormGroup(): FormGroup { + return this.formBuilder.nonNullable.group({ + id: [ + '', + [Validators.required, noWhitespacesOrColonsValidator], + [this.validators.contractDefinitionIdExistsValidator], + ], + accessPolicy: [null as PolicyDefinitionDto | null, Validators.required], + contractPolicy: [null as PolicyDefinitionDto | null, Validators.required], + assets: [[] as UiAssetMapped[], Validators.required], + }); + } +} diff --git a/connector-ui/src/app/routes/connector-ui/contract-definition-page/contract-definition-editor-dialog/contract-definition-editor-dialog-result.ts b/connector-ui/src/app/routes/connector-ui/contract-definition-page/contract-definition-editor-dialog/contract-definition-editor-dialog-result.ts new file mode 100644 index 000000000..81ebdf921 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/contract-definition-page/contract-definition-editor-dialog/contract-definition-editor-dialog-result.ts @@ -0,0 +1,6 @@ +export interface ContractDefinitionEditorDialogResult { + /** + * When creating a contract definition, update contract definition list + */ + refreshList: boolean; +} diff --git a/connector-ui/src/app/routes/connector-ui/contract-definition-page/contract-definition-editor-dialog/contract-definition-editor-dialog.component.html b/connector-ui/src/app/routes/connector-ui/contract-definition-page/contract-definition-editor-dialog/contract-definition-editor-dialog.component.html new file mode 100644 index 000000000..6326b0b34 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/contract-definition-page/contract-definition-editor-dialog/contract-definition-editor-dialog.component.html @@ -0,0 +1,53 @@ +

{{ 'contract_agreement_page.new_def' | translate }}

+ + +
+ + + ID + + {{ + validationMessages.invalidWhitespacesOrColonsMessage + }} + + {{ validationMessages.idExistsErrorMessage }} + + + + + + + + + + + +
+
+ + + + + + diff --git a/connector-ui/src/app/routes/connector-ui/contract-definition-page/contract-definition-editor-dialog/contract-definition-editor-dialog.component.ts b/connector-ui/src/app/routes/connector-ui/contract-definition-page/contract-definition-editor-dialog/contract-definition-editor-dialog.component.ts new file mode 100644 index 000000000..e0e9ddf89 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/contract-definition-page/contract-definition-editor-dialog/contract-definition-editor-dialog.component.ts @@ -0,0 +1,91 @@ +import {Component, OnDestroy, OnInit} from '@angular/core'; +import {MatDialogRef} from '@angular/material/dialog'; +import {Subject} from 'rxjs'; +import {finalize, takeUntil} from 'rxjs/operators'; +import {PolicyDefinitionDto} from '@sovity.de/edc-client'; +import {EdcApiService} from '../../../../core/services/api/edc-api.service'; +import {AssetService} from '../../../../core/services/asset.service'; +import {ContractDefinitionBuilder} from '../../../../core/services/contract-definition-builder'; +import {UiAssetMapped} from '../../../../core/services/models/ui-asset-mapped'; +import {NotificationService} from '../../../../core/services/notification.service'; +import {ValidationMessages} from '../../../../core/validators/validation-messages'; +import {ContractDefinitionEditorDialogForm} from './contract-definition-editor-dialog-form'; +import {ContractDefinitionEditorDialogResult} from './contract-definition-editor-dialog-result'; + +@Component({ + selector: 'contract-definition-editor-dialog', + templateUrl: './contract-definition-editor-dialog.component.html', + providers: [ContractDefinitionEditorDialogForm], +}) +export class ContractDefinitionEditorDialog implements OnInit, OnDestroy { + policies: PolicyDefinitionDto[] = []; + assets: UiAssetMapped[] = []; + loading = false; + + constructor( + private assetServiceMapped: AssetService, + public form: ContractDefinitionEditorDialogForm, + private notificationService: NotificationService, + private edcApiService: EdcApiService, + private contractDefinitionBuilder: ContractDefinitionBuilder, + private dialogRef: MatDialogRef, + public validationMessages: ValidationMessages, + ) {} + + ngOnInit() { + this.edcApiService + .getPolicyDefinitionPage() + .pipe(takeUntil(this.ngOnDestroy$)) + .subscribe((policyDefinitionPage) => { + this.policies = policyDefinitionPage.policies; + }); + this.assetServiceMapped + .fetchAssets() + .pipe(takeUntil(this.ngOnDestroy$)) + .subscribe((assets) => { + this.assets = assets; + }); + } + + onCreate() { + const formValue = this.form.value; + const contractDefinition = + this.contractDefinitionBuilder.buildContractDefinition(formValue); + this.loading = true; + this.edcApiService + .createContractDefinition(contractDefinition) + .pipe( + takeUntil(this.ngOnDestroy$), + finalize(() => { + this.form.group.enable(); + this.loading = false; + }), + ) + .subscribe({ + complete: () => { + this.notificationService.showInfo('Successfully created data offer.'); + this.close({refreshList: true}); + }, + error: (error) => { + if (error.status == 409) { + this.notificationService.showError('Data offer ID already taken.'); + } else if (error.status >= 500) { + this.notificationService.showError( + 'Error creating data offer: ' + (error?.error?.message ?? '???'), + ); + } + console.error('Error creating data offer!', error); + }, + }); + } + + private close(params: ContractDefinitionEditorDialogResult) { + this.dialogRef.close(params); + } + ngOnDestroy$ = new Subject(); + + ngOnDestroy(): void { + this.ngOnDestroy$.next(null); + this.ngOnDestroy$.complete(); + } +} diff --git a/connector-ui/src/app/routes/connector-ui/contract-definition-page/contract-definition-page.module.ts b/connector-ui/src/app/routes/connector-ui/contract-definition-page/contract-definition-page.module.ts new file mode 100644 index 000000000..cd1361de8 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/contract-definition-page/contract-definition-page.module.ts @@ -0,0 +1,32 @@ +import {CommonModule} from '@angular/common'; +import {NgModule} from '@angular/core'; +import {FormsModule, ReactiveFormsModule} from '@angular/forms'; +import {RouterModule} from '@angular/router'; +import {SharedModule} from '../../../shared/shared.module'; +import {AssetSelectComponent} from './asset-select/asset-select.component'; +import {ContractDefinitionCardsComponent} from './contract-definition-cards/contract-definition-cards.component'; +import {ContractDefinitionEditorDialog} from './contract-definition-editor-dialog/contract-definition-editor-dialog.component'; +import {ContractDefinitionPageComponent} from './contract-definition-page/contract-definition-page.component'; +import {PolicySelectComponent} from './policy-select/policy-select.component'; + +@NgModule({ + imports: [ + // Angular + CommonModule, + FormsModule, + ReactiveFormsModule, + RouterModule, + + // EDC UI Modules + SharedModule, + ], + declarations: [ + AssetSelectComponent, + ContractDefinitionCardsComponent, + ContractDefinitionEditorDialog, + ContractDefinitionPageComponent, + PolicySelectComponent, + ], + exports: [ContractDefinitionPageComponent], +}) +export class ContractDefinitionPageModule {} diff --git a/connector-ui/src/app/routes/connector-ui/contract-definition-page/contract-definition-page/contract-definition-page.component.html b/connector-ui/src/app/routes/connector-ui/contract-definition-page/contract-definition-page/contract-definition-page.component.html new file mode 100644 index 000000000..5d0fd5453 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/contract-definition-page/contract-definition-page/contract-definition-page.component.html @@ -0,0 +1,76 @@ +
+
+ + + + {{ 'contract_agreement_page.search_def' | translate }} + + search + + + + + + + +
+ + + +
+ +
+ + + +
+ +
diff --git a/connector-ui/src/app/routes/connector-ui/contract-definition-page/contract-definition-page/contract-definition-page.component.scss b/connector-ui/src/app/routes/connector-ui/contract-definition-page/contract-definition-page/contract-definition-page.component.scss new file mode 100644 index 000000000..2fd1ec0ea --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/contract-definition-page/contract-definition-page/contract-definition-page.component.scss @@ -0,0 +1,21 @@ +#wrapper { + margin: 20px; +} + +.contract-definition-card { + width: calc(100% / 2 - 48px); +} + +#create-button { + margin-left: 10px; +} + +mat-paginator { + display: inline-block; + background-color: transparent; +} + +.search-form-field { + min-width: 200px; + width: 30%; +} diff --git a/connector-ui/src/app/routes/connector-ui/contract-definition-page/contract-definition-page/contract-definition-page.component.ts b/connector-ui/src/app/routes/connector-ui/contract-definition-page/contract-definition-page/contract-definition-page.component.ts new file mode 100644 index 000000000..b6dd0375b --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/contract-definition-page/contract-definition-page/contract-definition-page.component.ts @@ -0,0 +1,59 @@ +import {Component, OnInit} from '@angular/core'; +import {FormControl} from '@angular/forms'; +import {MatDialog} from '@angular/material/dialog'; +import {BehaviorSubject, Observable, distinctUntilChanged} from 'rxjs'; +import {filter, map} from 'rxjs/operators'; +import {Fetched} from '../../../../core/services/models/fetched'; +import {value$} from '../../../../core/utils/form-group-utils'; +import {ContractDefinitionEditorDialogResult} from '../contract-definition-editor-dialog/contract-definition-editor-dialog-result'; +import {ContractDefinitionEditorDialog} from '../contract-definition-editor-dialog/contract-definition-editor-dialog.component'; +import {ContractDefinitionPageData} from './contract-definition-page.data'; +import {ContractDefinitionPageService} from './contract-definition-page.service'; + +@Component({ + selector: 'app-contract-definition-page', + templateUrl: './contract-definition-page.component.html', + styleUrls: ['./contract-definition-page.component.scss'], +}) +export class ContractDefinitionPageComponent implements OnInit { + contractDefinitionList: Fetched = Fetched.empty(); + searchText = new FormControl(''); + deleteBusy = false; + + private fetch$ = new BehaviorSubject(null); + + constructor( + private contractDefinitionPageService: ContractDefinitionPageService, + private readonly dialog: MatDialog, + ) {} + + ngOnInit(): void { + this.contractDefinitionPageService + .contractDefinitionPageData$(this.fetch$, this.searchText$()) + .subscribe((contractDefinitionList) => { + this.contractDefinitionList = contractDefinitionList; + }); + } + + onCreate() { + const dialogRef = this.dialog.open(ContractDefinitionEditorDialog); + dialogRef + .afterClosed() + .pipe( + map((it) => it as ContractDefinitionEditorDialogResult | null), + filter((it) => !!it?.refreshList), + ) + .subscribe(() => this.refresh()); + } + + refresh() { + this.fetch$.next(null); + } + + private searchText$(): Observable { + return (value$(this.searchText) as Observable).pipe( + map((it) => (it ?? '').trim()), + distinctUntilChanged(), + ); + } +} diff --git a/connector-ui/src/app/routes/connector-ui/contract-definition-page/contract-definition-page/contract-definition-page.data.ts b/connector-ui/src/app/routes/connector-ui/contract-definition-page/contract-definition-page/contract-definition-page.data.ts new file mode 100644 index 000000000..6af45dd2a --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/contract-definition-page/contract-definition-page/contract-definition-page.data.ts @@ -0,0 +1,6 @@ +import {ContractDefinitionCard} from '../contract-definition-cards/contract-definition-card'; + +export interface ContractDefinitionPageData { + contractDefinitionCards: ContractDefinitionCard[]; + numTotalContractDefinitions: number; +} diff --git a/connector-ui/src/app/routes/connector-ui/contract-definition-page/contract-definition-page/contract-definition-page.service.ts b/connector-ui/src/app/routes/connector-ui/contract-definition-page/contract-definition-page/contract-definition-page.service.ts new file mode 100644 index 000000000..e867c2daf --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/contract-definition-page/contract-definition-page/contract-definition-page.service.ts @@ -0,0 +1,83 @@ +import {Injectable} from '@angular/core'; +import {Observable, combineLatest, of} from 'rxjs'; +import {catchError, map, switchMap} from 'rxjs/operators'; +import {EdcApiService} from '../../../../core/services/api/edc-api.service'; +import {AssetService} from '../../../../core/services/asset.service'; +import {Fetched} from '../../../../core/services/models/fetched'; +import {search} from '../../../../core/utils/search-utils'; +import {ContractDefinitionCard} from '../contract-definition-cards/contract-definition-card'; +import {ContractDefinitionCardBuilder} from '../contract-definition-cards/contract-definition-card-builder'; + +export interface ContractDefinitionList { + contractDefinitionCards: ContractDefinitionCard[]; + numTotalContractDefinitions: number; +} + +@Injectable({providedIn: 'root'}) +export class ContractDefinitionPageService { + constructor( + private edcApiService: EdcApiService, + private assetServiceMapped: AssetService, + private contractDefinitionCardBuilder: ContractDefinitionCardBuilder, + ) {} + + contractDefinitionPageData$( + refresh$: Observable, + searchText$: Observable, + ): Observable> { + return combineLatest([ + refresh$.pipe(switchMap(() => this.fetchCards())), + searchText$, + ]).pipe( + map(([fetchedCards, searchText]) => + fetchedCards.map((cards) => ({ + contractDefinitionCards: this.filterCards(cards, searchText), + numTotalContractDefinitions: cards.length, + })), + ), + ); + } + + filterCards( + cards: ContractDefinitionCard[], + searchText: string, + ): ContractDefinitionCard[] { + return search(cards, searchText, (card) => [ + card.id, + card.accessPolicy.policyDefinitionId, + card.contractPolicy.policyDefinitionId, + ...card.criteria.map((it) => it.label), + ...card.criteria + .flatMap((it) => it.values) + .flatMap((it) => it.searchTargets), + ]); + } + //ed + fetchCards(): Observable> { + return combineLatest([ + this.edcApiService.getContractDefinitionPage(), + this.assetServiceMapped.fetchAssets().pipe( + catchError((err) => { + console.warn('Failed fetching assets.', err); + return of([]); + }), + ), + this.edcApiService.getPolicyDefinitionPage().pipe( + map((policyDefinitionPage) => policyDefinitionPage.policies), + catchError((err) => { + console.warn('Failed fetching policy definitions.', err); + return of([]); + }), + ), + ]).pipe( + map(([contractDefinitions, assets, policyDefinitions]) => + this.contractDefinitionCardBuilder.buildContractDefinitionCards( + contractDefinitions, + assets, + policyDefinitions, + ), + ), + Fetched.wrap({failureMessage: 'Failed fetching contract definitions'}), + ); + } +} diff --git a/connector-ui/src/app/routes/connector-ui/contract-definition-page/policy-select/policy-select.component.html b/connector-ui/src/app/routes/connector-ui/contract-definition-page/policy-select/policy-select.component.html new file mode 100644 index 000000000..486af06fb --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/contract-definition-page/policy-select/policy-select.component.html @@ -0,0 +1,9 @@ + + {{ label }} + + + + {{ policyId(policy) }} + + + diff --git a/connector-ui/src/app/routes/connector-ui/contract-definition-page/policy-select/policy-select.component.ts b/connector-ui/src/app/routes/connector-ui/contract-definition-page/policy-select/policy-select.component.ts new file mode 100644 index 000000000..b0ac41d9f --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/contract-definition-page/policy-select/policy-select.component.ts @@ -0,0 +1,33 @@ +import {Component, HostBinding, Input} from '@angular/core'; +import {FormControl} from '@angular/forms'; +import {PolicyDefinitionDto} from '@sovity.de/edc-client'; + +@Component({ + selector: 'policy-select', + templateUrl: 'policy-select.component.html', +}) +export class PolicySelectComponent { + @Input() + label!: string; + + @Input() + control!: FormControl; + + @Input() + policies: PolicyDefinitionDto[] = []; + + @HostBinding('class.flex') + @HostBinding('class.flex-row') + cls = true; + + compareWith( + a: PolicyDefinitionDto | null, + b: PolicyDefinitionDto | null, + ): boolean { + return this.policyId(a) === this.policyId(b); + } + + policyId(a: PolicyDefinitionDto | null): string | null { + return a?.policyDefinitionId ?? null; + } +} diff --git a/connector-ui/src/app/routes/connector-ui/dashboard-page/dashboard-donut-chart/dashboard-donut-chart.component.html b/connector-ui/src/app/routes/connector-ui/dashboard-page/dashboard-donut-chart/dashboard-donut-chart.component.html new file mode 100644 index 000000000..e3193e14b --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/dashboard-page/dashboard-donut-chart/dashboard-donut-chart.component.html @@ -0,0 +1,26 @@ + + + + + +
+ {{ data.data.totalLabel }}: {{ data.data.totalValue | number }} +
+
+ + + diff --git a/connector-ui/src/app/routes/connector-ui/dashboard-page/dashboard-donut-chart/dashboard-donut-chart.component.ts b/connector-ui/src/app/routes/connector-ui/dashboard-page/dashboard-donut-chart/dashboard-donut-chart.component.ts new file mode 100644 index 000000000..9e2f527c6 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/dashboard-page/dashboard-donut-chart/dashboard-donut-chart.component.ts @@ -0,0 +1,20 @@ +import {Component, HostBinding, Input} from '@angular/core'; +import {Fetched} from '../../../../core/services/models/fetched'; +import {DonutChartData} from './donut-chart-data'; + +@Component({ + selector: 'dashboard-donut-chart', + templateUrl: './dashboard-donut-chart.component.html', +}) +export class DashboardDonutChartComponent { + @HostBinding('class.flex') + @HostBinding('class.flex-col') + @HostBinding('class.justify-center') + @HostBinding('class.items-center') + @HostBinding('class.items-center') + @HostBinding('class.min-h-[300px]') + cls = true; + + @Input() + data = Fetched.empty(); +} diff --git a/connector-ui/src/app/routes/connector-ui/dashboard-page/dashboard-donut-chart/donut-chart-data.ts b/connector-ui/src/app/routes/connector-ui/dashboard-page/dashboard-donut-chart/donut-chart-data.ts new file mode 100644 index 000000000..a74844744 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/dashboard-page/dashboard-donut-chart/donut-chart-data.ts @@ -0,0 +1,13 @@ +import {ChartConfiguration} from 'chart.js'; + +export interface DonutChartData { + labels: string[]; + datasets: ChartConfiguration<'doughnut'>['data']['datasets']; + options: ChartConfiguration<'doughnut'>['options']; + + isEmpty: boolean; + emptyMessage: string; + + totalLabel: string; + totalValue: number; +} diff --git a/connector-ui/src/app/routes/connector-ui/dashboard-page/dashboard-kpi-card/dashboard-kpi-card.component.html b/connector-ui/src/app/routes/connector-ui/dashboard-page/dashboard-kpi-card/dashboard-kpi-card.component.html new file mode 100644 index 000000000..74e97e7af --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/dashboard-page/dashboard-kpi-card/dashboard-kpi-card.component.html @@ -0,0 +1,32 @@ + +
+ {{ + kpi.error.failureIcon + }} + + +
+ {{ + kpi.data | number + }} +
+
+
+
+ {{ kpi.error.failureMessage }} +
+
+ {{ label }} +
+
+
diff --git a/connector-ui/src/app/routes/connector-ui/dashboard-page/dashboard-kpi-card/dashboard-kpi-card.component.ts b/connector-ui/src/app/routes/connector-ui/dashboard-page/dashboard-kpi-card/dashboard-kpi-card.component.ts new file mode 100644 index 000000000..50c9e965a --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/dashboard-page/dashboard-kpi-card/dashboard-kpi-card.component.ts @@ -0,0 +1,38 @@ +import {Component, HostBinding, Input} from '@angular/core'; +import {Fetched} from '../../../../core/services/models/fetched'; + +@Component({ + selector: 'dashboard-kpi-card', + styles: [ + ` + :host { + min-width: 200px; + height: 180px; + } + `, + ], + templateUrl: './dashboard-kpi-card.component.html', +}) +export class DashboardKpiCardComponent { + @HostBinding('class.flex') + cls = true; + + @Input() + kpi = Fetched.empty(); + + @Input() + label = 'Label'; + + fontSize(data: number): number { + const abs = Math.abs(data); + if (abs < 100) { + return 81; + } else if (abs < 1000) { + return 72; + } else if (abs < 10000) { + return 64; + } else { + return 56; + } + } +} diff --git a/connector-ui/src/app/routes/connector-ui/dashboard-page/dashboard-page.module.ts b/connector-ui/src/app/routes/connector-ui/dashboard-page/dashboard-page.module.ts new file mode 100644 index 000000000..baba53f70 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/dashboard-page/dashboard-page.module.ts @@ -0,0 +1,28 @@ +import {CommonModule} from '@angular/common'; +import {NgModule} from '@angular/core'; +import {FormsModule, ReactiveFormsModule} from '@angular/forms'; +import {RouterModule} from '@angular/router'; +import {SharedModule} from '../../../shared/shared.module'; +import {DashboardDonutChartComponent} from './dashboard-donut-chart/dashboard-donut-chart.component'; +import {DashboardKpiCardComponent} from './dashboard-kpi-card/dashboard-kpi-card.component'; +import {DashboardPageComponent} from './dashboard-page/dashboard-page.component'; + +@NgModule({ + imports: [ + // Angular + CommonModule, + FormsModule, + ReactiveFormsModule, + RouterModule, + + // EDC UI Modules + SharedModule, + ], + declarations: [ + DashboardDonutChartComponent, + DashboardKpiCardComponent, + DashboardPageComponent, + ], + exports: [DashboardPageComponent], +}) +export class DashboardPageModule {} diff --git a/connector-ui/src/app/routes/connector-ui/dashboard-page/dashboard-page/dashboard-page-data.service.ts b/connector-ui/src/app/routes/connector-ui/dashboard-page/dashboard-page/dashboard-page-data.service.ts new file mode 100644 index 000000000..c2afe5432 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/dashboard-page/dashboard-page/dashboard-page-data.service.ts @@ -0,0 +1,199 @@ +import {Injectable} from '@angular/core'; +import {Observable, combineLatest, merge, of, sampleTime, scan} from 'rxjs'; +import {catchError, map} from 'rxjs/operators'; +import {TranslateService} from '@ngx-translate/core'; +import {DashboardTransferAmounts, UiDataOffer} from '@sovity.de/edc-client'; +import {EdcApiService} from '../../../../core/services/api/edc-api.service'; +import {LastCommitInfoService} from '../../../../core/services/api/last-commit-info.service'; +import {ConnectorInfoPropertyGridGroupBuilder} from '../../../../core/services/connector-info-property-grid-group-builder'; +import {Fetched} from '../../../../core/services/models/fetched'; +import {CatalogApiUrlService} from '../../catalog-browser-page/catalog-browser-page/catalog-api-url.service'; +import {DonutChartData} from '../dashboard-donut-chart/donut-chart-data'; +import {DashboardPageData, defaultDashboardData} from './dashboard-page-data'; + +@Injectable({providedIn: 'root'}) +export class DashboardPageDataService { + constructor( + private edcApiService: EdcApiService, + private catalogApiUrlService: CatalogApiUrlService, + private lastCommitInfoService: LastCommitInfoService, + private connectorInfoPropertyGridGroupBuilder: ConnectorInfoPropertyGridGroupBuilder, + private translateService: TranslateService, + ) {} + + /** + * Fetch {@link DashboardPageData}. + */ + getDashboardData(): Observable { + const initial = defaultDashboardData(); + + // Dashboard is built from different API calls + const sources: Observable>[] = [ + this.catalogBrowserKpis(), + this.numCatalogs(), + this.dashboardData(), + ]; + + // We merge all results as they come in, constructing our DashboardData + // This allows single KPIs to have their own individual loading statuses + return merge(...sources).pipe( + scan((data, patch) => ({...data, ...patch}), initial), + ); + } + + private catalogBrowserKpis(): Observable> { + return this.getAllDataOffers().pipe( + map((dataOffers) => dataOffers.length), + Fetched.wrap({ + failureMessage: this.translateService.instant( + 'dashboard_page.failed_offers', + ), + }), + map((numOffers) => ({numCatalogEntries: numOffers})), + ); + } + + private getAllDataOffers(): Observable { + const catalogUrls = this.catalogApiUrlService.getAllProviders(); + + const dataOffers = catalogUrls.map((it) => + this.edcApiService + .getCatalogPageDataOffers(it) + .pipe(catchError(() => of([]))), + ); + + return merge(...dataOffers).pipe( + sampleTime(50), + map((results) => results.flat()), + ); + } + + private numCatalogs(): Observable> { + return of({ + numCatalogs: Fetched.ready( + this.catalogApiUrlService.getPresetProviders().length, + ), + }); + } + + private buildTransferChart( + transfers: DashboardTransferAmounts, + direction: 'CONSUMING' | 'PROVIDING', + ): DonutChartData { + const amounts: {label: string; amount: number; color: string}[] = [ + { + label: this.translateService.instant('dashboard_page.completed'), + amount: transfers.numOk, + color: '#b2e061', + }, + { + label: this.translateService.instant('dashboard_page.progress'), + amount: transfers.numRunning, + color: '#7eb0d5', + }, + { + label: this.translateService.instant('dashboard_page.error'), + amount: transfers.numError, + color: '#fd7f6f', + }, + ].filter((it) => it.amount); + + const total = transfers.numTotal; + const emptyMessage = + direction === 'CONSUMING' + ? this.translateService.instant('dashboard_page.no_transfer') + : this.translateService.instant('dashboard_page.no_transfer2'); + return { + totalLabel: this.translateService.instant('general.total'), + totalValue: total, + isEmpty: !total, + emptyMessage, + labels: amounts.map((it) => it.label), + datasets: [ + { + label: this.translateService.instant('dashboard_page.num_transfer'), + data: amounts.map((it) => it.amount), + backgroundColor: amounts.map((it) => it.color), + }, + ], + options: {responsive: false}, + }; + } + + private dashboardData(): Observable> { + return combineLatest([ + this.lastCommitInfoService.getLastCommitInfoData().pipe( + Fetched.wrap({ + failureMessage: this.translateService.instant( + 'dashboard_page.failed_env', + ), + }), + ), + this.lastCommitInfoService.getUiBuildDateDetails().pipe( + Fetched.wrap({ + failureMessage: this.translateService.instant( + 'dashboard_page.failed_ui_build', + ), + }), + ), + this.lastCommitInfoService.getUiCommitDetails().pipe( + Fetched.wrap({ + failureMessage: this.translateService.instant( + 'dashboard_page.failed_ui', + ), + }), + ), + this.edcApiService.getDashboardPage().pipe( + Fetched.wrap({ + failureMessage: this.translateService.instant( + 'dashboard_page.failed_dashboard', + ), + }), + ), + ]).pipe( + map(([lastCommitInfo, uiBuildDate, uiCommitDetails, fetched]) => ({ + title: this.extractString(fetched, (it) => it.connectorTitle), + description: this.extractString( + fetched, + (it) => it.connectorDescription, + ), + numAssets: fetched.map((it) => it.numAssets), + numPolicies: fetched.map((it) => it.numPolicies), + numContractDefinitions: fetched.map((it) => it.numContractDefinitions), + numContractAgreements: fetched.map( + (it) => + it.numContractAgreementsConsuming + + it.numContractAgreementsProviding, + ), + connectorEndpoint: this.extractString( + fetched, + (it) => it.connectorEndpoint, + ), + incomingTransfersChart: fetched.map((it) => + this.buildTransferChart(it.transferProcessesConsuming, 'CONSUMING'), + ), + outgoingTransfersChart: fetched.map((it) => + this.buildTransferChart(it.transferProcessesProviding, 'PROVIDING'), + ), + connectorProperties: + this.connectorInfoPropertyGridGroupBuilder.buildPropertyGridGroups( + lastCommitInfo, + uiBuildDate, + uiCommitDetails, + fetched, + ), + })), + ); + } + + private extractString( + fetched: Fetched, + extractor: (item: T) => string, + ): string { + return fetched.match({ + ifLoading: () => 'Loading...', + ifError: () => 'Failed loading.', + ifOk: extractor, + }); + } +} diff --git a/connector-ui/src/app/routes/connector-ui/dashboard-page/dashboard-page/dashboard-page-data.ts b/connector-ui/src/app/routes/connector-ui/dashboard-page/dashboard-page/dashboard-page-data.ts new file mode 100644 index 000000000..41b9d8b7d --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/dashboard-page/dashboard-page/dashboard-page-data.ts @@ -0,0 +1,35 @@ +import {Fetched} from '../../../../core/services/models/fetched'; +import {PropertyGridGroup} from '../../../../shared/common/property-grid-group/property-grid-group'; +import {DonutChartData} from '../dashboard-donut-chart/donut-chart-data'; + +export interface DashboardPageData { + incomingTransfersChart: Fetched; + outgoingTransfersChart: Fetched; + numContractAgreements: Fetched; + numAssets: Fetched; + numCatalogEntries: Fetched; + numContractDefinitions: Fetched; + numPolicies: Fetched; + numCatalogs: Fetched; + connectorProperties: PropertyGridGroup[]; + connectorEndpoint: string; + title: string; + description: string; +} + +export function defaultDashboardData(): DashboardPageData { + return { + incomingTransfersChart: Fetched.empty(), + outgoingTransfersChart: Fetched.empty(), + numContractAgreements: Fetched.empty(), + numAssets: Fetched.empty(), + numCatalogEntries: Fetched.empty(), + numPolicies: Fetched.empty(), + numContractDefinitions: Fetched.empty(), + numCatalogs: Fetched.empty(), + connectorProperties: [], + connectorEndpoint: 'Loading...', + title: 'Loading...', + description: 'Loading...', + }; +} diff --git a/connector-ui/src/app/routes/connector-ui/dashboard-page/dashboard-page/dashboard-page.component.html b/connector-ui/src/app/routes/connector-ui/dashboard-page/dashboard-page/dashboard-page.component.html new file mode 100644 index 000000000..1919707c9 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/dashboard-page/dashboard-page/dashboard-page.component.html @@ -0,0 +1,330 @@ +
+
+
+ + {{ + 'dashboard_page.inc_data' | translate + }} + {{ + 'dashboard_page.trans_pro' | translate + }} +
+ +
+
+ + + {{ + 'dashboard_page.out_data' | translate + }} + {{ + 'dashboard_page.trans_pro' | translate + }} +
+ +
+
+
+ +
+ + + + + + + + + +
+ + + {{ + 'dashboard_page.conn_prop' | translate + }} + {{ + 'dashboard_page.add_prop' | translate + }} + + + +
+ +
+ + {{ + 'dashboard_page.edc_conn' | translate + }} + + {{ data.title }} + +

+ {{ data.description }} +

+

+ {{ 'dashboard_page.descrip' | translate }} +

+ + {{ 'dashboard_page.conn_end' | translate }} + + link + + + + {{ 'dashboard_page.api_url' | translate }} + + link + + +
+ + + {{ 'dashboard_page.provided' | translate }} + sovity logo + {{ + 'dashboard_page.edition_edc' | translate + }} + +
+
+

+ {{ 'dashboard_page.marketing_mds_basic_intro' | translate }} +

+

+ {{ 'dashboard_page.marketing_mds_basic_intro2' | translate }} +

+
+
+ +
+ + + {{ + 'dashboard_page.managed_edc' | translate + }} + {{ + 'dashboard_page.conn_service' | translate + }} + + + + {{ 'dashboard_page.about' | translate }} + {{ + 'dashboard_page.eclipse' | translate + }} +
+

+ {{ 'dashboard_page.marketing_about' | translate }} +

+

+ {{ 'dashboard_page.marketing_about2' | translate }} +

+

+ {{ 'dashboard_page.marketing_about3' | translate }} +

+ +
+
+ + {{ + 'dashboard_page.about_ui' | translate + }} + {{ + 'dashboard_page.data_dashboard' | translate + }} + + +
+
diff --git a/connector-ui/src/app/routes/connector-ui/dashboard-page/dashboard-page/dashboard-page.component.ts b/connector-ui/src/app/routes/connector-ui/dashboard-page/dashboard-page/dashboard-page.component.ts new file mode 100644 index 000000000..368496c73 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/dashboard-page/dashboard-page/dashboard-page.component.ts @@ -0,0 +1,40 @@ +import {Component, Inject, OnDestroy, OnInit} from '@angular/core'; +import {BehaviorSubject, Subject} from 'rxjs'; +import {switchMap, takeUntil} from 'rxjs/operators'; +import {APP_CONFIG, AppConfig} from 'src/app/core/config/app-config'; +import {ConnectorInfoPropertyGridGroupBuilder} from '../../../../core/services/connector-info-property-grid-group-builder'; +import {DashboardPageData, defaultDashboardData} from './dashboard-page-data'; +import {DashboardPageDataService} from './dashboard-page-data.service'; + +@Component({ + selector: 'dashboard-page', + templateUrl: './dashboard-page.component.html', + providers: [ConnectorInfoPropertyGridGroupBuilder], +}) +export class DashboardPageComponent implements OnInit, OnDestroy { + data: DashboardPageData = defaultDashboardData(); + private refresh$ = new BehaviorSubject(true); + + constructor( + @Inject(APP_CONFIG) public config: AppConfig, + private dashboardDataService: DashboardPageDataService, + ) {} + + ngOnInit() { + this.refresh$ + .pipe( + switchMap(() => this.dashboardDataService.getDashboardData()), + takeUntil(this.ngOnDestroy$), + ) + .subscribe((data) => { + this.data = data; + }); + } + + ngOnDestroy$ = new Subject(); + + ngOnDestroy() { + this.ngOnDestroy$.next(null); + this.ngOnDestroy$.complete(); + } +} diff --git a/connector-ui/src/app/routes/connector-ui/logout-page/location-history-utils.ts b/connector-ui/src/app/routes/connector-ui/logout-page/location-history-utils.ts new file mode 100644 index 000000000..c80709499 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/logout-page/location-history-utils.ts @@ -0,0 +1,39 @@ +import {Location} from '@angular/common'; +import {Injectable} from '@angular/core'; +import {PreviousRouteListener} from './previous-route-listener'; + +/** + * Required because: + * - The logout action is currently a "page"/route instead of a navigation entry with a click/enter handler. + * - This page should not be in location history or the back button won't work. + * - For that we need to replace the logout page's state with the correct URL to return to. + * - For that we need that URL in the first place. + */ +@Injectable() +export class LocationHistoryUtils { + constructor( + private location: Location, + private previousRouteListener: PreviousRouteListener, + ) {} + + replaceStateWithPreviousUrl(opts: {skipUrlsStartingWith: string}) { + const goodReplacementUrl = this.getGoodReplacementUrl(opts); + this.location.replaceState(goodReplacementUrl); + } + + getGoodReplacementUrl(opts: {skipUrlsStartingWith: string}): string { + const urlsToTry: (string | null)[] = [ + this.previousRouteListener.currentUrl, + this.previousRouteListener.previousUrl, + + // Fallback to dashboard + '/', + ]; + + const url = urlsToTry.find( + (url) => url && !url.startsWith(opts.skipUrlsStartingWith), + ); + + return url ?? urlsToTry[urlsToTry.length - 1]!!; + } +} diff --git a/connector-ui/src/app/routes/connector-ui/logout-page/logout-page.component.ts b/connector-ui/src/app/routes/connector-ui/logout-page/logout-page.component.ts new file mode 100644 index 000000000..cc5a1bb5c --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/logout-page/logout-page.component.ts @@ -0,0 +1,24 @@ +import {DOCUMENT} from '@angular/common'; +import {Component, Inject, OnInit} from '@angular/core'; +import {APP_CONFIG, AppConfig} from '../../../core/config/app-config'; +import {LocationHistoryUtils} from './location-history-utils'; + +@Component({ + selector: 'logout', + template: ``, +}) +export class LogoutPageComponent implements OnInit { + constructor( + @Inject(APP_CONFIG) private config: AppConfig, + @Inject(DOCUMENT) private document: Document, + private locationHistoryUtils: LocationHistoryUtils, + ) {} + + ngOnInit(): void { + // Prevent back button hijacking from /logout in history + this.locationHistoryUtils.replaceStateWithPreviousUrl({ + skipUrlsStartingWith: '/logout', + }); + this.document.location.href = this.config.logoutUrl!; + } +} diff --git a/connector-ui/src/app/routes/connector-ui/logout-page/logout-page.module.ts b/connector-ui/src/app/routes/connector-ui/logout-page/logout-page.module.ts new file mode 100644 index 000000000..d32f270d9 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/logout-page/logout-page.module.ts @@ -0,0 +1,15 @@ +import {CommonModule} from '@angular/common'; +import {NgModule} from '@angular/core'; +import {RouterModule} from '@angular/router'; +import {LogoutPageComponent} from './logout-page.component'; + +@NgModule({ + imports: [ + // Angular + CommonModule, + RouterModule, + ], + declarations: [LogoutPageComponent], + exports: [LogoutPageComponent], +}) +export class LogoutPageModule {} diff --git a/connector-ui/src/app/routes/connector-ui/logout-page/previous-route-listener.ts b/connector-ui/src/app/routes/connector-ui/logout-page/previous-route-listener.ts new file mode 100644 index 000000000..974a1495f --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/logout-page/previous-route-listener.ts @@ -0,0 +1,28 @@ +import {Injectable} from '@angular/core'; +import {NavigationEnd, Router} from '@angular/router'; + +/** + * Required because: + * - The logout action is currently a "page"/route instead of a navigation entry with a click/enter handler. + * - This page should not be in location history or the back button won't work. + * - For that we need to replace the logout page's state with the correct URL to return to. + * - For that we need that URL in the first place. + */ +@Injectable() +export class PreviousRouteListener { + previousUrl: string | null = null; + currentUrl: string | null = null; + + constructor(private router: Router) { + this.startListeningToUrlChanges(); + } + + private startListeningToUrlChanges() { + this.router.events.subscribe((event) => { + if (event instanceof NavigationEnd) { + this.previousUrl = this.currentUrl; + this.currentUrl = event.url; + } + }); + } +} diff --git a/connector-ui/src/app/routes/connector-ui/page-not-found-page/page-not-found-page.component.html b/connector-ui/src/app/routes/connector-ui/page-not-found-page/page-not-found-page.component.html new file mode 100644 index 000000000..21ab90fec --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/page-not-found-page/page-not-found-page.component.html @@ -0,0 +1,9 @@ +
+

+ Page not found +

+

The page you are looking for doesn't exist.

+ +
diff --git a/connector-ui/src/app/routes/connector-ui/page-not-found-page/page-not-found-page.component.ts b/connector-ui/src/app/routes/connector-ui/page-not-found-page/page-not-found-page.component.ts new file mode 100644 index 000000000..db210a297 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/page-not-found-page/page-not-found-page.component.ts @@ -0,0 +1,16 @@ +import {Component, HostBinding} from '@angular/core'; + +@Component({ + selector: 'app-page-not-found-page', + templateUrl: './page-not-found-page.component.html', +}) +export class PageNotFoundPageComponent { + @HostBinding('class.container') + @HostBinding('class.flex') + @HostBinding('class.items-center') + @HostBinding('class.min-h-screen') + @HostBinding('class.px-6') + @HostBinding('class.py-12') + @HostBinding('class.box-border') + cls = true; +} diff --git a/connector-ui/src/app/routes/connector-ui/page-not-found-page/page-not-found-page.module.ts b/connector-ui/src/app/routes/connector-ui/page-not-found-page/page-not-found-page.module.ts new file mode 100644 index 000000000..3a17739ca --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/page-not-found-page/page-not-found-page.module.ts @@ -0,0 +1,15 @@ +import {CommonModule} from '@angular/common'; +import {NgModule} from '@angular/core'; +import {RouterModule} from '@angular/router'; +import {PageNotFoundPageComponent} from './page-not-found-page.component'; + +@NgModule({ + imports: [ + // Angular + CommonModule, + RouterModule, + ], + declarations: [PageNotFoundPageComponent], + exports: [PageNotFoundPageComponent], +}) +export class PageNotFoundPageModule {} diff --git a/connector-ui/src/app/routes/connector-ui/policy-definition-create-page/policy-definition-create-page.module.ts b/connector-ui/src/app/routes/connector-ui/policy-definition-create-page/policy-definition-create-page.module.ts new file mode 100644 index 000000000..84d6c92bd --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/policy-definition-create-page/policy-definition-create-page.module.ts @@ -0,0 +1,24 @@ +import {CommonModule} from '@angular/common'; +import {NgModule} from '@angular/core'; +import {FormsModule, ReactiveFormsModule} from '@angular/forms'; +import {RouterModule} from '@angular/router'; +import {TranslateModule} from '@ngx-translate/core'; +import {SharedModule} from '../../../shared/shared.module'; +import {PolicyDefinitionCreatePageComponent} from './policy-definition-create-page/policy-definition-create-page.component'; + +@NgModule({ + imports: [ + // Angular + CommonModule, + FormsModule, + ReactiveFormsModule, + RouterModule, + TranslateModule, + + // EDC UI Modules + SharedModule, + ], + declarations: [PolicyDefinitionCreatePageComponent], + exports: [PolicyDefinitionCreatePageComponent], +}) +export class PolicyDefinitionCreatePageModule {} diff --git a/connector-ui/src/app/routes/connector-ui/policy-definition-create-page/policy-definition-create-page/policy-definition-create-page-form-model.ts b/connector-ui/src/app/routes/connector-ui/policy-definition-create-page/policy-definition-create-page/policy-definition-create-page-form-model.ts new file mode 100644 index 000000000..3d1eae0db --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/policy-definition-create-page/policy-definition-create-page/policy-definition-create-page-form-model.ts @@ -0,0 +1,9 @@ +import {FormControl, UntypedFormGroup, ɵFormGroupValue} from '@angular/forms'; + +export type PolicyDefinitionCreatePageFormValue = + ɵFormGroupValue; + +export interface PolicyDefinitionCreatePageFormModel { + id: FormControl; + treeControls: UntypedFormGroup; +} diff --git a/connector-ui/src/app/routes/connector-ui/policy-definition-create-page/policy-definition-create-page/policy-definition-create-page-form.ts b/connector-ui/src/app/routes/connector-ui/policy-definition-create-page/policy-definition-create-page/policy-definition-create-page-form.ts new file mode 100644 index 000000000..e068e10eb --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/policy-definition-create-page/policy-definition-create-page/policy-definition-create-page-form.ts @@ -0,0 +1,41 @@ +import {Injectable} from '@angular/core'; +import {FormBuilder, FormGroup, Validators} from '@angular/forms'; +import {DataOfferFormValidators} from 'src/app/core/validators/data-offer-form-validators'; +import {noWhitespacesOrColonsValidator} from '../../../../core/validators/no-whitespaces-or-colons-validator'; +import {ExpressionFormControls} from '../../../../shared/business/policy-editor/editor/expression-form-controls'; +import { + PolicyDefinitionCreatePageFormModel, + PolicyDefinitionCreatePageFormValue, +} from './policy-definition-create-page-form-model'; + +/** + * Handles AngularForms for NewPolicyDialog + */ +@Injectable() +export class PolicyDefinitionCreatePageForm { + group = this.buildFormGroup(); + + /** + * Quick access to full value + */ + get value(): PolicyDefinitionCreatePageFormValue { + return this.group.value; + } + + constructor( + private formBuilder: FormBuilder, + private expressionFormControls: ExpressionFormControls, + private validators: DataOfferFormValidators, + ) {} + + buildFormGroup(): FormGroup { + return this.formBuilder.nonNullable.group({ + id: [ + '', + [Validators.required, noWhitespacesOrColonsValidator], + [this.validators.policyIdExistsValidator], + ], + treeControls: this.expressionFormControls.formGroup, + }); + } +} diff --git a/connector-ui/src/app/routes/connector-ui/policy-definition-create-page/policy-definition-create-page/policy-definition-create-page.component.html b/connector-ui/src/app/routes/connector-ui/policy-definition-create-page/policy-definition-create-page/policy-definition-create-page.component.html new file mode 100644 index 000000000..be0829ea6 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/policy-definition-create-page/policy-definition-create-page/policy-definition-create-page.component.html @@ -0,0 +1,40 @@ +
+
+ {{ 'policy_definition_page.metadata' | translate }} +
+ + + ID + + {{ + validationMessages.invalidWhitespacesOrColonsMessage + }} + {{ + validationMessages.idExistsErrorMessage + }} + + +
+ {{ 'policy_definition_page.expression' | translate }} +
+ + + +
+ + + +
+
diff --git a/connector-ui/src/app/routes/connector-ui/policy-definition-create-page/policy-definition-create-page/policy-definition-create-page.component.ts b/connector-ui/src/app/routes/connector-ui/policy-definition-create-page/policy-definition-create-page/policy-definition-create-page.component.ts new file mode 100644 index 000000000..e0d9d0c2a --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/policy-definition-create-page/policy-definition-create-page/policy-definition-create-page.component.ts @@ -0,0 +1,78 @@ +import {Component, OnDestroy} from '@angular/core'; +import {Router} from '@angular/router'; +import {Subject} from 'rxjs'; +import {finalize, takeUntil} from 'rxjs/operators'; +import {TranslateService} from '@ngx-translate/core'; +import {PolicyDefinitionCreateDto} from '@sovity.de/edc-client'; +import {EdcApiService} from '../../../../core/services/api/edc-api.service'; +import {NotificationService} from '../../../../core/services/notification.service'; +import {ValidationMessages} from '../../../../core/validators/validation-messages'; +import {ExpressionFormHandler} from '../../../../shared/business/policy-editor/editor/expression-form-handler'; +import {policyFormRequiredViewProviders} from '../../../../shared/business/policy-editor/editor/policy-form-required-providers'; +import {PolicyDefinitionCreatePageForm} from './policy-definition-create-page-form'; + +@Component({ + selector: 'policy-definition-create-page', + templateUrl: './policy-definition-create-page.component.html', + viewProviders: [ + ...policyFormRequiredViewProviders, + PolicyDefinitionCreatePageForm, + ], +}) +export class PolicyDefinitionCreatePageComponent implements OnDestroy { + loading = false; + + constructor( + private router: Router, + public form: PolicyDefinitionCreatePageForm, + public expressionFormHandler: ExpressionFormHandler, + public validationMessages: ValidationMessages, + private edcApiService: EdcApiService, + private notificationService: NotificationService, + private translateService: TranslateService, + ) {} + + onSave() { + const createDto = this.buildPolicyDefinitionCreateDto(); + this.form.group.disable(); + this.loading = true; + this.edcApiService + .createPolicyDefinitionV2(createDto) + .pipe( + takeUntil(this.ngOnDestroy$), + finalize(() => { + this.form.group.enable(); + this.loading = false; + }), + ) + .subscribe({ + complete: () => { + this.notificationService.showInfo( + this.translateService.instant('notification.succ_pol'), + ); + this.router.navigate(['/policies']); + }, + error: (error) => { + const message = this.translateService.instant( + 'notification.failed_create_policy', + ); + console.error(message, error); + this.notificationService.showError(message); + }, + }); + } + + private buildPolicyDefinitionCreateDto(): PolicyDefinitionCreateDto { + return { + policyDefinitionId: this.form.group.controls.id.value, + expression: this.expressionFormHandler.toUiPolicyExpression(), + }; + } + + ngOnDestroy$ = new Subject(); + + ngOnDestroy(): void { + this.ngOnDestroy$.next(null); + this.ngOnDestroy$.complete(); + } +} diff --git a/connector-ui/src/app/routes/connector-ui/policy-definition-page/policy-cards/policy-card-builder.ts b/connector-ui/src/app/routes/connector-ui/policy-definition-page/policy-cards/policy-card-builder.ts new file mode 100644 index 000000000..11f49b8f5 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/policy-definition-page/policy-cards/policy-card-builder.ts @@ -0,0 +1,29 @@ +import {Injectable} from '@angular/core'; +import {PolicyDefinitionDto, PolicyDefinitionPage} from '@sovity.de/edc-client'; +import {PolicyMapper} from '../../../../shared/business/policy-editor/model/policy-mapper'; +import {PolicyCard} from './policy-card'; + +@Injectable({providedIn: 'root'}) +export class PolicyCardBuilder { + constructor(private policyMapper: PolicyMapper) {} + buildPolicyCards(policyDefinitionPage: PolicyDefinitionPage): PolicyCard[] { + return policyDefinitionPage.policies.map((policyDefinition) => + this.buildPolicyCard(policyDefinition), + ); + } + + buildPolicyCard(policyDefinition: PolicyDefinitionDto): PolicyCard { + const irregularities = policyDefinition.policy?.errors ?? []; + const expression = this.policyMapper.buildPolicy( + policyDefinition.policy.expression!, + ); + return { + id: policyDefinition.policyDefinitionId, + isRegular: !irregularities.length, + irregularities, + expression, + searchText: JSON.stringify(expression), + objectForJson: JSON.parse(policyDefinition.policy.policyJsonLd), + }; + } +} diff --git a/connector-ui/src/app/routes/connector-ui/policy-definition-page/policy-cards/policy-card.ts b/connector-ui/src/app/routes/connector-ui/policy-definition-page/policy-cards/policy-card.ts new file mode 100644 index 000000000..1953da00a --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/policy-definition-page/policy-cards/policy-card.ts @@ -0,0 +1,18 @@ +import {PolicyExpressionMapped} from '../../../../shared/business/policy-editor/model/policy-expression-mapped'; + +export interface PolicyCard { + id: string; + isRegular: boolean; + irregularities: string[]; + expression: PolicyExpressionMapped; + + searchText: string; + + objectForJson: any; +} + +export interface PolicyCardConstraint { + left: string; + operator: string; + right: string; +} diff --git a/connector-ui/src/app/routes/connector-ui/policy-definition-page/policy-cards/policy-cards.component.html b/connector-ui/src/app/routes/connector-ui/policy-definition-page/policy-cards/policy-cards.component.html new file mode 100644 index 000000000..d63600a11 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/policy-definition-page/policy-cards/policy-cards.component.html @@ -0,0 +1,23 @@ +
+ + + policy + + + {{ policyCard.id }} + + + {{ 'general.policy' | translate }} + + + + + +
diff --git a/connector-ui/src/app/routes/connector-ui/policy-definition-page/policy-cards/policy-cards.component.ts b/connector-ui/src/app/routes/connector-ui/policy-definition-page/policy-cards/policy-cards.component.ts new file mode 100644 index 000000000..e2ea37634 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/policy-definition-page/policy-cards/policy-cards.component.ts @@ -0,0 +1,79 @@ +import { + Component, + EventEmitter, + HostBinding, + Input, + Output, +} from '@angular/core'; +import {MatDialog, MatDialogRef} from '@angular/material/dialog'; +import {EMPTY} from 'rxjs'; +import {catchError, tap} from 'rxjs/operators'; +import {TranslateService} from '@ngx-translate/core'; +import {EdcApiService} from '../../../../core/services/api/edc-api.service'; +import {NotificationService} from '../../../../core/services/notification.service'; +import {ConfirmDialogModel} from '../../../../shared/common/confirmation-dialog/confirmation-dialog.component'; +import {JsonDialogComponent} from '../../../../shared/common/json-dialog/json-dialog.component'; +import {JsonDialogData} from '../../../../shared/common/json-dialog/json-dialog.data'; +import {PolicyCard} from './policy-card'; + +@Component({ + selector: 'policy-cards', + templateUrl: './policy-cards.component.html', +}) +export class PolicyCardsComponent { + @HostBinding('class.flex') + @HostBinding('class.flex-wrap') + @HostBinding('class.gap-[10px]') + cls = true; + + @Input() + policyCards: PolicyCard[] = []; + + @Input() + deleteBusy = false; + + @Output() + deleteDone = new EventEmitter(); + + constructor( + private edcApiService: EdcApiService, + private matDialog: MatDialog, + private notificationService: NotificationService, + private translateService: TranslateService, + ) {} + + onPolicyDetailClick(policyCard: PolicyCard) { + let dialogRef: MatDialogRef; + const data: JsonDialogData = { + title: policyCard.id, + subtitle: this.translateService.instant('general.policy'), + icon: 'policy', + objectForJson: policyCard.objectForJson, + toolbarButton: { + text: this.translateService.instant('general.delete'), + icon: 'delete', + confirmation: ConfirmDialogModel.forDelete( + 'general.policy', + policyCard.id, + this.translateService, + ), + action: () => + this.edcApiService.deletePolicyDefinition(policyCard.id).pipe( + tap(() => { + this.notificationService.showInfo('Policy deleted!'); + this.deleteDone.emit(); + dialogRef?.close(); + }), + catchError((err) => { + const msg = `Failed deleting policy with id ${policyCard.id}`; + console.error(msg, err); + this.notificationService.showError(msg); + return EMPTY; + }), + ), + }, + }; + + dialogRef = this.matDialog.open(JsonDialogComponent, {data}); + } +} diff --git a/connector-ui/src/app/routes/connector-ui/policy-definition-page/policy-definition-page.module.ts b/connector-ui/src/app/routes/connector-ui/policy-definition-page/policy-definition-page.module.ts new file mode 100644 index 000000000..003dbeca1 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/policy-definition-page/policy-definition-page.module.ts @@ -0,0 +1,23 @@ +import {CommonModule} from '@angular/common'; +import {NgModule} from '@angular/core'; +import {FormsModule, ReactiveFormsModule} from '@angular/forms'; +import {RouterModule} from '@angular/router'; +import {SharedModule} from '../../../shared/shared.module'; +import {PolicyCardsComponent} from './policy-cards/policy-cards.component'; +import {PolicyDefinitionPageComponent} from './policy-definition-page/policy-definition-page.component'; + +@NgModule({ + imports: [ + // Angular + CommonModule, + FormsModule, + ReactiveFormsModule, + RouterModule, + + // EDC UI Modules + SharedModule, + ], + declarations: [PolicyCardsComponent, PolicyDefinitionPageComponent], + exports: [PolicyDefinitionPageComponent], +}) +export class PolicyDefinitionPageModule {} diff --git a/connector-ui/src/app/routes/connector-ui/policy-definition-page/policy-definition-page/policy-definition-page.component.html b/connector-ui/src/app/routes/connector-ui/policy-definition-page/policy-definition-page/policy-definition-page.component.html new file mode 100644 index 000000000..127a047de --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/policy-definition-page/policy-definition-page/policy-definition-page.component.html @@ -0,0 +1,69 @@ +
+
+ + + {{ + 'policy_definition_page.search_pol' | translate + }} + + search + + + + + + + +
+ + + +
+ +
+ + + +
+ +
diff --git a/connector-ui/src/app/routes/connector-ui/policy-definition-page/policy-definition-page/policy-definition-page.component.scss b/connector-ui/src/app/routes/connector-ui/policy-definition-page/policy-definition-page/policy-definition-page.component.scss new file mode 100644 index 000000000..68efc9c9e --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/policy-definition-page/policy-definition-page/policy-definition-page.component.scss @@ -0,0 +1,21 @@ +#wrapper { + margin: 20px; +} + +.policy-card { + width: calc(100% / 2 - 48px); +} + +#create-button { + margin-left: 10px; +} + +mat-paginator { + display: inline-block; + background-color: transparent; +} + +.search-form-field { + min-width: 200px; + width: 30%; +} diff --git a/connector-ui/src/app/routes/connector-ui/policy-definition-page/policy-definition-page/policy-definition-page.component.ts b/connector-ui/src/app/routes/connector-ui/policy-definition-page/policy-definition-page/policy-definition-page.component.ts new file mode 100644 index 000000000..0c6b2df03 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/policy-definition-page/policy-definition-page/policy-definition-page.component.ts @@ -0,0 +1,78 @@ +import {Component, OnInit} from '@angular/core'; +import {BehaviorSubject} from 'rxjs'; +import {map, switchMap} from 'rxjs/operators'; +import {EdcApiService} from '../../../../core/services/api/edc-api.service'; +import {Fetched} from '../../../../core/services/models/fetched'; +import {search} from '../../../../core/utils/search-utils'; +import {PolicyCard} from '../policy-cards/policy-card'; +import {PolicyCardBuilder} from '../policy-cards/policy-card-builder'; + +export interface PolicyList { + policyCards: PolicyCard[]; + numTotalPolicies: number; +} + +@Component({ + selector: 'policy-definition-page', + templateUrl: './policy-definition-page.component.html', + styleUrls: ['./policy-definition-page.component.scss'], +}) +export class PolicyDefinitionPageComponent implements OnInit { + policyList: Fetched = Fetched.empty(); + searchText: string = ''; + deleteBusy = false; + private fetch$ = new BehaviorSubject(null); + + constructor( + private edcApiService: EdcApiService, + private policyCardBuilder: PolicyCardBuilder, + ) {} + + ngOnInit(): void { + this.fetch$ + .pipe( + switchMap(() => { + return this.edcApiService.getPolicyDefinitionPage().pipe( + map( + (policyDefinitionPage): PolicyList => ({ + policyCards: + this.policyCardBuilder.buildPolicyCards(policyDefinitionPage), + + numTotalPolicies: policyDefinitionPage.policies.length, + }), + ), + map((policyList) => this.filterPolicies(policyList)), + Fetched.wrap({ + failureMessage: 'Failed fetching policies.', + }), + ); + }), + ) + .subscribe((policyList) => (this.policyList = policyList)); + } + + onSearch() { + this.refresh(); + } + + refresh() { + this.fetch$.next(null); + } + + private filterPolicies(policyList: PolicyList): PolicyList { + const policyCards = search( + policyList.policyCards, + this.searchText, + (policyCard: PolicyCard) => [ + policyCard.id, + ...policyCard.irregularities, + policyCard.searchText, + ], + ); + + return { + policyCards, + numTotalPolicies: policyCards.length, + }; + } +} diff --git a/connector-ui/src/app/routes/connector-ui/transfer-history-page/transfer-history-page.module.ts b/connector-ui/src/app/routes/connector-ui/transfer-history-page/transfer-history-page.module.ts new file mode 100644 index 000000000..fe9ca4574 --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/transfer-history-page/transfer-history-page.module.ts @@ -0,0 +1,24 @@ +import {CommonModule} from '@angular/common'; +import {NgModule} from '@angular/core'; +import {FormsModule, ReactiveFormsModule} from '@angular/forms'; +import {RouterModule} from '@angular/router'; +import {TranslateModule} from '@ngx-translate/core'; +import {SharedModule} from '../../../shared/shared.module'; +import {TransferHistoryPageComponent} from './transfer-history-page/transfer-history-page.component'; + +@NgModule({ + imports: [ + // Angular + CommonModule, + FormsModule, + ReactiveFormsModule, + RouterModule, + TranslateModule, + + // EDC UI Modules + SharedModule, + ], + declarations: [TransferHistoryPageComponent], + exports: [TransferHistoryPageComponent], +}) +export class TransferHistoryPageModule {} diff --git a/connector-ui/src/app/routes/connector-ui/transfer-history-page/transfer-history-page/transfer-history-page.component.html b/connector-ui/src/app/routes/connector-ui/transfer-history-page/transfer-history-page/transfer-history-page.component.html new file mode 100644 index 000000000..fb547048f --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/transfer-history-page/transfer-history-page/transfer-history-page.component.html @@ -0,0 +1,145 @@ +
+
+
+ +
+
+ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ {{ 'general.direction' | translate }} + + + {{ item.direction === 'PROVIDING' ? 'upload' : 'download' }} + + + {{ 'general.updated' | translate }} + + + + {{ 'general.asset' | translate }} + + +
+ {{ item.assetName }} +
+
+ {{ 'general.state' | translate }} + +
+ + {{ item.state.name }} + {{ + item.state.name === 'CUSTOM' ? ' (' + item.state.code + ')' : '' + }} + + + warning + + + + +
+
+ {{ 'transfer_history_page.counter_id' | translate }} + + {{ item.counterPartyParticipantId }} + + {{ 'transfer_history_page.counter_endpoint' | translate }} + + {{ item.counterPartyConnectorEndpoint }} + + {{ 'general.details' | translate }} + + + {{ 'general.details' | translate }} + +
+
+
diff --git a/connector-ui/src/app/routes/connector-ui/transfer-history-page/transfer-history-page/transfer-history-page.component.scss b/connector-ui/src/app/routes/connector-ui/transfer-history-page/transfer-history-page/transfer-history-page.component.scss new file mode 100644 index 000000000..61532b28b --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/transfer-history-page/transfer-history-page/transfer-history-page.component.scss @@ -0,0 +1,46 @@ +#wrapper { + margin: 20px; +} + +.transfer-history-table { + width: 100%; +} + +.transfer-history-table-header th { + font-weight: bold; +} + +.mat-table { + overflow-x: scroll; +} + +.mat-cell, +.mat-header-cell { + padding: 0px 10px; +} + +.singleline-cell { + white-space: nowrap; +} + +mat-paginator { + display: inline-block; + background-color: transparent; +} + +.info-box { + display: flex; + align-items: center; + + .text { + padding-left: 10px; + } + + .button { + margin-left: auto; + } +} + +.action-button { + margin-right: 5px; +} diff --git a/connector-ui/src/app/routes/connector-ui/transfer-history-page/transfer-history-page/transfer-history-page.component.ts b/connector-ui/src/app/routes/connector-ui/transfer-history-page/transfer-history-page/transfer-history-page.component.ts new file mode 100644 index 000000000..baa1dd7bf --- /dev/null +++ b/connector-ui/src/app/routes/connector-ui/transfer-history-page/transfer-history-page/transfer-history-page.component.ts @@ -0,0 +1,137 @@ +import {Component, OnDestroy, OnInit} from '@angular/core'; +import { + EMPTY, + Observable, + Subject, + concat, + interval, + skip, + switchMap, +} from 'rxjs'; +import {catchError, map} from 'rxjs/operators'; +import {TranslateService} from '@ngx-translate/core'; +import { + TransferHistoryEntry, + TransferHistoryPage, + UiAsset, +} from '@sovity.de/edc-client'; +import {EdcApiService} from '../../../../core/services/api/edc-api.service'; +import {AssetBuilder} from '../../../../core/services/asset-builder'; +import {Fetched} from '../../../../core/services/models/fetched'; +import {UiAssetMapped} from '../../../../core/services/models/ui-asset-mapped'; +import {NotificationService} from '../../../../core/services/notification.service'; +import {ParticipantIdLocalization} from '../../../../core/services/participant-id-localization'; +import {AssetDetailDialogDataService} from '../../../../shared/business/asset-detail-dialog/asset-detail-dialog-data.service'; +import {AssetDetailDialogService} from '../../../../shared/business/asset-detail-dialog/asset-detail-dialog.service'; +import {JsonDialogService} from '../../../../shared/common/json-dialog/json-dialog.service'; + +@Component({ + selector: 'transfer-history-page', + templateUrl: './transfer-history-page.component.html', + styleUrls: ['./transfer-history-page.component.scss'], +}) +export class TransferHistoryPageComponent implements OnInit, OnDestroy { + columns: string[] = [ + 'direction', + 'lastUpdated', + 'assetName', + 'state', + 'counterPartyParticipantId', + 'counterPartyConnectorEndpoint', + 'details', + ]; + transferProcessesList: Fetched<{ + transferProcesses: Array; + }> = Fetched.empty(); + + constructor( + private edcApiService: EdcApiService, + private assetDetailDialogDataService: AssetDetailDialogDataService, + private assetDetailDialogService: AssetDetailDialogService, + private assetBuilder: AssetBuilder, + private notificationService: NotificationService, + private jsonDialogService: JsonDialogService, + public participantIdLocalization: ParticipantIdLocalization, + private translateService: TranslateService, + ) {} + + onTransferHistoryDetailsClick(item: TransferHistoryEntry) { + this.jsonDialogService.showJsonDetailDialog( + { + title: item.assetName ?? item.assetId, + subtitle: this.translateService.instant( + 'transfer_history_page.subtitle', + ), + icon: 'assignment', + objectForJson: item, + }, + this.ngOnDestroy$, + ); + } + + loadAssetDetails(item: TransferHistoryEntry): Observable { + return this.edcApiService + .getTransferProcessAsset(item.transferProcessId) + .pipe(map((asset: UiAsset) => this.assetBuilder.buildAsset(asset))); + } + + onAssetDetailsClick(item: TransferHistoryEntry) { + this.loadAssetDetails(item).subscribe({ + next: (asset) => { + const data = + this.assetDetailDialogDataService.assetDetailsReadonly(asset); + this.assetDetailDialogService.open(data, this.ngOnDestroy$); + }, + error: (error) => { + const message = this.translateService.instant( + 'notification.failed_transfer_detail_fetch', + ); + console.error(message, error); + this.notificationService.showError(message); + }, + }); + } + + ngOnInit(): void { + this.loadTransferProcesses(); + } + + loadTransferProcesses() { + const initialRequest: Observable> = + this.edcApiService.getTransferHistoryPage().pipe( + Fetched.wrap({ + failureMessage: 'Failed fetching transfer history.', + }), + ); + + const polling: Observable> = interval( + 5_000, + ).pipe( + skip(1), + switchMap(() => + this.edcApiService + .getTransferHistoryPage() + .pipe(catchError(() => EMPTY)), + ), + map((data) => Fetched.ready(data)), + ); + + return concat(initialRequest, polling) + .pipe( + Fetched.map((transferHistoryPage) => ({ + transferProcesses: transferHistoryPage.transferEntries, + })), + ) + .subscribe( + (transferProcessesList) => + (this.transferProcessesList = transferProcessesList), + ); + } + + ngOnDestroy$ = new Subject(); + + ngOnDestroy() { + this.ngOnDestroy$.next(null); + this.ngOnDestroy$.complete(); + } +} diff --git a/connector-ui/src/app/shared/business/asset-card-tag-list/asset-card-tag-list.component.html b/connector-ui/src/app/shared/business/asset-card-tag-list/asset-card-tag-list.component.html new file mode 100644 index 000000000..4392c8708 --- /dev/null +++ b/connector-ui/src/app/shared/business/asset-card-tag-list/asset-card-tag-list.component.html @@ -0,0 +1,10 @@ + + {{ version }} + {{ keyword }} + + +{{ keywords!.length - numberOfKeywordsDisplayed }} + + diff --git a/connector-ui/src/app/shared/business/asset-card-tag-list/asset-card-tag-list.component.ts b/connector-ui/src/app/shared/business/asset-card-tag-list/asset-card-tag-list.component.ts new file mode 100644 index 000000000..a656d4fd9 --- /dev/null +++ b/connector-ui/src/app/shared/business/asset-card-tag-list/asset-card-tag-list.component.ts @@ -0,0 +1,12 @@ +import {Component, HostBinding, Input} from '@angular/core'; + +@Component({ + selector: 'asset-card-tag-list', + templateUrl: './asset-card-tag-list.component.html', +}) +export class AssetCardTagListComponent { + @HostBinding('class.block') cls = true; + @Input() numberOfKeywordsDisplayed: number = 3; + @Input() keywords: string[] | undefined; + @Input() version: string | undefined; +} diff --git a/connector-ui/src/app/shared/business/asset-detail-dialog/asset-detail-dialog-data.service.ts b/connector-ui/src/app/shared/business/asset-detail-dialog/asset-detail-dialog-data.service.ts new file mode 100644 index 000000000..4680aa20d --- /dev/null +++ b/connector-ui/src/app/shared/business/asset-detail-dialog/asset-detail-dialog-data.service.ts @@ -0,0 +1,109 @@ +import {Injectable} from '@angular/core'; +import {TranslateService} from '@ngx-translate/core'; +import {DataOffer} from '../../../core/services/models/data-offer'; +import {UiAssetMapped} from '../../../core/services/models/ui-asset-mapped'; +import {ContractAgreementCardMapped} from '../../../routes/connector-ui/contract-agreement-page/contract-agreement-cards/contract-agreement-card-mapped'; +import { + AssetDetailDialogData, + OnAssetEditClickFn, +} from './asset-detail-dialog-data'; +import {AssetPropertyGridGroupBuilder} from './asset-property-grid-group-builder'; + +@Injectable() +export class AssetDetailDialogDataService { + constructor( + private assetPropertyGridGroupBuilder: AssetPropertyGridGroupBuilder, + private translateService: TranslateService, + ) {} + + assetDetailsReadonly(asset: UiAssetMapped): AssetDetailDialogData { + const propertyGridGroups = [ + this.assetPropertyGridGroupBuilder.buildAssetPropertiesGroup(asset, null), + ...this.assetPropertyGridGroupBuilder.buildAdditionalPropertiesGroups( + asset, + ), + ].filter((it) => it.properties.length); + + return { + type: 'asset-details', + asset, + propertyGridGroups, + }; + } + + assetDetailsEditable( + asset: UiAssetMapped, + opts: {onAssetEditClick: OnAssetEditClickFn}, + ): AssetDetailDialogData { + const assetDetailsReadonly = this.assetDetailsReadonly(asset); + return { + ...assetDetailsReadonly, + propertyGridGroups: [ + ...assetDetailsReadonly.propertyGridGroups, + ...this.assetPropertyGridGroupBuilder.buildOnRequestContactInformation( + asset, + ), + ], + showDeleteButton: true, + showEditButton: true, + onAssetEditClick: opts.onAssetEditClick, + }; + } + + dataOfferDetails( + dataOffer: DataOffer, + consumingLimitsExceeded: boolean, + ): AssetDetailDialogData { + const asset = dataOffer.asset; + const propertyGridGroups = [ + this.assetPropertyGridGroupBuilder.buildAssetPropertiesGroup(asset, null), + ...this.assetPropertyGridGroupBuilder.buildAdditionalPropertiesGroups( + asset, + ), + ...this.assetPropertyGridGroupBuilder.buildOnRequestContactInformation( + asset, + true, + ), + ].filter((it) => it.properties.length); + + return { + type: 'data-offer', + asset: asset, + dataOffer, + propertyGridGroups, + consumingLimitsExceeded, + }; + } + + contractAgreementDetails( + contractAgreement: ContractAgreementCardMapped, + refreshCallback: () => void, + ): AssetDetailDialogData { + const asset = contractAgreement.asset; + + const propertyGridGroups = [ + this.assetPropertyGridGroupBuilder.buildContractAgreementGroup( + contractAgreement, + ), + this.assetPropertyGridGroupBuilder.buildContractPolicyGroup( + contractAgreement.contractPolicy, + asset.title, + ), + this.assetPropertyGridGroupBuilder.buildAssetPropertiesGroup( + asset, + this.translateService.instant('general.asset'), + ), + ...this.assetPropertyGridGroupBuilder.buildAdditionalPropertiesGroups( + asset, + ), + ].filter((it) => it.properties.length); + + return { + type: 'contract-agreement', + asset: contractAgreement.asset, + contractAgreement, + propertyGridGroups, + refreshCallback, + }; + } +} diff --git a/connector-ui/src/app/shared/business/asset-detail-dialog/asset-detail-dialog-data.ts b/connector-ui/src/app/shared/business/asset-detail-dialog/asset-detail-dialog-data.ts new file mode 100644 index 000000000..a40a6f6b5 --- /dev/null +++ b/connector-ui/src/app/shared/business/asset-detail-dialog/asset-detail-dialog-data.ts @@ -0,0 +1,25 @@ +import {UiAssetMapped} from 'src/app/core/services/models/ui-asset-mapped'; +import {DataOffer} from '../../../core/services/models/data-offer'; +import {ContractAgreementCardMapped} from '../../../routes/connector-ui/contract-agreement-page/contract-agreement-cards/contract-agreement-card-mapped'; +import {PropertyGridGroup} from '../../common/property-grid-group/property-grid-group'; + +export interface AssetDetailDialogData { + type: 'asset-details' | 'data-offer' | 'contract-agreement'; + propertyGridGroups: PropertyGridGroup[]; + asset: UiAssetMapped; + dataOffer?: DataOffer; + consumingLimitsExceeded?: boolean; + contractAgreement?: ContractAgreementCardMapped; + showDeleteButton?: boolean; + showEditButton?: boolean; + onAssetEditClick?: OnAssetEditClickFn; + refreshCallback?: () => void; +} + +export type OnAssetEditClickFn = ( + asset: UiAssetMapped, + /** + * Required so that after the editing the detail dialog can be updated again + */ + afterEditCb: (updatedDialogData: AssetDetailDialogData) => void, +) => void; diff --git a/connector-ui/src/app/shared/business/asset-detail-dialog/asset-detail-dialog-result.ts b/connector-ui/src/app/shared/business/asset-detail-dialog/asset-detail-dialog-result.ts new file mode 100644 index 000000000..8fede8221 --- /dev/null +++ b/connector-ui/src/app/shared/business/asset-detail-dialog/asset-detail-dialog-result.ts @@ -0,0 +1,6 @@ +export interface AssetDetailDialogResult { + /** + * When deleting an asset, update asset list + */ + refreshList: boolean; +} diff --git a/connector-ui/src/app/shared/business/asset-detail-dialog/asset-detail-dialog.component.html b/connector-ui/src/app/shared/business/asset-detail-dialog/asset-detail-dialog.component.html new file mode 100644 index 000000000..c4b84e3e2 --- /dev/null +++ b/connector-ui/src/app/shared/business/asset-detail-dialog/asset-detail-dialog.component.html @@ -0,0 +1,240 @@ +
+
+ + {{ asset.dataSourceAvailability === 'LIVE' ? 'upload' : 'contact_page' }} + + + {{ + data.contractAgreement!.direction === 'PROVIDING' + ? data.contractAgreement!.isTerminated + ? 'file_upload_off' + : 'file_upload' + : data.contractAgreement!.isTerminated + ? 'file_download_off' + : 'file_download' + }} + + +
+
+ {{ asset.title }} +
+
+ {{ asset.creatorOrganizationName }} +
+
+
+
+ + +
+
+ +
+ + + + +
+
+
+ +
+
+
+ {{ data.contractAgreement!.terminationInformation!.reason }} +
+
+ {{ data.contractAgreement!.terminationInformation!.detail }} +
+
+ + · {{ 'general.terminated_by' | translate }} + {{ + data.contractAgreement!.terminationInformation?.terminatedBy === + 'SELF' + ? 'you' + : 'the counter-party' + }} +
+
+
+
+ + + + +
+
+
+ + +
+ + {{ keyword }} + +
+ + + +
+ {{ 'component_library.t_history' | translate }} +
+ + +
+ + + + + + + + + + +
+ {{ 'asset_detail_dialog.on_request_data_offer_title' | translate }} +
+
+
+
+ + +
+
+ +
+ +
+ + + + + + + + + + + + +
+
+
diff --git a/connector-ui/src/app/shared/business/asset-detail-dialog/asset-detail-dialog.component.scss b/connector-ui/src/app/shared/business/asset-detail-dialog/asset-detail-dialog.component.scss new file mode 100644 index 000000000..b16dfa10f --- /dev/null +++ b/connector-ui/src/app/shared/business/asset-detail-dialog/asset-detail-dialog.component.scss @@ -0,0 +1,4 @@ +mat-dialog-content, +.mat-card-header { + width: 800px; +} diff --git a/connector-ui/src/app/shared/business/asset-detail-dialog/asset-detail-dialog.component.ts b/connector-ui/src/app/shared/business/asset-detail-dialog/asset-detail-dialog.component.ts new file mode 100644 index 000000000..3c7e526fb --- /dev/null +++ b/connector-ui/src/app/shared/business/asset-detail-dialog/asset-detail-dialog.component.ts @@ -0,0 +1,255 @@ +import {DOCUMENT} from '@angular/common'; +import {Component, Inject, OnDestroy} from '@angular/core'; +import { + MAT_DIALOG_DATA, + MatDialog, + MatDialogRef, +} from '@angular/material/dialog'; +import {Observable, Subject, isObservable} from 'rxjs'; +import {filter, finalize, takeUntil} from 'rxjs/operators'; +import {TranslateService} from '@ngx-translate/core'; +import {UiContractOffer} from '@sovity.de/edc-client'; +import {MailtoLinkBuilder} from 'src/app/core/services/mailto-link-builder'; +import {EdcApiService} from '../../../core/services/api/edc-api.service'; +import {ConnectorLimitsService} from '../../../core/services/connector-limits.service'; +import {ContractNegotiationService} from '../../../core/services/contract-negotiation.service'; +import {UiAssetMapped} from '../../../core/services/models/ui-asset-mapped'; +import {NotificationService} from '../../../core/services/notification.service'; +import {ContractAgreementTerminationDialogData} from '../../../routes/connector-ui/contract-agreement-page/contract-agreement-termination-dialog/contract-agreement-termination-dialog-data'; +import {ContractAgreementTerminationDialogResult} from '../../../routes/connector-ui/contract-agreement-page/contract-agreement-termination-dialog/contract-agreement-termination-dialog-result'; +import {ContractAgreementTerminationDialogComponent} from '../../../routes/connector-ui/contract-agreement-page/contract-agreement-termination-dialog/contract-agreement-termination-dialog.component'; +import {ContractAgreementTransferDialogData} from '../../../routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/contract-agreement-transfer-dialog-data'; +import {ContractAgreementTransferDialogResult} from '../../../routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/contract-agreement-transfer-dialog-result'; +import {ContractAgreementTransferDialogComponent} from '../../../routes/connector-ui/contract-agreement-page/contract-agreement-transfer-dialog/contract-agreement-transfer-dialog.component'; +import { + ConfirmDialogModel, + ConfirmationDialogComponent, +} from '../../common/confirmation-dialog/confirmation-dialog.component'; +import {PropertyGridGroup} from '../../common/property-grid-group/property-grid-group'; +import {AssetDetailDialogData} from './asset-detail-dialog-data'; +import {AssetDetailDialogResult} from './asset-detail-dialog-result'; + +/** + * Asset Detail Dialog + * Contract Agreement Detail Dialog + * Contract Offer Detail Dialog + *

+ * All in one! If that's a good idea remains to be seen. + */ +@Component({ + selector: 'asset-detail-dialog', + templateUrl: './asset-detail-dialog.component.html', + styleUrls: ['./asset-detail-dialog.component.scss'], +}) +export class AssetDetailDialogComponent implements OnDestroy { + data!: AssetDetailDialogData; + asset!: UiAssetMapped; + propGroups!: PropertyGridGroup[]; + + limitsExceeded: boolean | null = null; + + loading = false; + + get isProgressBarVisible(): boolean { + switch (this.data.type) { + case 'data-offer': + return ( + this.data.dataOffer?.contractOffers?.some((it) => + this.contractNegotiationService.isBusy(it), + ) ?? false + ); + case 'contract-agreement': + return this.data.contractAgreement!.isInProgress; + default: + return false; + } + } + + get isLiveDataOffer(): boolean { + return ( + this.data.type === 'data-offer' && + this.data.asset.dataSourceAvailability === 'LIVE' + ); + } + + get isOnRequestDataOffer(): boolean { + return ( + this.data.type === 'data-offer' && + this.data.asset.dataSourceAvailability === 'ON_REQUEST' + ); + } + + constructor( + private edcApiService: EdcApiService, + private notificationService: NotificationService, + private connectorLimitsService: ConnectorLimitsService, + private matDialog: MatDialog, + private matDialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) + private _data: AssetDetailDialogData | Observable, + public contractNegotiationService: ContractNegotiationService, + private mailtoLinkBuilder: MailtoLinkBuilder, + @Inject(DOCUMENT) private document: Document, + private translateService: TranslateService, + ) { + if (isObservable(this._data)) { + this._data + .pipe(takeUntil(this.ngOnDestroy$)) + .subscribe((data) => this.setData(data)); + } else { + this.setData(this._data); + } + } + + setData(data: AssetDetailDialogData) { + this.data = data; + this.limitsExceeded = data.consumingLimitsExceeded ?? null; + this.asset = this.data.asset; + this.propGroups = this.data.propertyGridGroups; + } + + onContactClick() { + if (!this.asset.onRequestContactEmail) { + throw new Error('On request asset must have contact email'); + } + + const url = this.mailtoLinkBuilder.buildMailtoUrl( + this.asset.onRequestContactEmail, + this.asset.onRequestContactEmailSubject ?? + "I'm interested in your data offer", + ); + this.document.location.href = url; + } + + onEditClick() { + if (this.data.onAssetEditClick) { + this.data.onAssetEditClick(this.data.asset, (data) => this.setData(data)); + } + } + + onDeleteClick() { + this.confirmDelete().subscribe(() => { + this.blockingRequest({ + successMessage: `Deleted asset ${this.asset.assetId}.`, + failureMessage: `Failed deleting asset ${this.asset.assetId}.`, + onsuccess: () => this.close({refreshList: true}), + req: () => this.edcApiService.deleteAsset(this.asset.assetId), + }); + }); + } + + onNegotiateClick(contractOffer: UiContractOffer) { + this.connectorLimitsService + .isConsumingAgreementLimitExceeded() + .subscribe((limitExceeded) => { + if (!limitExceeded) { + this.limitsExceeded = false; + this.contractNegotiationService.negotiate( + this.data.dataOffer!, + contractOffer, + ); + } else { + this.limitsExceeded = true; + this.notificationService.showError( + 'Cannot negotiate. Maximum number of active consuming contracts reached.', + ); + } + }); + } + + onTransferClick() { + const data: ContractAgreementTransferDialogData = { + contractId: this.data.contractAgreement?.contractAgreementId!!, + asset: this.data.asset, + }; + const ref = this.matDialog.open(ContractAgreementTransferDialogComponent, { + data, + }); + + ref + .afterClosed() + .subscribe( + (result: ContractAgreementTransferDialogResult | undefined) => { + if (result) { + this.data.refreshCallback?.(); + } + }, + ); + } + + onTerminateClick() { + const data: ContractAgreementTerminationDialogData = { + contractId: this.data.contractAgreement?.contractAgreementId!!, + asset: this.data.asset, + }; + const ref = this.matDialog.open( + ContractAgreementTerminationDialogComponent, + {data}, + ); + + ref + .afterClosed() + .subscribe( + (result: ContractAgreementTerminationDialogResult | undefined) => { + if (result) { + this.data.refreshCallback?.(); + } + }, + ); + } + + private confirmDelete(): Observable { + const dialogData = ConfirmDialogModel.forDelete( + 'general.asset', + this.asset.title, + this.translateService, + ); + const ref = this.matDialog.open(ConfirmationDialogComponent, { + maxWidth: '20%', + data: dialogData, + }); + return ref.afterClosed().pipe(filter((it) => !!it)); + } + + private blockingRequest(opts: { + req: () => Observable; + successMessage: string; + failureMessage: string; + onsuccess?: () => void; + }) { + if (this.loading) { + return; + } + + this.loading = true; + opts + .req() + .pipe( + takeUntil(this.ngOnDestroy$), + finalize(() => (this.loading = false)), + ) + .subscribe({ + complete: () => { + this.notificationService.showInfo(opts.successMessage); + if (opts.onsuccess) { + opts.onsuccess(); + } + }, + error: (err) => { + console.error(opts.failureMessage, err); + this.notificationService.showError(opts.failureMessage); + }, + }); + } + + private close(result: AssetDetailDialogResult) { + this.matDialogRef.close(result); + } + + ngOnDestroy$ = new Subject(); + + ngOnDestroy(): void { + this.ngOnDestroy$.next(null); + this.ngOnDestroy$.complete(); + } +} diff --git a/connector-ui/src/app/shared/business/asset-detail-dialog/asset-detail-dialog.service.ts b/connector-ui/src/app/shared/business/asset-detail-dialog/asset-detail-dialog.service.ts new file mode 100644 index 000000000..2772149ed --- /dev/null +++ b/connector-ui/src/app/shared/business/asset-detail-dialog/asset-detail-dialog.service.ts @@ -0,0 +1,29 @@ +import {Injectable} from '@angular/core'; +import {MatDialog} from '@angular/material/dialog'; +import {NEVER, Observable} from 'rxjs'; +import {showDialogUntil} from '../../../core/utils/mat-dialog-utils'; +import {AssetDetailDialogData} from './asset-detail-dialog-data'; +import {AssetDetailDialogResult} from './asset-detail-dialog-result'; +import {AssetDetailDialogComponent} from './asset-detail-dialog.component'; + +@Injectable() +export class AssetDetailDialogService { + constructor(private dialog: MatDialog) {} + + /** + * Shows an Asset Detail Dialog until until$ emits / completes + * @param data Asset Detail Dialog data, or a stream if there's a need to refresh the data + * @param until$ observable that controls the lifetime of the dialog + */ + open( + data: AssetDetailDialogData | Observable, + until$: Observable = NEVER, + ): Observable { + return showDialogUntil( + this.dialog, + AssetDetailDialogComponent, + {data, maxWidth: '1000px', maxHeight: '90vh', autoFocus: false}, + until$, + ); + } +} diff --git a/connector-ui/src/app/shared/business/asset-detail-dialog/asset-property-grid-group-builder.ts b/connector-ui/src/app/shared/business/asset-detail-dialog/asset-property-grid-group-builder.ts new file mode 100644 index 000000000..5007ffcc6 --- /dev/null +++ b/connector-ui/src/app/shared/business/asset-detail-dialog/asset-property-grid-group-builder.ts @@ -0,0 +1,475 @@ +import {Injectable} from '@angular/core'; +import {TranslateService} from '@ngx-translate/core'; +import {UiPolicy} from '@sovity.de/edc-client'; +import {ActiveFeatureSet} from '../../../core/config/active-feature-set'; +import {UiAssetMapped} from '../../../core/services/models/ui-asset-mapped'; +import {ParticipantIdLocalization} from '../../../core/services/participant-id-localization'; +import {ContractAgreementCardMapped} from '../../../routes/connector-ui/contract-agreement-page/contract-agreement-cards/contract-agreement-card-mapped'; +import {PropertyGridGroup} from '../../common/property-grid-group/property-grid-group'; +import {PropertyGridField} from '../../common/property-grid/property-grid-field'; +import {PropertyGridFieldService} from '../../common/property-grid/property-grid-field.service'; +import {UrlListDialogService} from '../../common/url-list-dialog/url-list-dialog.service'; +import {ConditionsForUseDialogService} from '../conditions-for-use-dialog/conditions-for-use-dialog.service'; +import {PolicyPropertyFieldBuilder} from './policy-property-field-builder'; + +@Injectable() +export class AssetPropertyGridGroupBuilder { + constructor( + private activeFeatureSet: ActiveFeatureSet, + private propertyGridUtils: PropertyGridFieldService, + private urlListDialogService: UrlListDialogService, + private conditionsForUseDialogService: ConditionsForUseDialogService, + private policyPropertyFieldBuilder: PolicyPropertyFieldBuilder, + private translateService: TranslateService, + private participantIdLocalization: ParticipantIdLocalization, + ) {} + + buildAssetPropertiesGroup( + asset: UiAssetMapped, + groupLabel: string | null, + ): PropertyGridGroup { + const fields: PropertyGridField[] = [ + { + icon: 'category', + label: 'ID', + ...this.propertyGridUtils.guessValue(asset.assetId), + }, + { + icon: 'file_copy', + label: 'Version', + ...this.propertyGridUtils.guessValue(asset.version), + }, + { + icon: 'language', + label: this.translateService.instant('general.language'), + ...this.propertyGridUtils.guessValue(asset.language?.label), + }, + { + icon: 'apartment', + label: this.translateService.instant('general.publisher'), + ...this.propertyGridUtils.guessValue(asset.publisherHomepage), + }, + { + icon: 'bookmarks', + label: this.translateService.instant('general.endpoint_doc'), + ...this.propertyGridUtils.guessValue(asset.landingPageUrl), + }, + { + icon: 'gavel', + label: this.translateService.instant('general.standard_license'), + ...this.propertyGridUtils.guessValue(asset.licenseUrl), + }, + { + icon: 'category', + label: this.translateService.instant( + 'component_library.participant_id', + ), + ...this.propertyGridUtils.guessValue(asset.participantId), + }, + { + icon: 'account_circle', + label: this.translateService.instant('component_library.organization'), + ...this.propertyGridUtils.guessValue(asset.creatorOrganizationName), + }, + this.buildConnectorEndpointField(asset.connectorEndpoint), + ...this.buildHttpDatasourceFields(asset), + ]; + + if (this.activeFeatureSet.hasMdsFields()) { + fields.push(...this.buildMdsProperties(asset)); + } + + return { + groupLabel, + properties: fields, + }; + } + + private buildHttpDatasourceFields(asset: UiAssetMapped): PropertyGridField[] { + const fields: PropertyGridField[] = []; + + const hints: {label: string; value: boolean | undefined}[] = [ + { + label: this.translateService.instant('general.method'), + value: asset.httpDatasourceHintsProxyMethod, + }, + { + label: this.translateService.instant('general.path'), + value: asset.httpDatasourceHintsProxyPath, + }, + { + label: this.translateService.instant('general.params'), + value: asset.httpDatasourceHintsProxyQueryParams, + }, + { + label: this.translateService.instant('general.body'), + value: asset.httpDatasourceHintsProxyBody, + }, + ]; + + if (hints.some((hint) => hint.value != null)) { + const text = hints.some((hint) => hint.value) + ? hints + .filter((hint) => hint.value) + .map((hint) => hint.label) + .join(', ') + : 'Disabled'; + + fields.push({ + icon: 'api', + label: this.translateService.instant('component_library.http_param'), + text, + }); + } + + if (asset.mediaType) { + fields.push({ + icon: 'category', + label: this.translateService.instant('general.content_type'), + ...this.propertyGridUtils.guessValue(asset.mediaType), + }); + } + + return fields; + } + + buildAdditionalPropertiesGroups(asset: UiAssetMapped): PropertyGridGroup[] { + const additionalProperties: PropertyGridField[] = []; + if (!this.activeFeatureSet.hasMdsFields()) { + additionalProperties.push(...this.buildMdsProperties(asset)); + } + + const customProperties: PropertyGridField[] = [ + asset.customJsonProperties, + asset.customJsonLdProperties, + ] + .flat() + .map((prop) => { + return { + icon: 'category ', + label: prop.key, + labelTitle: prop.key, + ...this.propertyGridUtils.guessValue(prop.value), + }; + }); + + const privateCustomProperties: PropertyGridField[] = [ + asset.privateCustomJsonProperties, + asset.privateCustomJsonLdProperties, + ] + .flat() + .map((prop) => { + return { + icon: 'category ', + label: prop.key, + labelTitle: prop.key, + ...this.propertyGridUtils.guessValue(prop.value), + }; + }); + + return [ + { + groupLabel: this.translateService.instant( + 'general.additional_properties', + ), + properties: additionalProperties, + }, + { + groupLabel: 'Custom Properties', + properties: customProperties, + }, + { + groupLabel: 'Private Properties', + properties: privateCustomProperties, + }, + ]; + } + + buildMdsProperties(asset: UiAssetMapped): PropertyGridField[] { + const fields: PropertyGridField[] = []; + if (asset.transportMode) { + fields.push({ + icon: 'commute', + label: this.translateService.instant('general.transport_mode'), + ...this.propertyGridUtils.guessValue(asset.transportMode?.label), + }); + } + if (asset.dataCategory) { + fields.push({ + icon: 'commute', + label: this.translateService.instant('general.data_category'), + ...this.propertyGridUtils.guessValue(asset.dataCategory?.label), + }); + } + if (asset.dataSubcategory) { + fields.push({ + icon: 'commute', + label: this.translateService.instant('general.data_subcategory'), + ...this.propertyGridUtils.guessValue(asset.dataSubcategory?.label), + }); + } + if (asset.dataModel) { + fields.push({ + icon: 'category', + label: this.translateService.instant('general.data_model'), + ...this.propertyGridUtils.guessValue(asset.dataModel), + }); + } + if (asset.geoReferenceMethod) { + fields.push({ + icon: 'commute', + label: this.translateService.instant('general.geo_reference_method'), + ...this.propertyGridUtils.guessValue(asset.geoReferenceMethod), + }); + } + if (asset.geoLocation) { + fields.push({ + icon: 'location_on', + label: this.translateService.instant('general.geo_location'), + ...this.propertyGridUtils.guessValue(asset.geoLocation), + }); + } + if (asset.nutsLocations?.length) { + fields.push(this.buildNutsLocationsField(asset.nutsLocations)); + } + if (asset.sovereignLegalName) { + fields.push({ + icon: 'account_balance', + label: this.translateService.instant('general.sovereign'), + ...this.propertyGridUtils.guessValue(asset.sovereignLegalName), + }); + } + if (asset.dataSampleUrls?.length) { + fields.push( + this.buildDataSampleUrlsField(asset.dataSampleUrls, asset.title), + ); + } + if (asset.referenceFileUrls?.length) { + fields.push( + this.buildReferenceFileUrlsField( + asset.referenceFileUrls, + asset.referenceFilesDescription, + asset.title, + ), + ); + } + if (asset.conditionsForUse) { + fields.push( + this.buildConditionsForUseField(asset.conditionsForUse, asset.title), + ); + } + if (asset.dataUpdateFrequency) { + fields.push({ + icon: 'timelapse', + label: this.translateService.instant('general.frequency'), + ...this.propertyGridUtils.guessValue(asset.dataUpdateFrequency), + }); + } + if (asset.temporalCoverageFrom || asset.temporalCoverageToInclusive) { + fields.push({ + icon: 'today', + label: this.translateService.instant('general.coverage'), + ...this.propertyGridUtils.guessValue( + this.buildTemporalCoverageString( + asset.temporalCoverageFrom, + asset.temporalCoverageToInclusive, + ), + ), + }); + } + return fields; + } + + buildContractAgreementGroup(contractAgreement: ContractAgreementCardMapped) { + const properties: PropertyGridField[] = [ + { + icon: 'category', + label: this.translateService.instant('general.signed'), + ...this.propertyGridUtils.guessValue( + this.propertyGridUtils.formatDateWithTime( + contractAgreement.contractSigningDate, + ), + ), + }, + { + icon: 'policy', + label: this.translateService.instant('general.direction'), + ...this.propertyGridUtils.guessValue( + contractAgreement.direction === 'CONSUMING' + ? this.translateService.instant('general.consuming') + : this.translateService.instant('general.providing'), + ), + }, + { + icon: 'category', + label: this.translateService.instant('general.contract') + ' ID', + ...this.propertyGridUtils.guessValue( + contractAgreement.contractAgreementId, + ), + }, + { + icon: 'link', + label: `${this.translateService.instant('general.oth_connector')} ${ + this.participantIdLocalization.participantId + }`, + ...this.propertyGridUtils.guessValue(contractAgreement.counterPartyId), + }, + { + icon: 'link', + label: this.translateService.instant( + 'transfer_history_page.counter_endpoint', + ), + ...this.propertyGridUtils.guessValue( + contractAgreement.counterPartyAddress, + ), + }, + ]; + + if (contractAgreement.isConsumingLimitsEnforced) { + properties.push({ + icon: contractAgreement.isTerminated ? 'sync_disabled' : 'sync', + label: 'Status', + additionalClasses: contractAgreement.isTerminated ? 'text-warn' : '', + }); + } + + return { + groupLabel: this.translateService.instant('general.contract'), + properties, + }; + } + + buildContractPolicyGroup( + contractPolicy: UiPolicy, + subtitle: string, + ): PropertyGridGroup { + return { + groupLabel: this.translateService.instant('general.contract_policy'), + properties: this.policyPropertyFieldBuilder.buildPolicyPropertyFields( + contractPolicy, + this.translateService.instant('general.contract_policy') + ' JSON-LD', + subtitle, + ), + }; + } + + buildConnectorEndpointField(endpoint: string): PropertyGridField { + return { + icon: 'link', + label: this.translateService.instant('general.endpoint'), + ...this.propertyGridUtils.guessValue(endpoint), + }; + } + + buildNutsLocationsField(locations: string[]): PropertyGridField { + return { + icon: 'location_on', + label: this.translateService.instant('general.nuts'), + text: locations.join(', '), + }; + } + + buildDataSampleUrlsField( + dataSampleUrls: string[], + title: string, + ): PropertyGridField { + return { + icon: 'attachment', + label: this.translateService.instant('general.data'), + text: this.translateService.instant('general.show_data'), + onclick: () => + this.urlListDialogService.showUrlListDialog({ + title: this.translateService.instant('general.data'), + subtitle: title, + icon: 'attachment', + urls: dataSampleUrls, + }), + }; + } + + buildConditionsForUseField( + conditionsForUse: string, + title: string, + ): PropertyGridField { + return { + icon: 'description', + label: this.translateService.instant('general.conditions'), + text: 'Show Conditions For Use', // TODO + onclick: () => + this.conditionsForUseDialogService.showConditionsForUseDialog({ + title: 'Conditions For Use', + subtitle: title, + icon: 'description', + description: conditionsForUse, + }), + }; + } + + buildReferenceFileUrlsField( + referenceFileUrls: string[], + description: string | undefined, + title: string, + ): PropertyGridField { + return { + icon: 'receipt', + label: this.translateService.instant('general.files'), + text: this.translateService.instant('general.show_files'), + onclick: () => + this.urlListDialogService.showUrlListDialog({ + title: this.translateService.instant('general.show_files'), + subtitle: title, + icon: 'receipt', + urls: referenceFileUrls, + description: description, + }), + }; + } + + buildTemporalCoverageString( + start: Date | undefined, + end: Date | undefined, + ): string { + if (!end) { + return `Start: ${this.propertyGridUtils.formatDate(start)}`; + } + + if (!start) { + return `End: ${this.propertyGridUtils.formatDate(end)}`; + } + + return `${this.propertyGridUtils.formatDate( + start, + )} - ${this.propertyGridUtils.formatDate(end)}`; + } + + buildOnRequestContactInformation( + asset: UiAssetMapped, + isMailHidden = false, + ): PropertyGridGroup[] { + if (asset.dataSourceAvailability === 'LIVE') { + return []; + } + return [ + { + groupLabel: 'Contact Information', + properties: [ + { + icon: 'mail', + label: 'Contact E-Mail Address', + copyButton: true, + hideFieldValue: isMailHidden, + ...this.propertyGridUtils.guessValue(asset.onRequestContactEmail), + }, + { + icon: 'subject', + label: 'Preferred E-Mail Subject', + copyButton: true, + ...this.propertyGridUtils.guessValue( + asset.onRequestContactEmailSubject, + ), + }, + ], + }, + ]; + } +} diff --git a/connector-ui/src/app/shared/business/asset-detail-dialog/policy-property-field-builder.ts b/connector-ui/src/app/shared/business/asset-detail-dialog/policy-property-field-builder.ts new file mode 100644 index 000000000..c5fa5478c --- /dev/null +++ b/connector-ui/src/app/shared/business/asset-detail-dialog/policy-property-field-builder.ts @@ -0,0 +1,44 @@ +import {Injectable} from '@angular/core'; +import {TranslateService} from '@ngx-translate/core'; +import {UiPolicy} from '@sovity.de/edc-client'; +import {JsonDialogService} from '../../common/json-dialog/json-dialog.service'; +import {PropertyGridField} from '../../common/property-grid/property-grid-field'; +import {PolicyMapper} from '../policy-editor/model/policy-mapper'; + +@Injectable() +export class PolicyPropertyFieldBuilder { + constructor( + private jsonDialogService: JsonDialogService, + private policyMapper: PolicyMapper, + private translateService: TranslateService, + ) {} + + buildPolicyPropertyFields( + policy: UiPolicy, + policyDetailDialogTitle: string, + policyDetailDialogSubtitle: string, + ): PropertyGridField[] { + return [ + { + icon: 'policy', + label: this.translateService.instant('general.contract_policy'), + policy: this.policyMapper.buildPolicy(policy.expression!), + policyErrors: policy.errors || [], + additionalContainerClasses: 'col-span-2', + }, + { + icon: 'policy', + label: + this.translateService.instant('general.contract_policy') + ' JSON-LD', + text: this.translateService.instant('component_library.json_ld'), + onclick: () => + this.jsonDialogService.showJsonDetailDialog({ + title: policyDetailDialogTitle, + subtitle: policyDetailDialogSubtitle, + icon: 'policy', + objectForJson: JSON.parse(policy.policyJsonLd), + }), + }, + ]; + } +} diff --git a/connector-ui/src/app/shared/business/conditions-for-use-dialog/conditions-for-use-dialog.component.html b/connector-ui/src/app/shared/business/conditions-for-use-dialog/conditions-for-use-dialog.component.html new file mode 100644 index 000000000..850487d79 --- /dev/null +++ b/connector-ui/src/app/shared/business/conditions-for-use-dialog/conditions-for-use-dialog.component.html @@ -0,0 +1,28 @@ +

+
+ + {{ data.icon }} + +
+
+ {{ data.title }} +
+
+ {{ data.subtitle }} +
+
+
+
+ +
+
+ +
+
+ +
+ +
diff --git a/connector-ui/src/app/shared/business/conditions-for-use-dialog/conditions-for-use-dialog.component.ts b/connector-ui/src/app/shared/business/conditions-for-use-dialog/conditions-for-use-dialog.component.ts new file mode 100644 index 000000000..457f1f618 --- /dev/null +++ b/connector-ui/src/app/shared/business/conditions-for-use-dialog/conditions-for-use-dialog.component.ts @@ -0,0 +1,14 @@ +import {Component, Inject} from '@angular/core'; +import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog'; +import {ConditionsForUseDialogData} from './conditions-for-use-dialog.data'; + +@Component({ + selector: 'conditions-for-use-dialog', + templateUrl: './conditions-for-use-dialog.component.html', +}) +export class ConditionsForUseDialogComponent { + constructor( + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: ConditionsForUseDialogData, + ) {} +} diff --git a/connector-ui/src/app/shared/business/conditions-for-use-dialog/conditions-for-use-dialog.data.ts b/connector-ui/src/app/shared/business/conditions-for-use-dialog/conditions-for-use-dialog.data.ts new file mode 100644 index 000000000..1d10bc026 --- /dev/null +++ b/connector-ui/src/app/shared/business/conditions-for-use-dialog/conditions-for-use-dialog.data.ts @@ -0,0 +1,6 @@ +export interface ConditionsForUseDialogData { + title: string; + subtitle: string; + icon: string; + description?: string; +} diff --git a/connector-ui/src/app/shared/business/conditions-for-use-dialog/conditions-for-use-dialog.service.ts b/connector-ui/src/app/shared/business/conditions-for-use-dialog/conditions-for-use-dialog.service.ts new file mode 100644 index 000000000..04cc455ae --- /dev/null +++ b/connector-ui/src/app/shared/business/conditions-for-use-dialog/conditions-for-use-dialog.service.ts @@ -0,0 +1,28 @@ +import {Injectable} from '@angular/core'; +import {MatDialog} from '@angular/material/dialog'; +import {NEVER, Observable} from 'rxjs'; +import {showDialogUntil} from '../../../core/utils/mat-dialog-utils'; +import {ConditionsForUseDialogComponent} from './conditions-for-use-dialog.component'; +import {ConditionsForUseDialogData} from './conditions-for-use-dialog.data'; + +@Injectable() +export class ConditionsForUseDialogService { + constructor(private dialog: MatDialog) {} + + /** + * Shows JSON Detail Dialog until until$ emits / completes + * @param data json detail dialog data + * @param until$ observable that controls the lifetime of the dialog + */ + showConditionsForUseDialog( + data: ConditionsForUseDialogData, + until$: Observable = NEVER, + ): Observable { + return showDialogUntil( + this.dialog, + ConditionsForUseDialogComponent, + {data, autoFocus: false}, + until$, + ); + } +} diff --git a/connector-ui/src/app/shared/business/contract-offer-icon/contract-offer-icon.component.ts b/connector-ui/src/app/shared/business/contract-offer-icon/contract-offer-icon.component.ts new file mode 100644 index 000000000..02e9fb503 --- /dev/null +++ b/connector-ui/src/app/shared/business/contract-offer-icon/contract-offer-icon.component.ts @@ -0,0 +1,51 @@ +import {Component, Input} from '@angular/core'; +import {ContractNegotiationService} from '../../../core/services/contract-negotiation.service'; +import {DataOffer} from '../../../core/services/models/data-offer'; + +@Component({ + selector: 'contract-offer-icon', + template: ` + +
+ + done + +
+ + + sim_card + + + contact_page + `, +}) +export class ContractOfferIconComponent { + @Input() + dataOffer!: DataOffer; + + constructor(public contractNegotiationService: ContractNegotiationService) {} + + isNegotiated(): boolean { + return this.dataOffer?.contractOffers?.some((it) => + this.contractNegotiationService.isNegotiated(it), + ); + } + + isOnRequestAsset(): boolean { + return this.dataOffer.asset.dataSourceAvailability === 'ON_REQUEST'; + } +} diff --git a/connector-ui/src/app/shared/business/contract-offer-mini-list/contract-offer-mini-list.component.html b/connector-ui/src/app/shared/business/contract-offer-mini-list/contract-offer-mini-list.component.html new file mode 100644 index 000000000..33722f0ac --- /dev/null +++ b/connector-ui/src/app/shared/business/contract-offer-mini-list/contract-offer-mini-list.component.html @@ -0,0 +1,69 @@ + +
+ Contract Offers +
+ No contract offers available. +
+ + +
+ + + {{ 'general.contract_offer' | translate }} + {{ data.contractOffers.length >= 2 ? i + 1 : '' }} + + + + + +
+ + + + + +
+ + +
+
diff --git a/connector-ui/src/app/shared/business/contract-offer-mini-list/contract-offer-mini-list.component.ts b/connector-ui/src/app/shared/business/contract-offer-mini-list/contract-offer-mini-list.component.ts new file mode 100644 index 000000000..b0b4ea2a8 --- /dev/null +++ b/connector-ui/src/app/shared/business/contract-offer-mini-list/contract-offer-mini-list.component.ts @@ -0,0 +1,41 @@ +import { + Component, + EventEmitter, + HostBinding, + Input, + Output, +} from '@angular/core'; +import {DataOffer} from 'src/app/core/services/models/data-offer'; +import {ContractNegotiationService} from '../../../core/services/contract-negotiation.service'; +import {ContractOffer} from '../../../core/services/models/contract-offer'; +import {PropertyGridField} from '../../common/property-grid/property-grid-field'; + +@Component({ + selector: 'contract-offer-mini-list', + templateUrl: 'contract-offer-mini-list.component.html', +}) +export class ContractOfferMiniListComponent { + @Input() + data!: DataOffer; + + @HostBinding('class.flex') + @HostBinding('class.flex-col') + cls = true; + + @Output() + negotiateClick = new EventEmitter(); + + constructor(public contractNegotiationService: ContractNegotiationService) {} + + contractOfferIdGroup(id: string): PropertyGridField[] { + return [ + { + icon: 'category', + label: 'Contract Offer Id', + text: this.data.contractOffers.find((it) => it.contractOfferId == id) + ?.contractOfferId, + additionalClasses: 'min-h-fit h-fit break-all', + }, + ]; + } +} diff --git a/connector-ui/src/app/shared/business/data-offer-cards/data-offer-cards.component.html b/connector-ui/src/app/shared/business/data-offer-cards/data-offer-cards.component.html new file mode 100644 index 000000000..d0d49bd2d --- /dev/null +++ b/connector-ui/src/app/shared/business/data-offer-cards/data-offer-cards.component.html @@ -0,0 +1,35 @@ + + + + + + + {{ asset.title }} + + + {{ asset.creatorOrganizationName }} + + + + + + + + + + + + + diff --git a/connector-ui/src/app/shared/business/data-offer-cards/data-offer-cards.component.ts b/connector-ui/src/app/shared/business/data-offer-cards/data-offer-cards.component.ts new file mode 100644 index 000000000..2c87c6c87 --- /dev/null +++ b/connector-ui/src/app/shared/business/data-offer-cards/data-offer-cards.component.ts @@ -0,0 +1,34 @@ +import { + Component, + EventEmitter, + HostBinding, + Input, + Output, +} from '@angular/core'; +import {ContractNegotiationService} from '../../../core/services/contract-negotiation.service'; +import {DataOffer} from '../../../core/services/models/data-offer'; + +@Component({ + selector: 'data-offer-cards', + templateUrl: './data-offer-cards.component.html', +}) +export class DataOfferCardsComponent { + @HostBinding('class.flex') + @HostBinding('class.flex-wrap') + @HostBinding('class.gap-[10px]') + cls = true; + + @Input() + dataOffers: DataOffer[] = []; + + @Output() + dataOfferClick = new EventEmitter(); + + constructor(public contractNegotiationService: ContractNegotiationService) {} + + isBusy(dataOffer: DataOffer): boolean { + return dataOffer.contractOffers.some((it) => + this.contractNegotiationService.isBusy(it), + ); + } +} diff --git a/connector-ui/src/app/shared/business/edit-asset-form/edit-asset-form-required-providers.ts b/connector-ui/src/app/shared/business/edit-asset-form/edit-asset-form-required-providers.ts new file mode 100644 index 000000000..d75efc586 --- /dev/null +++ b/connector-ui/src/app/shared/business/edit-asset-form/edit-asset-form-required-providers.ts @@ -0,0 +1,17 @@ +import {AssetRequestBuilder} from 'src/app/core/services/asset-request-builder'; +import {policyFormRequiredViewProviders} from '../policy-editor/editor/policy-form-required-providers'; +import {AssetAdvancedFormBuilder} from './form/asset-advanced-form-builder'; +import {AssetDatasourceFormBuilder} from './form/asset-datasource-form-builder'; +import {AssetGeneralFormBuilder} from './form/asset-general-form-builder'; +import {EditAssetForm} from './form/edit-asset-form'; +import {EditAssetFormInitializer} from './form/edit-asset-form-initializer'; + +export const editAssetFormRequiredViewProviders = [ + EditAssetFormInitializer, + AssetRequestBuilder, + EditAssetForm, + AssetGeneralFormBuilder, + AssetDatasourceFormBuilder, + AssetAdvancedFormBuilder, + ...policyFormRequiredViewProviders, +]; diff --git a/connector-ui/src/app/shared/business/edit-asset-form/edit-asset-form.component.html b/connector-ui/src/app/shared/business/edit-asset-form/edit-asset-form.component.html new file mode 100644 index 000000000..c381b58a7 --- /dev/null +++ b/connector-ui/src/app/shared/business/edit-asset-form/edit-asset-form.component.html @@ -0,0 +1,996 @@ +
+
+ + +
+ + + + {{ 'create_data_offer_page.unchanged' | translate }} + + + {{ 'create_data_offer_page.readily_available' | translate }} + + + {{ 'create_data_offer_page.on_request' | translate }} + + +
+ + + + + + + + + + + + +
+ + +
+ + + + + + + +
+ + + + {{ method }} + + + + {{ + 'create_data_offer_page.custom_http_method_hint' | translate + }} + + + +
+ +
+
+ + +
+ {{ + 'create_data_offer_page.custom_http_subpath_hint' | translate + }} + + + +
+ +
+
+ +
+ +
+
+ + + {{ 'general.name' | translate }} + + {{ header.errors }} + {{ validationMessages.invalidQueryParam }} + + + + + {{ 'general.value' | translate }} + + {{ validationMessages.invalidQueryParam }} + + + + +
+ + {{ + 'create_data_offer_page.query_param_enabled_hint' | translate + }} + +
+ + + + +
+
+ +
+ + + {{ 'asset_list_page.info_body' | translate }} + + +
+ +
+
+ +
+ + +
+ +
+ + + {{ 'general.type' | translate }} + + + {{ + 'create_data_offer_page.header_with_vault_secret' + | translate + }} + + {{ + 'create_data_offer_page.header_with_value' | translate + }} + + +
+ + + {{ + 'create_data_offer_page.auth_header_name' | translate + }} + + + + + {{ + 'create_data_offer_page.auth_header_value' | translate + }} + + + + + {{ + 'create_data_offer_page.vault_secret_name' | translate + }} + + +
+ +
+ +
+
+ +
+ +
+
+ + + {{ + 'create_data_offer_page.header_name' | translate + }} + + + + + {{ + 'create_data_offer_page.header_value' | translate + }} + + + + +
+ +
+ +
+
+
+
+
+ + + + + + + + + + +
+ {{ 'create_data_offer_page.description_uses' | translate }} + Markdown syntax +
+
+ + +
+ +
+ + + {{ + 'create_data_offer_page.show_advanced_fields' | translate + }} + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ + URL + + + +
+
+ +
+
+ +
+ + +
+
+ + URL + + + +
+
+ +
+
+ + + +
+ {{ + 'create_data_offer_page.reference_files_description' | translate + }} + Markdown syntax +
+
+
+
+ + + +
+ + + + + + + {{ 'create_data_offer_page.temporal_coverage_hint' | translate }} + + + + {{ validationMessages.invalidDateRangeMessage }} + + +
+ + + + + + + + + + + +
+ + +
+
+ + {{ + 'create_data_offer_page.nuts_location' | translate + }} + + + +
+
+ +
+
+
+ + + + + + + + + + + + + +
+ {{ + 'create_data_offer_page.conditions_for_use_description_hint' + | translate + }} + Markdown syntax +
+
+
+ + + +
+ + + + {{ 'create_data_offer_page.publish_unrestricted' | translate }} + + + + {{ 'create_data_offer_page.publish_restricted' | translate }} + + + + {{ 'create_data_offer_page.publish_asset_only' | translate }} + + + +
+ + +
+ + +
+
+ + +
+
diff --git a/connector-ui/src/app/shared/business/edit-asset-form/edit-asset-form.component.ts b/connector-ui/src/app/shared/business/edit-asset-form/edit-asset-form.component.ts new file mode 100644 index 000000000..d8aa7c3ac --- /dev/null +++ b/connector-ui/src/app/shared/business/edit-asset-form/edit-asset-form.component.ts @@ -0,0 +1,28 @@ +import {Component, EventEmitter, Input, Output} from '@angular/core'; +import {ActiveFeatureSet} from 'src/app/core/config/active-feature-set'; +import {ValidationMessages} from 'src/app/core/validators/validation-messages'; +import {ExpressionFormHandler} from '../policy-editor/editor/expression-form-handler'; +import {EditAssetForm} from './form/edit-asset-form'; +import {DATA_SOURCE_HTTP_METHODS} from './form/http-methods'; + +@Component({ + selector: 'edit-asset-form', + templateUrl: './edit-asset-form.component.html', +}) +export class EditAssetFormComponent { + @Input() isLoading!: boolean; + @Output() submitClicked = new EventEmitter(); + + methods = DATA_SOURCE_HTTP_METHODS; + + constructor( + public form: EditAssetForm, + public validationMessages: ValidationMessages, + public expressionFormHandler: ExpressionFormHandler, + public activeFeatureSet: ActiveFeatureSet, + ) {} + + multipleDataSourceOptionsAvailable(): boolean { + return this.form.mode === 'EDIT' || this.activeFeatureSet.hasMdsFields(); + } +} diff --git a/connector-ui/src/app/shared/business/edit-asset-form/form/asset-advanced-form-builder.ts b/connector-ui/src/app/shared/business/edit-asset-form/form/asset-advanced-form-builder.ts new file mode 100644 index 000000000..9fffeccb7 --- /dev/null +++ b/connector-ui/src/app/shared/business/edit-asset-form/form/asset-advanced-form-builder.ts @@ -0,0 +1,55 @@ +import {Injectable} from '@angular/core'; +import {FormBuilder, FormControl, FormGroup, Validators} from '@angular/forms'; +import {urlValidator} from 'src/app/core/validators/url-validator'; +import {validOptionalDateRange} from 'src/app/core/validators/valid-optional-date-range'; +import { + AssetAdvancedFormModel, + AssetAdvancedFormValue, +} from './model/asset-advanced-form-model'; + +@Injectable() +export class AssetAdvancedFormBuilder { + constructor(private formBuilder: FormBuilder) {} + + buildFormGroup( + initial: AssetAdvancedFormValue, + ): FormGroup { + return this.formBuilder.nonNullable.group({ + dataModel: initial?.dataModel!, + transportMode: initial?.transportMode || null, + geoReferenceMethod: initial?.geoReferenceMethod!, + sovereignLegalName: initial?.sovereignLegalName!, + geoLocation: initial?.geoLocation!, + nutsLocations: this.formBuilder.nonNullable.array( + initial?.nutsLocations?.map((x) => this.buildRequiredString(x)) ?? [], + ), + dataSampleUrls: this.formBuilder.array( + initial?.dataSampleUrls?.map((x) => this.buildRequiredUrl(x)) ?? [], + ), + referenceFileUrls: this.formBuilder.nonNullable.array( + initial?.referenceFileUrls?.map((x) => this.buildRequiredUrl(x)) ?? [], + ), + referenceFilesDescription: initial?.referenceFilesDescription!, + conditionsForUse: initial?.conditionsForUse!, + dataUpdateFrequency: initial?.dataUpdateFrequency!, + temporalCoverage: this.formBuilder.group( + { + from: initial?.temporalCoverage?.from || null, + toInclusive: initial?.temporalCoverage?.toInclusive || null, + }, + {validators: validOptionalDateRange}, + ), + }); + } + + buildRequiredString(initial: string): FormControl { + return this.formBuilder.nonNullable.control(initial, Validators.required); + } + + buildRequiredUrl(initial: string): FormControl { + return this.formBuilder.nonNullable.control(initial, [ + Validators.required, + urlValidator, + ]); + } +} diff --git a/connector-ui/src/app/shared/business/edit-asset-form/form/asset-datasource-form-builder.ts b/connector-ui/src/app/shared/business/edit-asset-form/form/asset-datasource-form-builder.ts new file mode 100644 index 000000000..e3d871fc7 --- /dev/null +++ b/connector-ui/src/app/shared/business/edit-asset-form/form/asset-datasource-form-builder.ts @@ -0,0 +1,107 @@ +import {Injectable} from '@angular/core'; +import {FormBuilder, FormGroup, Validators} from '@angular/forms'; +import {switchDisabledControls} from 'src/app/core/utils/form-group-utils'; +import {jsonValidator} from 'src/app/core/validators/json-validator'; +import {urlValidator} from 'src/app/core/validators/url-validator'; +import {validQueryParam} from 'src/app/core/validators/valid-query-param'; +import {assetDatasourceFormEnabledCtrls} from './model/asset-datasource-form-enabled-ctrls'; +import { + AssetDatasourceFormModel, + AssetDatasourceFormValue, +} from './model/asset-datasource-form-model'; +import { + HttpDatasourceHeaderFormModel, + HttpDatasourceHeaderFormValue, +} from './model/http-datasource-header-form-model'; +import { + HttpDatasourceQueryParamFormModel, + HttpDatasourceQueryParamFormValue, +} from './model/http-datasource-query-param-form-model'; + +@Injectable() +export class AssetDatasourceFormBuilder { + constructor(private formBuilder: FormBuilder) {} + + buildFormGroup( + initial: AssetDatasourceFormValue, + ): FormGroup { + const datasource: FormGroup = + this.formBuilder.nonNullable.group({ + dataSourceAvailability: initial?.dataSourceAvailability!, + + dataAddressType: initial?.dataAddressType!, + dataDestination: [ + initial?.dataDestination!, + [Validators.required, jsonValidator], + ], + + // On-Request + contactEmail: [ + initial?.contactEmail!, + [Validators.required, Validators.email], + ], + contactPreferredEmailSubject: [ + initial?.contactPreferredEmailSubject!, + Validators.required, + ], + + // Http Datasource Fields + httpUrl: [initial?.httpUrl!, [Validators.required, urlValidator]], + httpMethod: [initial?.httpMethod!, Validators.required], + + httpAuthHeaderType: [initial?.httpAuthHeaderType!], + httpAuthHeaderName: [initial?.httpAuthHeaderName!, Validators.required], + httpAuthHeaderValue: [ + initial?.httpAuthHeaderValue!, + Validators.required, + ], + httpAuthHeaderSecretName: [ + initial?.httpAuthHeaderSecretName!, + Validators.required, + ], + httpQueryParams: this.formBuilder.array( + initial?.httpQueryParams?.map( + (param: HttpDatasourceQueryParamFormValue) => + this.buildQueryParamFormGroup(param), + ) ?? [], + ), + + httpDefaultPath: [initial?.httpDefaultPath!], + httpProxyMethod: [initial?.httpProxyMethod!], + httpProxyPath: [initial?.httpProxyPath!], + httpProxyQueryParams: [initial?.httpProxyQueryParams!], + httpProxyBody: [initial?.httpProxyBody!], + + httpHeaders: this.formBuilder.array( + initial?.httpHeaders?.map((header: HttpDatasourceHeaderFormValue) => + this.buildHeaderFormGroup(header), + ) ?? [], + ), + }); + + switchDisabledControls( + datasource, + assetDatasourceFormEnabledCtrls, + ); + + return datasource; + } + + buildHeaderFormGroup( + initial: HttpDatasourceHeaderFormValue, + ): FormGroup { + return this.formBuilder.nonNullable.group({ + headerName: [initial.headerName!, Validators.required], + headerValue: [initial.headerValue!, Validators.required], + }); + } + + buildQueryParamFormGroup( + initial: HttpDatasourceQueryParamFormValue, + ): FormGroup { + return this.formBuilder.nonNullable.group({ + paramName: [initial.paramName!, [Validators.required, validQueryParam]], + paramValue: [initial.paramValue!, [validQueryParam]], + }); + } +} diff --git a/connector-ui/src/app/shared/business/edit-asset-form/form/asset-general-form-builder.ts b/connector-ui/src/app/shared/business/edit-asset-form/form/asset-general-form-builder.ts new file mode 100644 index 000000000..3eaf5c591 --- /dev/null +++ b/connector-ui/src/app/shared/business/edit-asset-form/form/asset-general-form-builder.ts @@ -0,0 +1,101 @@ +import {Injectable} from '@angular/core'; +import {FormBuilder, FormControl, FormGroup, Validators} from '@angular/forms'; +import {combineLatest, distinctUntilChanged, pairwise} from 'rxjs'; +import {map} from 'rxjs/operators'; +import {ActiveFeatureSet} from 'src/app/core/config/active-feature-set'; +import {value$} from 'src/app/core/utils/form-group-utils'; +import {noWhitespacesOrColonsValidator} from 'src/app/core/validators/no-whitespaces-or-colons-validator'; +import {EditAssetFormValidators} from './edit-asset-form-validators'; +import {AssetEditDialogMode} from './model/asset-edit-dialog-mode'; +import { + AssetGeneralFormModel, + AssetGeneralFormValue, +} from './model/asset-general-form-model'; + +@Injectable() +export class AssetGeneralFormBuilder { + constructor( + private formBuilder: FormBuilder, + private activeFeatureSet: ActiveFeatureSet, + private editAssetFormValidators: EditAssetFormValidators, + ) {} + + buildFormGroup( + initial: AssetGeneralFormValue, + mode: AssetEditDialogMode, + ): FormGroup { + const general: FormGroup = + this.formBuilder.nonNullable.group({ + id: [ + initial.id!, + [Validators.required, noWhitespacesOrColonsValidator], + this.editAssetFormValidators.isValidId(), + ], + name: [initial.name!, Validators.required], + description: [initial.description!], + keywords: [initial.keywords!], + showAdvancedFields: [initial.showAdvancedFields || false], + version: [initial.version!], + contentType: [initial.contentType!], + language: [initial.language || null], + publisher: [initial.publisher!], + standardLicense: [initial.standardLicense!], + endpointDocumentation: [initial.endpointDocumentation!], + }); + + if (this.activeFeatureSet.hasMdsFields()) { + general.addControl( + 'dataCategory', + this.formBuilder.control( + initial.dataCategory || null, + Validators.required, + ), + ); + general.addControl( + 'dataSubcategory', + this.formBuilder.control(initial.dataSubcategory || null), + ); + } + + if (mode === 'CREATE') { + this.initIdGeneration(general.controls.id, general.controls.name); + } else { + general.controls.id.disable(); + } + + return general; + } + + private initIdGeneration( + idCtrl: FormControl, + nameCtrl: FormControl, + ) { + combineLatest([value$(nameCtrl).pipe(distinctUntilChanged())]) + .pipe( + map(([title]) => this.generateId(title)), + pairwise(), + ) + .subscribe(([previousId, currentId]) => { + if (!idCtrl.value || idCtrl.value === previousId) { + idCtrl.setValue(currentId); + idCtrl.markAsTouched(); + idCtrl.updateValueAndValidity(); + } + }); + } + + private generateId(name: string | null) { + if (!name) { + return ''; + } + return this.cleanIdComponent(name); + } + + private cleanIdComponent(s: string | null) { + return (s ?? '') + .trim() + .replace(':', '-') + .replaceAll(' ', '-') + .toLowerCase(); + } +} diff --git a/connector-ui/src/app/shared/business/edit-asset-form/form/edit-asset-form-initializer.ts b/connector-ui/src/app/shared/business/edit-asset-form/form/edit-asset-form-initializer.ts new file mode 100644 index 000000000..1aafcfa42 --- /dev/null +++ b/connector-ui/src/app/shared/business/edit-asset-form/form/edit-asset-form-initializer.ts @@ -0,0 +1,133 @@ +import {Injectable} from '@angular/core'; +import {ActiveFeatureSet} from 'src/app/core/config/active-feature-set'; +import {UiAssetMapped} from 'src/app/core/services/models/ui-asset-mapped'; +import {LanguageSelectItemService} from '../../../form-elements/language-select/language-select-item.service'; +import {AssetDatasourceFormValue} from './model/asset-datasource-form-model'; +import {EditAssetFormValue} from './model/edit-asset-form-model'; + +/** + * Handles AngularForms for Edit Asset Form + */ +@Injectable() +export class EditAssetFormInitializer { + constructor( + private languageSelectItemService: LanguageSelectItemService, + private activeFeatureSet: ActiveFeatureSet, + ) {} + + forCreate(): EditAssetFormValue { + return { + mode: 'CREATE', + publishMode: 'PUBLISH_UNRESTRICTED', + general: { + id: '', + name: '', + description: '', + keywords: [], + dataCategory: null, + dataSubcategory: null, + version: '', + contentType: '', + language: this.languageSelectItemService.english(), + publisher: '', + standardLicense: '', + endpointDocumentation: '', + showAdvancedFields: false, + }, + advanced: { + dataModel: '', + transportMode: null, + geoReferenceMethod: '', + conditionsForUse: '', + dataUpdateFrequency: '', + sovereignLegalName: '', + geoLocation: '', + nutsLocations: [], + dataSampleUrls: [], + referenceFileUrls: [], + referenceFilesDescription: '', + temporalCoverage: {from: null, toInclusive: null}, + }, + datasource: this.emptyHttpDatasource(), + }; + } + + forEdit(asset: UiAssetMapped): EditAssetFormValue { + return { + mode: 'EDIT', + publishMode: 'DO_NOT_PUBLISH', + general: { + id: asset.assetId, + name: asset.title, + description: asset.description, + keywords: asset.keywords, + dataCategory: asset.dataCategory, + dataSubcategory: asset.dataSubcategory, + version: asset.version, + contentType: asset.mediaType, + language: asset.language, + publisher: asset.publisherHomepage, + standardLicense: asset.licenseUrl, + endpointDocumentation: asset.landingPageUrl, + showAdvancedFields: true, + }, + advanced: { + dataModel: asset.dataModel, + transportMode: asset.transportMode, + geoReferenceMethod: asset.geoReferenceMethod, + sovereignLegalName: asset.sovereignLegalName, + geoLocation: asset.geoLocation, + nutsLocations: asset.nutsLocations, + dataSampleUrls: asset.dataSampleUrls, + referenceFileUrls: asset.referenceFileUrls, + referenceFilesDescription: asset.referenceFilesDescription, + conditionsForUse: asset.conditionsForUse, + dataUpdateFrequency: asset.dataUpdateFrequency, + temporalCoverage: { + from: asset.temporalCoverageFrom, + toInclusive: asset.temporalCoverageToInclusive, + }, + }, + datasource: this.emptyEditDatasource(asset), + }; + } + + private emptyHttpDatasource(): AssetDatasourceFormValue { + return { + dataSourceAvailability: this.activeFeatureSet.hasMdsFields() + ? 'On-Request' + : 'Datasource', + contactEmail: '', + contactPreferredEmailSubject: '', + + dataAddressType: 'Http', + dataDestination: '', + + httpUrl: '', + httpMethod: 'GET', + httpAuthHeaderType: 'None', + httpAuthHeaderName: '', + httpAuthHeaderValue: '', + httpAuthHeaderSecretName: '', + httpQueryParams: [], + + httpDefaultPath: '', + httpProxyMethod: false, + httpProxyPath: false, + httpProxyQueryParams: false, + httpProxyBody: false, + + httpHeaders: [], + }; + } + + private emptyEditDatasource(asset: UiAssetMapped): AssetDatasourceFormValue { + return { + ...this.emptyHttpDatasource(), + dataSourceAvailability: + asset.dataSourceAvailability === 'LIVE' ? 'Unchanged' : 'On-Request', + contactEmail: asset.onRequestContactEmail ?? '', + contactPreferredEmailSubject: asset.onRequestContactEmailSubject ?? '', + }; + } +} diff --git a/connector-ui/src/app/shared/business/edit-asset-form/form/edit-asset-form-validators.ts b/connector-ui/src/app/shared/business/edit-asset-form/form/edit-asset-form-validators.ts new file mode 100644 index 000000000..68861daae --- /dev/null +++ b/connector-ui/src/app/shared/business/edit-asset-form/form/edit-asset-form-validators.ts @@ -0,0 +1,86 @@ +import {Injectable} from '@angular/core'; +import { + AbstractControl, + AsyncValidatorFn, + ValidationErrors, +} from '@angular/forms'; +import {Observable, combineLatest, of} from 'rxjs'; +import {catchError, map} from 'rxjs/operators'; +import {IdAvailabilityResponse} from '@sovity.de/edc-client'; +import {EdcApiService} from 'src/app/core/services/api/edc-api.service'; +import {EditAssetFormValue} from './model/edit-asset-form-model'; + +/** + * Handles AngularForms for Edit Asset Form + */ +@Injectable({providedIn: 'root'}) +export class EditAssetFormValidators { + constructor(private edcApiService: EdcApiService) {} + + /** + * Use on asset control, reset asset control on publish mode changes, accesses parent form + */ + isValidId(): AsyncValidatorFn { + return (control: AbstractControl): Observable => { + const value = control?.parent?.parent?.value as EditAssetFormValue | null; + if (value?.mode !== 'CREATE') { + return of(null); + } + + const assetId = control.value! as string; + if (value.publishMode !== 'DO_NOT_PUBLISH') { + return combineLatest([ + this.assetIdExistsErrorMessage(assetId), + this.contractDefinitionIdErrorMessage(assetId), + this.policyIdExistsErrorMessage(assetId), + ]).pipe( + map((errorMessages) => this.buildValidationErrors(errorMessages)), + ); + } else { + return this.assetIdExistsErrorMessage(assetId).pipe( + map((result) => this.buildValidationErrors([result])), + ); + } + return of(null); + }; + } + + private assetIdExistsErrorMessage(id: string): Observable { + return this.edcApiService.isAssetIdAvailable(id).pipe( + catchError(() => of({id, available: false})), + map((it) => (it.available ? null : 'Asset already exists.')), + ); + } + + private contractDefinitionIdErrorMessage( + id: string, + ): Observable { + return this.edcApiService.isContractDefinitionIdAvailable(id).pipe( + catchError(() => of({id, available: false})), + map((it) => + it.available ? null : 'Contract Definition already exists.', + ), + ); + } + + private policyIdExistsErrorMessage(id: string): Observable { + return this.edcApiService.isPolicyIdAvailable(id).pipe( + catchError(() => of({id, available: false})), + map((it) => (it.available ? null : 'Policy already exists.')), + ); + } + + private buildValidationErrors( + errorMessages: (string | null)[], + ): ValidationErrors | null { + const errors = errorMessages.filter((it) => it); + if (!errors.length) { + return null; + } + + const message = + errors.length === 3 ? 'Data Offer already exists.' : errors.join(' '); + + return {exists: message}; + } +} diff --git a/connector-ui/src/app/shared/business/edit-asset-form/form/edit-asset-form.ts b/connector-ui/src/app/shared/business/edit-asset-form/form/edit-asset-form.ts new file mode 100644 index 000000000..8500040b8 --- /dev/null +++ b/connector-ui/src/app/shared/business/edit-asset-form/form/edit-asset-form.ts @@ -0,0 +1,186 @@ +import {Injectable} from '@angular/core'; +import {FormBuilder, FormGroup} from '@angular/forms'; +import {delay} from 'rxjs/operators'; +import {ActiveFeatureSet} from 'src/app/core/config/active-feature-set'; +import {switchDisabledControls} from 'src/app/core/utils/form-group-utils'; +import {DataCategorySelectItem} from '../../../form-elements/data-category-select/data-category-select-item'; +import {ExpressionFormControls} from '../../policy-editor/editor/expression-form-controls'; +import {AssetAdvancedFormBuilder} from './asset-advanced-form-builder'; +import {AssetDatasourceFormBuilder} from './asset-datasource-form-builder'; +import {AssetGeneralFormBuilder} from './asset-general-form-builder'; +import {AssetAdvancedFormModel} from './model/asset-advanced-form-model'; +import {AssetDatasourceFormModel} from './model/asset-datasource-form-model'; +import {AssetEditDialogMode} from './model/asset-edit-dialog-mode'; +import {AssetGeneralFormModel} from './model/asset-general-form-model'; +import {DataAddress} from './model/data-address'; +import {DataOfferPublishMode} from './model/data-offer-publish-mode'; +import { + EditAssetFormModel, + EditAssetFormValue, +} from './model/edit-asset-form-model'; + +/** + * Handles AngularForms for Edit Asset Form + */ +@Injectable() +export class EditAssetForm { + all!: FormGroup; + + general!: EditAssetFormModel['general']; + + datasource!: EditAssetFormModel['datasource']; + + advanced!: EditAssetFormModel['advanced']; + + get value(): EditAssetFormValue { + return this.all.value; + } + + get mode(): AssetEditDialogMode { + return this.all.controls.mode.value; + } + + get dataAddressType(): DataAddress | null { + return this.datasource!.controls.dataAddressType.value; + } + + get dataCategory(): DataCategorySelectItem | null { + return this.general.controls.dataCategory!.value; + } + + get proxyMethod(): boolean { + return this.datasource!.controls.httpProxyMethod.value; + } + + get proxyPath(): boolean { + return this.datasource!.controls.httpProxyPath.value; + } + + get proxyQueryParams(): boolean { + return this.datasource!.controls.httpProxyQueryParams.value; + } + + constructor( + private formBuilder: FormBuilder, + private assetGeneralFormBuilder: AssetGeneralFormBuilder, + private assetDatasourceFormBuilder: AssetDatasourceFormBuilder, + private assetAdvancedFormBuilder: AssetAdvancedFormBuilder, + private activeFeatureSet: ActiveFeatureSet, + private expressionFormControls: ExpressionFormControls, + ) {} + + reset(initial: EditAssetFormValue) { + this.all = this.buildFormGroup(initial); + this.general = this.all.controls.general; + this.datasource = this.all.controls.datasource; + this.advanced = this.all.controls.advanced; + } + + buildFormGroup(initial: EditAssetFormValue): FormGroup { + const general: FormGroup = + this.assetGeneralFormBuilder.buildFormGroup( + initial.general!, + initial.mode!, + ); + + const datasource: FormGroup = + this.assetDatasourceFormBuilder.buildFormGroup(initial.datasource!); + + const formGroup: FormGroup = + this.formBuilder.nonNullable.group({ + mode: [initial.mode as AssetEditDialogMode], + publishMode: [initial.publishMode as DataOfferPublishMode], + policyControls: this.expressionFormControls.formGroup, + general, + datasource, + }); + + formGroup.controls.publishMode.valueChanges + .pipe(delay(0)) + .subscribe(() => general.controls.id.updateValueAndValidity()); + + if (this.activeFeatureSet.hasMdsFields()) { + const advanced: FormGroup = + this.assetAdvancedFormBuilder.buildFormGroup(initial.advanced!); + formGroup.addControl('advanced', advanced); + } + + switchDisabledControls(formGroup, (value) => ({ + policyControls: value.publishMode === 'PUBLISH_RESTRICTED', + mode: true, + publishMode: true, + advanced: true, + general: true, + datasource: true, + })); + + return formGroup; + } + + onHttpHeadersAddClick(buttonClickedEvent: Event) { + buttonClickedEvent.preventDefault(); + this.datasource!.controls.httpHeaders.push( + this.assetDatasourceFormBuilder.buildHeaderFormGroup({ + headerName: '', + headerValue: '', + }), + ); + } + + onHttpHeadersRemoveClick(buttonClickedEvent: Event, index: number) { + buttonClickedEvent.preventDefault(); + this.datasource!.controls.httpHeaders.removeAt(index); + } + + onHttpQueryParamsAddClick(buttonClickedEvent: Event) { + buttonClickedEvent.preventDefault(); + this.datasource!.controls.httpQueryParams.push( + this.assetDatasourceFormBuilder.buildQueryParamFormGroup({ + paramName: '', + paramValue: '', + }), + ); + } + + // markAllAsTouched added as a workaround to get labels outside of mat-form-field to show invalid state correctly + onHttpQueryParamsRemoveClick(buttonClickedEvent: Event, index: number) { + buttonClickedEvent.preventDefault(); + this.datasource!.controls.httpQueryParams.removeAt(index); + } + + onNutsLocationsAddClick(buttonClickedEvent: Event) { + buttonClickedEvent.preventDefault(); + this.advanced!.controls.nutsLocations.push( + this.assetAdvancedFormBuilder.buildRequiredString(''), + ); + } + + onNutsLocationsRemoveClick(buttonClickedEvent: Event, index: number) { + buttonClickedEvent.preventDefault(); + this.advanced!.controls.nutsLocations.removeAt(index); + } + + onDataSampleUrlsAddClick(buttonClickedEvent: Event) { + buttonClickedEvent.preventDefault(); + this.advanced!.controls.dataSampleUrls.push( + this.assetAdvancedFormBuilder.buildRequiredUrl(''), + ); + } + + onDataSampleUrlsRemoveClick(buttonClickedEvent: Event, index: number) { + buttonClickedEvent.preventDefault(); + this.advanced!.controls.dataSampleUrls.removeAt(index); + } + + onReferenceFileUrlsAddClick(buttonClickedEvent: Event) { + buttonClickedEvent.preventDefault(); + this.advanced!.controls.referenceFileUrls.push( + this.assetAdvancedFormBuilder.buildRequiredUrl(''), + ); + } + + onReferenceFileUrlsRemoveClick(buttonClickedEvent: Event, index: number) { + buttonClickedEvent.preventDefault(); + this.advanced!.controls.referenceFileUrls.removeAt(index); + } +} diff --git a/connector-ui/src/app/shared/business/edit-asset-form/form/http-methods.ts b/connector-ui/src/app/shared/business/edit-asset-form/form/http-methods.ts new file mode 100644 index 000000000..ac48610ce --- /dev/null +++ b/connector-ui/src/app/shared/business/edit-asset-form/form/http-methods.ts @@ -0,0 +1,11 @@ +export const DATA_SOURCE_HTTP_METHODS = [ + 'GET', + 'POST', + 'PUT', + 'PATCH', + 'DELETE', + 'OPTIONS', +]; +export const DATA_SINK_HTTP_METHODS = DATA_SOURCE_HTTP_METHODS.filter( + (it) => it !== 'GET', +); diff --git a/connector-ui/src/app/shared/business/edit-asset-form/form/model/always-true-policy-id.ts b/connector-ui/src/app/shared/business/edit-asset-form/form/model/always-true-policy-id.ts new file mode 100644 index 000000000..8a082e831 --- /dev/null +++ b/connector-ui/src/app/shared/business/edit-asset-form/form/model/always-true-policy-id.ts @@ -0,0 +1 @@ +export const ALWAYS_TRUE_POLICY_ID = 'always-true'; diff --git a/connector-ui/src/app/shared/business/edit-asset-form/form/model/asset-advanced-form-model.ts b/connector-ui/src/app/shared/business/edit-asset-form/form/model/asset-advanced-form-model.ts new file mode 100644 index 000000000..af1e551fe --- /dev/null +++ b/connector-ui/src/app/shared/business/edit-asset-form/form/model/asset-advanced-form-model.ts @@ -0,0 +1,32 @@ +import { + FormArray, + FormControl, + FormGroup, + ɵFormGroupValue, +} from '@angular/forms'; +import {TransportModeSelectItem} from '../../../../form-elements/transport-mode-select/transport-mode-select-item'; +import {TemporalCoverageFormModel} from './temporal-coverage-form-model'; + +/** + * Form Model for Edit Asset Form > Advanced + * (MDS Properties) + */ +export interface AssetAdvancedFormModel { + dataModel: FormControl; + geoReferenceMethod: FormControl; + transportMode: FormControl; + sovereignLegalName: FormControl; + geoLocation: FormControl; + nutsLocations: FormArray>; + dataSampleUrls: FormArray>; + referenceFileUrls: FormArray>; + referenceFilesDescription: FormControl; + conditionsForUse: FormControl; + dataUpdateFrequency: FormControl; + temporalCoverage: FormGroup; +} + +/** + * Form Value for Edit Asset Form > Advanced + */ +export type AssetAdvancedFormValue = ɵFormGroupValue; diff --git a/connector-ui/src/app/shared/business/edit-asset-form/form/model/asset-datasource-form-enabled-ctrls.ts b/connector-ui/src/app/shared/business/edit-asset-form/form/model/asset-datasource-form-enabled-ctrls.ts new file mode 100644 index 000000000..7f347421f --- /dev/null +++ b/connector-ui/src/app/shared/business/edit-asset-form/form/model/asset-datasource-form-enabled-ctrls.ts @@ -0,0 +1,51 @@ +import { + AssetDatasourceFormModel, + AssetDatasourceFormValue, +} from './asset-datasource-form-model'; + +export const assetDatasourceFormEnabledCtrls = ( + value: AssetDatasourceFormValue, +): Record => { + const customDataAddressJson = + value.dataAddressType === 'Custom-Data-Address-Json'; + + const onRequest = value.dataSourceAvailability === 'On-Request'; + const datasource = value.dataSourceAvailability === 'Datasource'; + + const http = value.dataAddressType === 'Http' && datasource; + const httpAuth = value.httpAuthHeaderType !== 'None'; + const httpAuthByValue = value.httpAuthHeaderType === 'Value'; + const httpAuthByVault = value.httpAuthHeaderType === 'Vault-Secret'; + const proxyPath = !!value.httpProxyPath; + + return { + dataSourceAvailability: true, + + // On Request Datasource + contactEmail: onRequest, + contactPreferredEmailSubject: onRequest, + + dataAddressType: datasource, + + // Custom Datasource JSON + dataDestination: datasource && customDataAddressJson, + + // Http Datasource Fields + httpUrl: http, + httpMethod: http && !value.httpProxyMethod, + + httpAuthHeaderType: http, + httpAuthHeaderName: http && httpAuth, + httpAuthHeaderValue: http && httpAuthByValue, + httpAuthHeaderSecretName: http && httpAuthByVault, + httpQueryParams: http, + + httpDefaultPath: http && proxyPath, + httpProxyMethod: http, + httpProxyPath: http, + httpProxyQueryParams: http, + httpProxyBody: http, + + httpHeaders: http, + }; +}; diff --git a/connector-ui/src/app/shared/business/edit-asset-form/form/model/asset-datasource-form-model.ts b/connector-ui/src/app/shared/business/edit-asset-form/form/model/asset-datasource-form-model.ts new file mode 100644 index 000000000..3da71b80f --- /dev/null +++ b/connector-ui/src/app/shared/business/edit-asset-form/form/model/asset-datasource-form-model.ts @@ -0,0 +1,50 @@ +import { + FormArray, + FormControl, + FormGroup, + ɵFormGroupValue, +} from '@angular/forms'; +import {UiDataSourceHttpDataMethod} from '@sovity.de/edc-client'; +import {DataAddress} from './data-address'; +import {DatasourceType} from './datasource-type'; +import {HttpDatasourceAuthHeaderType} from './http-datasource-auth-header-type'; +import {HttpDatasourceHeaderFormModel} from './http-datasource-header-form-model'; +import {HttpDatasourceQueryParamFormModel} from './http-datasource-query-param-form-model'; + +/** + * Form Model for Edit Asset Form > Datasource + */ +export interface AssetDatasourceFormModel { + dataSourceAvailability: FormControl; + + dataAddressType: FormControl; + + // Custom Datasource JSON + dataDestination: FormControl; + + // On-Request Datasource + contactEmail: FormControl; + contactPreferredEmailSubject: FormControl; + + // Http Datasource + httpUrl: FormControl; + httpMethod: FormControl; + + httpAuthHeaderType: FormControl; + httpAuthHeaderName: FormControl; + httpAuthHeaderValue: FormControl; + httpAuthHeaderSecretName: FormControl; + httpHeaders: FormArray>; + httpQueryParams: FormArray>; + httpProxyMethod: FormControl; + httpProxyPath: FormControl; + httpProxyQueryParams: FormControl; + httpProxyBody: FormControl; + httpDefaultPath: FormControl; +} + +/** + * Form Value for Edit Asset Form > Datasource + */ +export type AssetDatasourceFormValue = + ɵFormGroupValue; diff --git a/connector-ui/src/app/shared/business/edit-asset-form/form/model/asset-edit-dialog-mode.ts b/connector-ui/src/app/shared/business/edit-asset-form/form/model/asset-edit-dialog-mode.ts new file mode 100644 index 000000000..829ea52c7 --- /dev/null +++ b/connector-ui/src/app/shared/business/edit-asset-form/form/model/asset-edit-dialog-mode.ts @@ -0,0 +1 @@ +export type AssetEditDialogMode = 'CREATE' | 'EDIT'; diff --git a/connector-ui/src/app/shared/business/edit-asset-form/form/model/asset-general-form-model.ts b/connector-ui/src/app/shared/business/edit-asset-form/form/model/asset-general-form-model.ts new file mode 100644 index 000000000..d059c8e09 --- /dev/null +++ b/connector-ui/src/app/shared/business/edit-asset-form/form/model/asset-general-form-model.ts @@ -0,0 +1,28 @@ +import {FormControl, ɵFormGroupValue} from '@angular/forms'; +import {DataCategorySelectItem} from '../../../../form-elements/data-category-select/data-category-select-item'; +import {DataSubcategorySelectItem} from '../../../../form-elements/data-subcategory-select/data-subcategory-select-item'; +import {LanguageSelectItem} from '../../../../form-elements/language-select/language-select-item'; + +/** + * Form Model for Edit Asset Form > General + */ +export interface AssetGeneralFormModel { + id: FormControl; + name: FormControl; + description: FormControl; + keywords: FormControl; + dataCategory?: FormControl; + dataSubcategory?: FormControl; + showAdvancedFields: FormControl; + version: FormControl; + contentType: FormControl; + language: FormControl; + publisher: FormControl; + standardLicense: FormControl; + endpointDocumentation: FormControl; +} + +/** + * Form Value for Edit Asset Form > General + */ +export type AssetGeneralFormValue = ɵFormGroupValue; diff --git a/connector-ui/src/app/shared/business/edit-asset-form/form/model/data-address.ts b/connector-ui/src/app/shared/business/edit-asset-form/form/model/data-address.ts new file mode 100644 index 000000000..5186bd121 --- /dev/null +++ b/connector-ui/src/app/shared/business/edit-asset-form/form/model/data-address.ts @@ -0,0 +1,4 @@ +export type DataAddress = + | 'Custom-Data-Address-Json' + | 'Custom-Transfer-Process-Request' + | 'Http'; diff --git a/connector-ui/src/app/shared/business/edit-asset-form/form/model/data-offer-publish-mode.ts b/connector-ui/src/app/shared/business/edit-asset-form/form/model/data-offer-publish-mode.ts new file mode 100644 index 000000000..229aa7d2c --- /dev/null +++ b/connector-ui/src/app/shared/business/edit-asset-form/form/model/data-offer-publish-mode.ts @@ -0,0 +1,4 @@ +export type DataOfferPublishMode = + | 'DO_NOT_PUBLISH' + | 'PUBLISH_UNRESTRICTED' + | 'PUBLISH_RESTRICTED'; diff --git a/connector-ui/src/app/shared/business/edit-asset-form/form/model/datasource-type.ts b/connector-ui/src/app/shared/business/edit-asset-form/form/model/datasource-type.ts new file mode 100644 index 000000000..c738373ca --- /dev/null +++ b/connector-ui/src/app/shared/business/edit-asset-form/form/model/datasource-type.ts @@ -0,0 +1 @@ +export type DatasourceType = 'Datasource' | 'On-Request' | 'Unchanged'; diff --git a/connector-ui/src/app/shared/business/edit-asset-form/form/model/edit-asset-form-model.ts b/connector-ui/src/app/shared/business/edit-asset-form/form/model/edit-asset-form-model.ts new file mode 100644 index 000000000..dff348313 --- /dev/null +++ b/connector-ui/src/app/shared/business/edit-asset-form/form/model/edit-asset-form-model.ts @@ -0,0 +1,28 @@ +import { + FormControl, + FormGroup, + UntypedFormGroup, + ɵFormGroupValue, +} from '@angular/forms'; +import {AssetAdvancedFormModel} from './asset-advanced-form-model'; +import {AssetDatasourceFormModel} from './asset-datasource-form-model'; +import {AssetEditDialogMode} from './asset-edit-dialog-mode'; +import {AssetGeneralFormModel} from './asset-general-form-model'; +import {DataOfferPublishMode} from './data-offer-publish-mode'; + +/** + * Form Model for Edit Asset Form + */ +export interface EditAssetFormModel { + mode: FormControl; + publishMode: FormControl; + policyControls: UntypedFormGroup; + general: FormGroup; + datasource: FormGroup; + advanced?: FormGroup; +} + +/** + * Form Value for Edit Asset Form + */ +export type EditAssetFormValue = ɵFormGroupValue; diff --git a/connector-ui/src/app/shared/business/edit-asset-form/form/model/http-datasource-auth-header-type.ts b/connector-ui/src/app/shared/business/edit-asset-form/form/model/http-datasource-auth-header-type.ts new file mode 100644 index 000000000..c208d6fd0 --- /dev/null +++ b/connector-ui/src/app/shared/business/edit-asset-form/form/model/http-datasource-auth-header-type.ts @@ -0,0 +1 @@ +export type HttpDatasourceAuthHeaderType = 'None' | 'Value' | 'Vault-Secret'; diff --git a/connector-ui/src/app/shared/business/edit-asset-form/form/model/http-datasource-header-form-model.ts b/connector-ui/src/app/shared/business/edit-asset-form/form/model/http-datasource-header-form-model.ts new file mode 100644 index 000000000..17739e929 --- /dev/null +++ b/connector-ui/src/app/shared/business/edit-asset-form/form/model/http-datasource-header-form-model.ts @@ -0,0 +1,15 @@ +import {FormControl, ɵFormGroupValue} from '@angular/forms'; + +/** + * Form Model for Edit Asset Form > Datasource > HTTP/REST > Header + */ +export interface HttpDatasourceHeaderFormModel { + headerName: FormControl; + headerValue: FormControl; +} + +/** + * Form Value for Edit Asset Form > Datasource > HTTP/REST > Header + */ +export type HttpDatasourceHeaderFormValue = + ɵFormGroupValue; diff --git a/connector-ui/src/app/shared/business/edit-asset-form/form/model/http-datasource-query-param-form-model.ts b/connector-ui/src/app/shared/business/edit-asset-form/form/model/http-datasource-query-param-form-model.ts new file mode 100644 index 000000000..747723c5a --- /dev/null +++ b/connector-ui/src/app/shared/business/edit-asset-form/form/model/http-datasource-query-param-form-model.ts @@ -0,0 +1,15 @@ +import {FormControl, ɵFormGroupValue} from '@angular/forms'; + +/** + * Form Model for Edit Asset Form > Datasource > HTTP/REST > Header + */ +export interface HttpDatasourceQueryParamFormModel { + paramName: FormControl; + paramValue: FormControl; +} + +/** + * Form Value for Edit Asset Form > Datasource > HTTP/REST > QueryParam + */ +export type HttpDatasourceQueryParamFormValue = + ɵFormGroupValue; diff --git a/connector-ui/src/app/shared/business/edit-asset-form/form/model/temporal-coverage-form-model.ts b/connector-ui/src/app/shared/business/edit-asset-form/form/model/temporal-coverage-form-model.ts new file mode 100644 index 000000000..f145f9f5e --- /dev/null +++ b/connector-ui/src/app/shared/business/edit-asset-form/form/model/temporal-coverage-form-model.ts @@ -0,0 +1,15 @@ +import {FormControl, ɵFormGroupValue} from '@angular/forms'; + +/** + * Form Model for AssetEditorDialog > Advanced > Temporal Coverage + */ +export interface TemporalCoverageFormModel { + from: FormControl; + toInclusive: FormControl; +} + +/** + * Form Value for AssetEditorDialog > Advanced > Temporal Coverage + */ +export type TemporalCoverageFormValue = + ɵFormGroupValue; diff --git a/connector-ui/src/app/shared/business/initiate-negotiation-confirm-tos-dialog/initiate-negotiation-confirm-tos-dialog.component.html b/connector-ui/src/app/shared/business/initiate-negotiation-confirm-tos-dialog/initiate-negotiation-confirm-tos-dialog.component.html new file mode 100644 index 000000000..9e97890d6 --- /dev/null +++ b/connector-ui/src/app/shared/business/initiate-negotiation-confirm-tos-dialog/initiate-negotiation-confirm-tos-dialog.component.html @@ -0,0 +1,26 @@ +

{{ 'component_library.data_offer' | translate }}

+ +
+

+ {{ 'component_library.accept_licence' | translate }} +

+
+ +
+ + {{ 'component_library.agree' | translate }} + + +
+ + +
+
diff --git a/connector-ui/src/app/shared/business/initiate-negotiation-confirm-tos-dialog/initiate-negotiation-confirm-tos-dialog.component.scss b/connector-ui/src/app/shared/business/initiate-negotiation-confirm-tos-dialog/initiate-negotiation-confirm-tos-dialog.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/connector-ui/src/app/shared/business/initiate-negotiation-confirm-tos-dialog/initiate-negotiation-confirm-tos-dialog.component.ts b/connector-ui/src/app/shared/business/initiate-negotiation-confirm-tos-dialog/initiate-negotiation-confirm-tos-dialog.component.ts new file mode 100644 index 000000000..2fd0195a3 --- /dev/null +++ b/connector-ui/src/app/shared/business/initiate-negotiation-confirm-tos-dialog/initiate-negotiation-confirm-tos-dialog.component.ts @@ -0,0 +1,30 @@ +import {Component} from '@angular/core'; +import {MatCheckboxChange} from '@angular/material/checkbox'; +import {MatDialogRef} from '@angular/material/dialog'; + +@Component({ + selector: 'app-initiate-negotiation-confirm-tos-dialog', + templateUrl: './initiate-negotiation-confirm-tos-dialog.component.html', + styleUrls: ['./initiate-negotiation-confirm-tos-dialog.component.scss'], +}) +export class InitiateNegotiationConfirmTosDialogComponent { + checkboxChecked = false; + + constructor( + public dialogRef: MatDialogRef, + ) {} + + public onCheckboxChange($event: MatCheckboxChange) { + this.checkboxChecked = $event.checked; + } + + onCancel() { + this.dialogRef.close(false); + } + + onConfirm() { + if (this.checkboxChecked) { + this.dialogRef.close(true); + } + } +} diff --git a/connector-ui/src/app/shared/business/policy-editor/editor/expression-form-controls.ts b/connector-ui/src/app/shared/business/policy-editor/editor/expression-form-controls.ts new file mode 100644 index 000000000..99b271199 --- /dev/null +++ b/connector-ui/src/app/shared/business/policy-editor/editor/expression-form-controls.ts @@ -0,0 +1,92 @@ +import {Injectable} from '@angular/core'; +import { + FormControl, + UntypedFormControl, + UntypedFormGroup, +} from '@angular/forms'; +import {OperatorDto, UiPolicyLiteral} from '@sovity.de/edc-client'; +import {PolicyOperatorConfig} from '../model/policy-operators'; +import {TreeNode} from '../model/tree'; +import {ExpressionFormValue} from './expression-form-value'; + +/** + * Manages the FormGroup across the expression tree + * + * Controls are needed for both constraint operators and values + * + * Must be provided at the component level as viewProvider. + */ +@Injectable() +export class ExpressionFormControls { + formGroup = new UntypedFormGroup({}); + + getValue(node: TreeNode): UiPolicyLiteral { + const formValue = this.getValueFormControl(node).value; + return node.value.verb!.adapter.buildValueFn( + formValue, + this.getOperator(node), + ); + } + + getOperator(node: TreeNode): PolicyOperatorConfig { + return this.getOperatorFormControl(node).value; + } + + registerControls( + nodeId: string, + expr: ExpressionFormValue, + operator: OperatorDto, + value: UiPolicyLiteral, + ) { + if (expr.type !== 'CONSTRAINT') { + return; + } + + const supportedOperators = expr.supportedOperators ?? []; + const operatorConfig = + supportedOperators.find((it) => it.id === operator) ?? + supportedOperators[0]; + + const operatorControl = new UntypedFormControl(operatorConfig); + + const valueControl = expr.verb!.adapter.fromControlFactory(); + valueControl.reset( + expr.verb!.adapter.buildFormValueFn(value, operatorConfig), + ); + + this.formGroup.addControl(`${nodeId}-value`, valueControl); + this.formGroup.addControl(`${nodeId}-op`, operatorControl); + } + + unregisterControls(node: TreeNode) { + this.dfs(node, (node) => this.unregisterNodeControls(node)); + } + + getValueFormControl(treeNode: TreeNode): FormControl { + return this.formGroup.get(`${treeNode.id}-value`) as FormControl; + } + + getOperatorFormControl( + treeNode: TreeNode, + ): UntypedFormControl { + return this.formGroup.get(`${treeNode.id}-op`) as FormControl; + } + + private unregisterNodeControls(node: TreeNode) { + if (node.value.type !== 'CONSTRAINT') { + return; + } + + [`${node.id}-value`, `${node.id}-op`].forEach((it) => + this.formGroup.removeControl(it), + ); + } + + private dfs( + treeNode: TreeNode, + callback: (node: TreeNode) => void, + ) { + callback(treeNode); + treeNode.children.forEach((child) => this.dfs(child, callback)); + } +} diff --git a/connector-ui/src/app/shared/business/policy-editor/editor/expression-form-handler.ts b/connector-ui/src/app/shared/business/policy-editor/editor/expression-form-handler.ts new file mode 100644 index 000000000..cae3495c3 --- /dev/null +++ b/connector-ui/src/app/shared/business/policy-editor/editor/expression-form-handler.ts @@ -0,0 +1,146 @@ +import {Injectable} from '@angular/core'; +import {UiPolicyExpression} from '@sovity.de/edc-client'; +import {PolicyExpressionMapped} from '../model/policy-expression-mapped'; +import {PolicyMapper} from '../model/policy-mapper'; +import {PolicyMultiExpressionConfig} from '../model/policy-multi-expressions'; +import {PolicyOperatorService} from '../model/policy-operators'; +import {PolicyVerbConfig} from '../model/policy-verbs'; +import {Tree, TreeGeneratorFn, TreeNode} from '../model/tree'; +import {ExpressionFormControls} from './expression-form-controls'; +import {ExpressionFormValue} from './expression-form-value'; + +/** + * Central service for interacting with the policy expression form. + * + * Must be provided at the component level as viewProvider. + */ +@Injectable() +export class ExpressionFormHandler { + tree: Tree = this.buildTree({type: 'EMPTY'}); + + constructor( + public controls: ExpressionFormControls, + public policyMapper: PolicyMapper, + private policyOperatorService: PolicyOperatorService, + ) {} + + private buildTree( + expression: PolicyExpressionMapped, + ): Tree { + return Tree.ofTreeLikeStructure< + PolicyExpressionMapped, + ExpressionFormValue + >({ + root: expression, + generatorFn: this.treeGenerator(), + }); + } + + addConstraint(path: string[], verb: PolicyVerbConfig) { + const expression: UiPolicyExpression = { + type: 'CONSTRAINT', + constraint: { + left: verb.operandLeftId, + ...verb.adapter.emptyConstraintValue(), + }, + }; + + this.addExpression(path, expression); + } + + addMultiExpression( + path: string[], + multiExpression: PolicyMultiExpressionConfig, + ) { + const expression: UiPolicyExpression = { + type: multiExpression.expressionType, + expressions: [], + }; + this.addExpression(path, expression); + } + + addExpression(path: string[], expression: UiPolicyExpression) { + const mapped = this.policyMapper.buildPolicy(expression); + this.addTree(path, mapped); + } + + removeNode(node: TreeNode) { + this.controls.unregisterControls(node); + if (node.path.length === 1) { + this.tree.replaceTree(node.path, {type: 'EMPTY'}, this.treeGenerator()); + } else { + this.tree.remove(node.path); + } + } + + private addTree( + path: string[], + expression: PolicyExpressionMapped, + ): TreeNode { + if (path.length === 1 && this.tree.root.value.type === 'EMPTY') { + this.tree.replaceTree(path, expression, this.treeGenerator()); + return this.tree.root; + } else { + return this.tree.pushTree(path, expression, this.treeGenerator()); + } + } + + private treeGenerator(): TreeGeneratorFn< + PolicyExpressionMapped, + ExpressionFormValue + > { + // Function returning a function for it to be available in the constructor + return (expr, nodeId) => { + const value = this.buildExpressionFormValue(expr); + + // Also create form controls as necessary + this.controls.registerControls( + nodeId, + value, + expr.operator?.id!, + expr.valueRaw!, + ); + + return {value, children: expr.expressions ?? []}; + }; + } + + private buildExpressionFormValue( + original: PolicyExpressionMapped, + ): ExpressionFormValue { + const supportedOperators = this.policyOperatorService + .getSupportedPolicyOperators() + .filter((it) => original.verb?.supportedOperators.includes(it.id)); + return { + type: original.type, + multiExpression: original.multiExpression, + verb: original.verb, + supportedOperators, + }; + } + + toUiPolicyExpression() { + const visit = (node: TreeNode): UiPolicyExpression => { + const value = node.value; + if (value.type === 'EMPTY') { + return {type: 'EMPTY'}; + } else if (value.type === 'MULTI') { + return { + type: value.multiExpression!.expressionType, + expressions: node.children.map((it) => visit(it)), + }; + } else { + return { + type: 'CONSTRAINT', + constraint: { + left: value.verb!.operandLeftId, + operator: this.controls.getOperator(node).id, + right: this.controls.getValue(node), + }, + }; + } + }; + + return visit(this.tree.root); + } +} diff --git a/connector-ui/src/app/shared/business/policy-editor/editor/expression-form-value.ts b/connector-ui/src/app/shared/business/policy-editor/editor/expression-form-value.ts new file mode 100644 index 000000000..395d9c95b --- /dev/null +++ b/connector-ui/src/app/shared/business/policy-editor/editor/expression-form-value.ts @@ -0,0 +1,11 @@ +import {PolicyMultiExpressionConfig} from '../model/policy-multi-expressions'; +import {PolicyOperatorConfig} from '../model/policy-operators'; +import {PolicyVerbConfig} from '../model/policy-verbs'; + +export interface ExpressionFormValue { + type: 'CONSTRAINT' | 'MULTI' | 'EMPTY'; + + multiExpression?: PolicyMultiExpressionConfig; + verb?: PolicyVerbConfig; + supportedOperators?: PolicyOperatorConfig[]; +} diff --git a/connector-ui/src/app/shared/business/policy-editor/editor/policy-form-add-menu/policy-form-add-menu.component.html b/connector-ui/src/app/shared/business/policy-editor/editor/policy-form-add-menu/policy-form-add-menu.component.html new file mode 100644 index 000000000..dbc6158da --- /dev/null +++ b/connector-ui/src/app/shared/business/policy-editor/editor/policy-form-add-menu/policy-form-add-menu.component.html @@ -0,0 +1,37 @@ + + + + + + + + + + + + diff --git a/connector-ui/src/app/shared/business/policy-editor/editor/policy-form-add-menu/policy-form-add-menu.component.ts b/connector-ui/src/app/shared/business/policy-editor/editor/policy-form-add-menu/policy-form-add-menu.component.ts new file mode 100644 index 000000000..ea0a9300a --- /dev/null +++ b/connector-ui/src/app/shared/business/policy-editor/editor/policy-form-add-menu/policy-form-add-menu.component.ts @@ -0,0 +1,65 @@ +import {Component, Input, OnDestroy} from '@angular/core'; +import {Subject} from 'rxjs'; +import { + PolicyMultiExpressionConfig, + PolicyMultiExpressionService, +} from '../../model/policy-multi-expressions'; +import {PolicyVerbConfig, PolicyVerbService} from '../../model/policy-verbs'; +import {TreeNode} from '../../model/tree'; +import {ExpressionFormHandler} from '../expression-form-handler'; +import {ExpressionFormValue} from '../expression-form-value'; +import { + PolicyExpressionRecipe, + PolicyExpressionRecipeService, +} from '../recipes/policy-expression-recipe.service'; + +@Component({ + selector: 'policy-form-add-menu', + templateUrl: './policy-form-add-menu.component.html', +}) +export class PolicyFormAddMenuComponent implements OnDestroy { + multiExpressions: PolicyMultiExpressionConfig[] = []; + verbs: PolicyVerbConfig[] = []; + + @Input() + treeNode!: TreeNode; + + constructor( + public expressionFormHandler: ExpressionFormHandler, + public policyExpressionRecipeService: PolicyExpressionRecipeService, + private policyVerbService: PolicyVerbService, + private policyMultiExpressionService: PolicyMultiExpressionService, + ) {} + + onMenuOpened() { + this.verbs = this.policyVerbService.getSupportedPolicyVerbs(); + this.multiExpressions = + this.policyMultiExpressionService.getSupportedMultiExpressions(); + } + + onAddConstraint(constraint: PolicyVerbConfig) { + const path = this.treeNode.path; + this.expressionFormHandler.addConstraint(path, constraint); + } + + onAddMultiExpression(multi: PolicyMultiExpressionConfig) { + const path = this.treeNode.path; + this.expressionFormHandler.addMultiExpression(path, multi); + } + + onAddRecipe(recipe: PolicyExpressionRecipe) { + const path = this.treeNode.path; + recipe + .onclick(this.ngOnDestroy$) + .subscribe((expression) => + this.expressionFormHandler.addExpression(path, expression), + ); + } + + ngOnDestroy$ = new Subject(); + + ngOnDestroy(): void { + this.ngOnDestroy$.next(null); + this.ngOnDestroy$.complete(); + } +} diff --git a/connector-ui/src/app/shared/business/policy-editor/editor/policy-form-expression-constraint/policy-form-expression-constraint.component.html b/connector-ui/src/app/shared/business/policy-editor/editor/policy-form-expression-constraint/policy-form-expression-constraint.component.html new file mode 100644 index 000000000..e31011f90 --- /dev/null +++ b/connector-ui/src/app/shared/business/policy-editor/editor/policy-form-expression-constraint/policy-form-expression-constraint.component.html @@ -0,0 +1,48 @@ +
+ {{ verb.operandLeftTitle }} +
+ +
+ +
+ + + {{ verb.operandLeftTitle }} + + {{ verb.operandRightHint }} + + + + {{ verb.operandLeftTitle }} + + {{ verb.operandRightHint }} + + + + + +
+ +
diff --git a/connector-ui/src/app/shared/business/policy-editor/editor/policy-form-expression-constraint/policy-form-expression-constraint.component.ts b/connector-ui/src/app/shared/business/policy-editor/editor/policy-form-expression-constraint/policy-form-expression-constraint.component.ts new file mode 100644 index 000000000..4dfd97c37 --- /dev/null +++ b/connector-ui/src/app/shared/business/policy-editor/editor/policy-form-expression-constraint/policy-form-expression-constraint.component.ts @@ -0,0 +1,28 @@ +import {Component, HostBinding, Input} from '@angular/core'; +import {PolicyVerbConfig} from '../../model/policy-verbs'; +import {TreeNode} from '../../model/tree'; +import {ExpressionFormHandler} from '../expression-form-handler'; +import {ExpressionFormValue} from '../expression-form-value'; + +@Component({ + selector: 'policy-form-expression-constraint', + templateUrl: './policy-form-expression-constraint.component.html', +}) +export class PolicyFormExpressionConstraintComponent { + @HostBinding('class.flex') + @HostBinding('class.gap-4') + cls = true; + + @Input() + treeNode!: TreeNode; + + get expr(): ExpressionFormValue { + return this.treeNode.value; + } + + get verb(): PolicyVerbConfig { + return this.expr.verb!; + } + + constructor(public expressionFormHandler: ExpressionFormHandler) {} +} diff --git a/connector-ui/src/app/shared/business/policy-editor/editor/policy-form-expression-empty/policy-form-expression-empty.component.html b/connector-ui/src/app/shared/business/policy-editor/editor/policy-form-expression-empty/policy-form-expression-empty.component.html new file mode 100644 index 000000000..073d65f36 --- /dev/null +++ b/connector-ui/src/app/shared/business/policy-editor/editor/policy-form-expression-empty/policy-form-expression-empty.component.html @@ -0,0 +1 @@ + diff --git a/connector-ui/src/app/shared/business/policy-editor/editor/policy-form-expression-empty/policy-form-expression-empty.component.ts b/connector-ui/src/app/shared/business/policy-editor/editor/policy-form-expression-empty/policy-form-expression-empty.component.ts new file mode 100644 index 000000000..23cdfca95 --- /dev/null +++ b/connector-ui/src/app/shared/business/policy-editor/editor/policy-form-expression-empty/policy-form-expression-empty.component.ts @@ -0,0 +1,17 @@ +import {Component, HostBinding, Input} from '@angular/core'; +import {TreeNode} from '../../model/tree'; +import {ExpressionFormValue} from '../expression-form-value'; + +@Component({ + selector: 'policy-form-expression-empty', + templateUrl: './policy-form-expression-empty.component.html', +}) +export class PolicyFormExpressionEmptyComponent { + @HostBinding('class.flex') + @HostBinding('class.h-[4rem]') + @HostBinding('class.items-center') + cls = true; + + @Input() + treeNode!: TreeNode; +} diff --git a/connector-ui/src/app/shared/business/policy-editor/editor/policy-form-expression-multi/policy-form-expression-multi.component.html b/connector-ui/src/app/shared/business/policy-editor/editor/policy-form-expression-multi/policy-form-expression-multi.component.html new file mode 100644 index 000000000..dc5543ee1 --- /dev/null +++ b/connector-ui/src/app/shared/business/policy-editor/editor/policy-form-expression-multi/policy-form-expression-multi.component.html @@ -0,0 +1,41 @@ +
+
+ {{ + expr.multiExpression!.title + }} +
+ +
+
+
+ + +
+
+ +
+ +
+
+
diff --git a/connector-ui/src/app/shared/business/policy-editor/editor/policy-form-expression-multi/policy-form-expression-multi.component.ts b/connector-ui/src/app/shared/business/policy-editor/editor/policy-form-expression-multi/policy-form-expression-multi.component.ts new file mode 100644 index 000000000..579859c82 --- /dev/null +++ b/connector-ui/src/app/shared/business/policy-editor/editor/policy-form-expression-multi/policy-form-expression-multi.component.ts @@ -0,0 +1,23 @@ +import {Component, HostBinding, Input, TrackByFunction} from '@angular/core'; +import {TreeNode} from '../../model/tree'; +import {ExpressionFormValue} from '../expression-form-value'; + +@Component({ + selector: 'policy-form-expression-multi', + templateUrl: './policy-form-expression-multi.component.html', +}) +export class PolicyFormExpressionMultiComponent { + @HostBinding('class.flex') + @HostBinding('class.flex-col') + @HostBinding('class.justify-stretch') + cls = true; + + @Input() + treeNode!: TreeNode; + + trackByFn: TrackByFunction> = (_, it) => it.id; + + get expr(): ExpressionFormValue { + return this.treeNode.value; + } +} diff --git a/connector-ui/src/app/shared/business/policy-editor/editor/policy-form-expression/policy-form-expression.component.html b/connector-ui/src/app/shared/business/policy-editor/editor/policy-form-expression/policy-form-expression.component.html new file mode 100644 index 000000000..d56831a5a --- /dev/null +++ b/connector-ui/src/app/shared/business/policy-editor/editor/policy-form-expression/policy-form-expression.component.html @@ -0,0 +1,14 @@ + + + + + + + + diff --git a/connector-ui/src/app/shared/business/policy-editor/editor/policy-form-expression/policy-form-expression.component.ts b/connector-ui/src/app/shared/business/policy-editor/editor/policy-form-expression/policy-form-expression.component.ts new file mode 100644 index 000000000..59d875887 --- /dev/null +++ b/connector-ui/src/app/shared/business/policy-editor/editor/policy-form-expression/policy-form-expression.component.ts @@ -0,0 +1,21 @@ +import {Component, Input, TrackByFunction} from '@angular/core'; +import {TreeNode} from '../../model/tree'; +import {ExpressionFormHandler} from '../expression-form-handler'; +import {ExpressionFormValue} from '../expression-form-value'; + +@Component({ + selector: 'policy-form-expression', + templateUrl: './policy-form-expression.component.html', +}) +export class PolicyFormExpressionComponent { + @Input() + treeNode!: TreeNode; + + trackByFn: TrackByFunction> = (_, it) => it.id; + + get expr(): ExpressionFormValue { + return this.treeNode.value; + } + + constructor(public expressionFormHandler: ExpressionFormHandler) {} +} diff --git a/connector-ui/src/app/shared/business/policy-editor/editor/policy-form-remove-button/policy-form-remove-button.component.html b/connector-ui/src/app/shared/business/policy-editor/editor/policy-form-remove-button/policy-form-remove-button.component.html new file mode 100644 index 000000000..c3223c12c --- /dev/null +++ b/connector-ui/src/app/shared/business/policy-editor/editor/policy-form-remove-button/policy-form-remove-button.component.html @@ -0,0 +1,3 @@ + diff --git a/connector-ui/src/app/shared/business/policy-editor/editor/policy-form-remove-button/policy-form-remove-button.component.ts b/connector-ui/src/app/shared/business/policy-editor/editor/policy-form-remove-button/policy-form-remove-button.component.ts new file mode 100644 index 000000000..33e5a6025 --- /dev/null +++ b/connector-ui/src/app/shared/business/policy-editor/editor/policy-form-remove-button/policy-form-remove-button.component.ts @@ -0,0 +1,19 @@ +import {Component, Input} from '@angular/core'; +import {TreeNode} from '../../model/tree'; +import {ExpressionFormHandler} from '../expression-form-handler'; +import {ExpressionFormValue} from '../expression-form-value'; + +@Component({ + selector: 'policy-form-remove-button', + templateUrl: './policy-form-remove-button.component.html', +}) +export class PolicyFormRemoveButton { + @Input() + treeNode!: TreeNode; + + constructor(public expressionFormHandler: ExpressionFormHandler) {} + + onRemoveClick() { + this.expressionFormHandler.removeNode(this.treeNode); + } +} diff --git a/connector-ui/src/app/shared/business/policy-editor/editor/policy-form-required-providers.ts b/connector-ui/src/app/shared/business/policy-editor/editor/policy-form-required-providers.ts new file mode 100644 index 000000000..2047b3756 --- /dev/null +++ b/connector-ui/src/app/shared/business/policy-editor/editor/policy-form-required-providers.ts @@ -0,0 +1,7 @@ +import {ExpressionFormControls} from './expression-form-controls'; +import {ExpressionFormHandler} from './expression-form-handler'; + +export const policyFormRequiredViewProviders = [ + ExpressionFormHandler, + ExpressionFormControls, +]; diff --git a/connector-ui/src/app/shared/business/policy-editor/editor/recipes/policy-expression-recipe.service.ts b/connector-ui/src/app/shared/business/policy-editor/editor/recipes/policy-expression-recipe.service.ts new file mode 100644 index 000000000..c94ef7891 --- /dev/null +++ b/connector-ui/src/app/shared/business/policy-editor/editor/recipes/policy-expression-recipe.service.ts @@ -0,0 +1,41 @@ +import {ComponentType} from '@angular/cdk/portal'; +import {Injectable} from '@angular/core'; +import {MatDialog} from '@angular/material/dialog'; +import {Observable} from 'rxjs'; +import {UiPolicyExpression} from '@sovity.de/edc-client'; +import {showDialogUntil} from '../../../../../core/utils/mat-dialog-utils'; +import {filterNotNull} from '../../../../../core/utils/rxjs-utils'; +import {TimespanRestrictionDialogComponent} from './timespan-restriction-dialog/timespan-restriction-dialog.component'; + +export interface PolicyExpressionRecipe { + title: string; + tooltip: string; + onclick: (until$: Observable) => Observable; +} + +@Injectable() +export class PolicyExpressionRecipeService { + recipes: PolicyExpressionRecipe[] = [ + { + title: 'Timespan Restriction', + tooltip: + 'Timespan at which the policy is evaluated. This can be used to restrict the data offer to certain time periods', + onclick: (until$: Observable) => + this.showRecipeDialog(TimespanRestrictionDialogComponent, until$), + }, + ]; + + constructor(private dialog: MatDialog) {} + + private showRecipeDialog( + cmp: ComponentType, + until$: Observable, + ): Observable { + return showDialogUntil( + this.dialog, + cmp, + {}, + until$, + ).pipe(filterNotNull()); + } +} diff --git a/connector-ui/src/app/shared/business/policy-editor/editor/recipes/timespan-restriction-dialog/timespan-restriction-dialog.component.html b/connector-ui/src/app/shared/business/policy-editor/editor/recipes/timespan-restriction-dialog/timespan-restriction-dialog.component.html new file mode 100644 index 000000000..a14abb38b --- /dev/null +++ b/connector-ui/src/app/shared/business/policy-editor/editor/recipes/timespan-restriction-dialog/timespan-restriction-dialog.component.html @@ -0,0 +1,46 @@ +

+ {{ 'policy_expression.timespan_restriction_title' | translate }} +

+ +
+ + + {{ + 'policy_definition_page.date_range' | translate + }} + + + + + DD/MM/YYYY – DD/MM/YYYY + + + {{ + validationMessages.invalidDateRangeMessage + }} + +
+
+ + + + + + diff --git a/connector-ui/src/app/shared/business/policy-editor/editor/recipes/timespan-restriction-dialog/timespan-restriction-dialog.component.ts b/connector-ui/src/app/shared/business/policy-editor/editor/recipes/timespan-restriction-dialog/timespan-restriction-dialog.component.ts new file mode 100644 index 000000000..2a163d9e9 --- /dev/null +++ b/connector-ui/src/app/shared/business/policy-editor/editor/recipes/timespan-restriction-dialog/timespan-restriction-dialog.component.ts @@ -0,0 +1,52 @@ +import {Component, OnDestroy} from '@angular/core'; +import {FormBuilder} from '@angular/forms'; +import {MatDialogRef} from '@angular/material/dialog'; +import {Subject} from 'rxjs'; +import {UiPolicyExpression} from '@sovity.de/edc-client'; +import {validDateRange} from '../../../../../../core/validators/valid-date-range-optional-end'; +import {ValidationMessages} from '../../../../../../core/validators/validation-messages'; +import {buildTimespanRestriction} from './timespan-restriction-expression'; + +@Component({ + selector: 'timespan-restriction-dialog', + templateUrl: './timespan-restriction-dialog.component.html', +}) +export class TimespanRestrictionDialogComponent implements OnDestroy { + group = this.formBuilder.nonNullable.group({ + range: this.formBuilder.group( + { + start: null as Date | null, + end: null as Date | null, + }, + {validators: validDateRange}, + ), + }); + + constructor( + private formBuilder: FormBuilder, + private dialogRef: MatDialogRef, + public validationMessages: ValidationMessages, + ) {} + + onAdd() { + const formValue = this.group.value; + + const expression = buildTimespanRestriction( + formValue.range!.start!, + formValue.range!.end!, + ); + + this.close(expression); + } + + private close(params: UiPolicyExpression) { + this.dialogRef.close(params); + } + + ngOnDestroy$ = new Subject(); + + ngOnDestroy(): void { + this.ngOnDestroy$.next(null); + this.ngOnDestroy$.complete(); + } +} diff --git a/connector-ui/src/app/shared/business/policy-editor/editor/recipes/timespan-restriction-dialog/timespan-restriction-expression.ts b/connector-ui/src/app/shared/business/policy-editor/editor/recipes/timespan-restriction-dialog/timespan-restriction-expression.ts new file mode 100644 index 000000000..ddb2b20e8 --- /dev/null +++ b/connector-ui/src/app/shared/business/policy-editor/editor/recipes/timespan-restriction-dialog/timespan-restriction-expression.ts @@ -0,0 +1,22 @@ +import {OperatorDto, UiPolicyExpression} from '@sovity.de/edc-client'; +import {addDays} from 'date-fns'; +import {policyLeftExpressions} from '../../../model/policy-left-expressions'; +import {constraint, multi} from '../../../model/ui-policy-expression-utils'; + +export const buildTimespanRestriction = ( + firstDay: Date, + lastDay: Date, +): UiPolicyExpression => { + const evaluationTimeConstraint = (operator: OperatorDto, value: Date) => + constraint( + policyLeftExpressions.policyEvaluationTime, + operator, + value.toISOString(), + ); + + return multi( + 'AND', + evaluationTimeConstraint('GEQ', firstDay), + evaluationTimeConstraint('LEQ', addDays(lastDay, 1)), + ); +}; diff --git a/connector-ui/src/app/shared/business/policy-editor/model/policy-expression-mapped.ts b/connector-ui/src/app/shared/business/policy-editor/model/policy-expression-mapped.ts new file mode 100644 index 000000000..221bd2abb --- /dev/null +++ b/connector-ui/src/app/shared/business/policy-editor/model/policy-expression-mapped.ts @@ -0,0 +1,17 @@ +import {UiPolicyLiteral} from '@sovity.de/edc-client'; +import {PolicyMultiExpressionConfig} from './policy-multi-expressions'; +import {PolicyOperatorConfig} from './policy-operators'; +import {PolicyVerbConfig} from './policy-verbs'; + +export interface PolicyExpressionMapped { + type: 'CONSTRAINT' | 'MULTI' | 'EMPTY'; + + multiExpression?: PolicyMultiExpressionConfig; + expressions?: PolicyExpressionMapped[]; + + verb?: PolicyVerbConfig; + operator?: PolicyOperatorConfig; + valueRaw?: UiPolicyLiteral; + valueJson?: string; + displayValue?: string; +} diff --git a/connector-ui/src/app/shared/business/policy-editor/model/policy-form-adapter.ts b/connector-ui/src/app/shared/business/policy-editor/model/policy-form-adapter.ts new file mode 100644 index 000000000..b1a9d727a --- /dev/null +++ b/connector-ui/src/app/shared/business/policy-editor/model/policy-form-adapter.ts @@ -0,0 +1,148 @@ +import {UntypedFormControl, Validators} from '@angular/forms'; +import {UiPolicyConstraint, UiPolicyLiteral} from '@sovity.de/edc-client'; +import { + localTzDayToIsoString, + truncateToLocalTzDay, + truncateToLocalTzDayRaw, +} from '../../../../core/utils/date-utils'; +import {jsonValidator} from '../../../../core/validators/json-validator'; +import { + readArrayLiteral, + readJsonLiteral, + readSingleStringLiteral, + stringLiteral, +} from './policy-jsonld-utils'; +import {PolicyOperatorConfig} from './policy-operators'; + +export interface PolicyFormAdapter { + displayText: ( + value: UiPolicyLiteral, + operator: PolicyOperatorConfig, + ) => string | null; + fromControlFactory: () => UntypedFormControl; + buildFormValueFn: ( + literal: UiPolicyLiteral, + operator: PolicyOperatorConfig, + ) => T; + buildValueFn: ( + formValue: T, + operator: PolicyOperatorConfig, + ) => UiPolicyLiteral; + emptyConstraintValue: () => Pick; +} + +export const localDateAdapter: PolicyFormAdapter = { + displayText: (literal, operator): string | null => { + const stringOrNull = readSingleStringLiteral(literal); + return safeConversion(stringOrNull, (string) => { + const date = new Date(string); + const upperBound = isUpperBound(operator); + + return truncateToLocalTzDay(date, upperBound); + }); + }, + fromControlFactory: () => new UntypedFormControl(null, Validators.required), + buildFormValueFn: (literal, operator): Date | null => { + const stringOrNull = readSingleStringLiteral(literal); + return safeConversion(stringOrNull, (string) => { + const date = new Date(string); + const upperBound = isUpperBound(operator); + + // Editing datetimes from a different TZ as days has no good solution + return truncateToLocalTzDayRaw(date, upperBound); + }); + }, + buildValueFn: (valueOrNull, operator) => { + return stringLiteral( + safeConversion(valueOrNull, (value) => { + const upperBound = isUpperBound(operator); + + return localTzDayToIsoString(value, upperBound); + }), + ); + }, + emptyConstraintValue: () => ({ + operator: 'LT', + right: { + type: 'STRING', + }, + }), +}; + +export const stringArrayOrCommaJoinedAdapter: PolicyFormAdapter = { + displayText: (literal): string | null => readArrayLiteral(literal).join(', '), + fromControlFactory: () => new UntypedFormControl([], Validators.required), + buildFormValueFn: (literal): string[] => { + if (literal.type === 'STRING') { + return literal.value?.split(',') ?? []; + } + + return readArrayLiteral(literal); + }, + buildValueFn: (value, operator) => { + const items = value as string[]; + if (operator.id === 'EQ') { + return { + type: 'STRING', + value: items.join(','), + }; + } + + return { + type: 'STRING_LIST', + valueList: items, + }; + }, + emptyConstraintValue: () => ({ + operator: 'IN', + right: { + type: 'STRING_LIST', + valueList: [], + }, + }), +}; + +export const jsonAdapter: PolicyFormAdapter = { + displayText: (literal) => readJsonLiteral(literal), + buildFormValueFn: (literal) => readJsonLiteral(literal), + buildValueFn: (formValue) => ({ + type: 'JSON', + value: formValue, + }), + fromControlFactory: () => + new UntypedFormControl('', [Validators.required, jsonValidator]), + emptyConstraintValue: () => ({ + operator: 'EQ', + right: { + type: 'JSON', + value: 'null', + }, + }), +}; + +const isUpperBound = (operator: PolicyOperatorConfig) => + operator.id === 'GT' || operator.id === 'LEQ'; + +/** + * Helper function for reducing mental complexity of mapping code: + * - Handles null input + * - Handles undefined output + * - Catches exceptions and returns null + * + * @param valueOrNull value + * @param mapper mapper + */ +const safeConversion = ( + valueOrNull: T | null | undefined, + mapper: (it: T) => R | null | undefined, +): R | null => { + if (valueOrNull == null) { + return null; + } + + try { + return mapper(valueOrNull) ?? null; + } catch (e) { + return null; + } +}; diff --git a/connector-ui/src/app/shared/business/policy-editor/model/policy-jsonld-utils.ts b/connector-ui/src/app/shared/business/policy-editor/model/policy-jsonld-utils.ts new file mode 100644 index 000000000..c4502862b --- /dev/null +++ b/connector-ui/src/app/shared/business/policy-editor/model/policy-jsonld-utils.ts @@ -0,0 +1,38 @@ +import {UiPolicyLiteral} from '@sovity.de/edc-client'; +import {filterNonNull} from '../../../../core/utils/array-utils'; + +export const readSingleStringLiteral = ( + literal: UiPolicyLiteral, +): string | null => { + if (literal.type === 'STRING') { + return literal.value ?? null; + } else if (literal.type === 'STRING_LIST') { + return literal.valueList?.length ? literal.valueList[0] : null; + } + return null; +}; + +export const readArrayLiteral = (literal: UiPolicyLiteral): string[] => { + if (literal.type === 'STRING') { + return filterNonNull([literal.value]); + } else if (literal.type === 'STRING_LIST') { + return literal.valueList ?? []; + } + return []; +}; + +export const readJsonLiteral = (literal: UiPolicyLiteral): string => { + if (literal.type === 'STRING') { + return JSON.stringify(literal.value); + } else if (literal.type === 'STRING_LIST') { + return JSON.stringify(literal.valueList); + } + return literal.value ?? 'null'; +}; + +export const stringLiteral = ( + value: string | null | undefined, +): UiPolicyLiteral => ({ + type: 'STRING', + value: value ?? undefined, +}); diff --git a/connector-ui/src/app/shared/business/policy-editor/model/policy-left-expressions.ts b/connector-ui/src/app/shared/business/policy-editor/model/policy-left-expressions.ts new file mode 100644 index 000000000..e09101017 --- /dev/null +++ b/connector-ui/src/app/shared/business/policy-editor/model/policy-left-expressions.ts @@ -0,0 +1,4 @@ +export const policyLeftExpressions = { + policyEvaluationTime: 'POLICY_EVALUATION_TIME', + referringConnector: 'REFERRING_CONNECTOR', +}; diff --git a/connector-ui/src/app/shared/business/policy-editor/model/policy-mapper.ts b/connector-ui/src/app/shared/business/policy-editor/model/policy-mapper.ts new file mode 100644 index 000000000..8fa3a63e4 --- /dev/null +++ b/connector-ui/src/app/shared/business/policy-editor/model/policy-mapper.ts @@ -0,0 +1,89 @@ +import {Injectable} from '@angular/core'; +import {UiPolicyExpression, UiPolicyLiteral} from '@sovity.de/edc-client'; +import {PolicyExpressionMapped} from './policy-expression-mapped'; +import {PolicyMultiExpressionService} from './policy-multi-expressions'; +import {PolicyOperatorConfig, PolicyOperatorService} from './policy-operators'; +import {PolicyVerbConfig, PolicyVerbService} from './policy-verbs'; + +@Injectable() +export class PolicyMapper { + constructor( + private policyOperatorService: PolicyOperatorService, + private policyMultiExpressionService: PolicyMultiExpressionService, + private policyVerbService: PolicyVerbService, + ) {} + + buildPolicy(expression: UiPolicyExpression): PolicyExpressionMapped { + if (expression.type === 'EMPTY') { + return {type: 'EMPTY'}; + } + + if (expression.type === 'CONSTRAINT') { + return this.mapConstraint(expression); + } + + return this.mapMultiExpression(expression); + } + + private mapConstraint( + expression: UiPolicyExpression, + ): PolicyExpressionMapped { + const verb = this.policyVerbService.getVerbConfig( + expression.constraint?.left!, + ); + const operator = this.policyOperatorService.getOperatorConfig( + expression.constraint?.operator!, + ); + const value = expression.constraint?.right; + + return { + type: 'CONSTRAINT', + verb, + operator, + valueRaw: value, + valueJson: this.formatJson(value!), + displayValue: this.formatValue(value, verb, operator) ?? 'null', + }; + } + + private mapMultiExpression( + expression: UiPolicyExpression, + ): PolicyExpressionMapped { + const multiExpression = + this.policyMultiExpressionService.getMultiExpressionConfig( + expression.type, + ); + const expressions = (expression.expressions ?? []).map((it) => + this.buildPolicy(it), + ); + return { + type: 'MULTI', + multiExpression, + expressions, + }; + } + + private formatValue( + value: UiPolicyLiteral | undefined, + verbConfig: PolicyVerbConfig, + operatorConfig: PolicyOperatorConfig, + ) { + if (value == null) { + return ''; + } + + return verbConfig.adapter.displayText(value, operatorConfig); + } + + private formatJson(value: UiPolicyLiteral): string { + if (value.type === 'STRING_LIST') { + return JSON.stringify(value.valueList); + } + + if (value.type === 'JSON') { + return value.value ?? ''; + } + + return JSON.stringify(value.value); + } +} diff --git a/connector-ui/src/app/shared/business/policy-editor/model/policy-multi-expressions.ts b/connector-ui/src/app/shared/business/policy-editor/model/policy-multi-expressions.ts new file mode 100644 index 000000000..afa9aa22b --- /dev/null +++ b/connector-ui/src/app/shared/business/policy-editor/model/policy-multi-expressions.ts @@ -0,0 +1,76 @@ +import {Injectable} from '@angular/core'; +import {TranslateService} from '@ngx-translate/core'; +import {UiPolicyExpressionType} from '@sovity.de/edc-client'; +import {LazyTranslation} from '../../../../core/utils/lazy-utils'; +import {associateBy} from '../../../../core/utils/map-utils'; + +export interface PolicyMultiExpressionConfig { + expressionType: UiPolicyExpressionType; + title: string; + description: string; +} + +@Injectable() +export class PolicyMultiExpressionService { + byId: LazyTranslation< + Map + >; + + constructor(private translateService: TranslateService) { + this.byId = new LazyTranslation(this.translateService, () => + this.buildByIdMap(), + ); + } + + getMultiExpressionConfig( + expressionType: UiPolicyExpressionType, + ): PolicyMultiExpressionConfig { + return ( + this.byId.getValue().get(expressionType) ?? + this.getFallbackMultiExpressionConfig(expressionType) + ); + } + + getSupportedMultiExpressions(): PolicyMultiExpressionConfig[] { + return [ + { + expressionType: 'AND', + title: 'AND', + description: + 'Conjunction of several expressions. Evaluates to true if and only if all child expressions are true', + }, + { + expressionType: 'OR', + title: 'OR', + description: + 'Disjunction of several expressions. Evaluates to true if and only if at least one child expression is true', + }, + { + expressionType: 'XONE', + title: 'XONE', + description: + 'XONE operation. Evaluates to true if and only if exactly one child expression is true', + }, + ]; + } + + private buildByIdMap(): Map< + UiPolicyExpressionType, + PolicyMultiExpressionConfig + > { + return associateBy( + this.getSupportedMultiExpressions(), + (it) => it.expressionType, + ); + } + + private getFallbackMultiExpressionConfig( + expressionType: UiPolicyExpressionType, + ) { + return { + expressionType, + title: expressionType, + description: '', + }; + } +} diff --git a/connector-ui/src/app/shared/business/policy-editor/model/policy-operators.ts b/connector-ui/src/app/shared/business/policy-editor/model/policy-operators.ts new file mode 100644 index 000000000..e9722fcc1 --- /dev/null +++ b/connector-ui/src/app/shared/business/policy-editor/model/policy-operators.ts @@ -0,0 +1,108 @@ +import {Injectable} from '@angular/core'; +import {TranslateService} from '@ngx-translate/core'; +import {OperatorDto} from '@sovity.de/edc-client'; +import {LazyTranslation} from '../../../../core/utils/lazy-utils'; +import {associateBy} from '../../../../core/utils/map-utils'; + +export interface PolicyOperatorConfig { + id: OperatorDto; + title: string; + description: string; +} + +@Injectable() +export class PolicyOperatorService { + byId: LazyTranslation>; + + constructor(private translateService: TranslateService) { + this.byId = new LazyTranslation(this.translateService, () => + this.buildByIdMap(), + ); + } + + getOperatorConfig(operator: OperatorDto): PolicyOperatorConfig { + return ( + this.byId.getValue().get(operator) ?? + this.defaultPolicyOperatorConfig(operator) + ); + } + + getSupportedPolicyOperators(): PolicyOperatorConfig[] { + return [ + { + id: 'EQ', + title: '=', + description: 'Equal to', + }, + { + id: 'NEQ', + title: '≠', + description: 'Not equal to', + }, + { + id: 'GEQ', + title: '≥', + description: 'Greater than or equal to', + }, + { + id: 'GT', + title: '>', + description: 'Greater than', + }, + { + id: 'LEQ', + title: '≤', + description: 'Less than or equal to', + }, + { + id: 'LT', + title: '<', + description: 'Less than', + }, + { + id: 'IN', + title: 'IN', + description: 'In', + }, + { + id: 'HAS_PART', + title: 'HAS PART', + description: 'Has Part', + }, + { + id: 'IS_A', + title: 'IS A', + description: 'Is a', + }, + { + id: 'IS_NONE_OF', + title: 'IS NONE OF', + description: 'Is none of', + }, + { + id: 'IS_ANY_OF', + title: 'IS ANY OF', + description: 'Is any of', + }, + { + id: 'IS_ALL_OF', + title: 'IS ALL OF', + description: 'Is all of', + }, + ]; + } + + private buildByIdMap(): Map { + return associateBy(this.getSupportedPolicyOperators(), (it) => it.id); + } + + private defaultPolicyOperatorConfig( + operator: OperatorDto, + ): PolicyOperatorConfig { + return { + id: operator, + title: operator, + description: '', + }; + } +} diff --git a/connector-ui/src/app/shared/business/policy-editor/model/policy-verbs.ts b/connector-ui/src/app/shared/business/policy-editor/model/policy-verbs.ts new file mode 100644 index 000000000..013ec73d1 --- /dev/null +++ b/connector-ui/src/app/shared/business/policy-editor/model/policy-verbs.ts @@ -0,0 +1,89 @@ +import {Injectable} from '@angular/core'; +import {TranslateService} from '@ngx-translate/core'; +import {OperatorDto} from '@sovity.de/edc-client'; +import {LazyTranslation} from '../../../../core/utils/lazy-utils'; +import {associateBy} from '../../../../core/utils/map-utils'; +import { + PolicyFormAdapter, + jsonAdapter, + localDateAdapter, + stringArrayOrCommaJoinedAdapter, +} from './policy-form-adapter'; +import {policyLeftExpressions} from './policy-left-expressions'; +import {PolicyOperatorService} from './policy-operators'; + +export interface PolicyVerbConfig { + operandLeftId: string; + operandLeftTitle: string; + operandLeftDescription: string; + operandRightType: 'DATE' | 'TEXT' | 'PARTICIPANT_ID'; + operandRightHint?: string; + operandRightPlaceholder?: string; + supportedOperators: OperatorDto[]; + adapter: PolicyFormAdapter; +} + +@Injectable() +export class PolicyVerbService { + byId: LazyTranslation>; + + constructor( + private translateService: TranslateService, + private policyOperatorService: PolicyOperatorService, + ) { + this.byId = new LazyTranslation(this.translateService, () => + this.buildByIdMap(), + ); + } + + getVerbConfig(verb: string): PolicyVerbConfig { + return this.byId.getValue().get(verb) ?? this.getFallbackVerbConfig(verb); + } + + getFallbackVerbConfig(verb: string): PolicyVerbConfig { + return { + operandLeftId: verb, + operandLeftTitle: verb, + operandLeftDescription: '', + supportedOperators: this.policyOperatorService + .getSupportedPolicyOperators() + .map((it) => it.id), + operandRightType: 'TEXT', + adapter: jsonAdapter, + }; + } + + getSupportedPolicyVerbs(): PolicyVerbConfig[] { + return [ + { + operandLeftId: policyLeftExpressions.referringConnector, + operandLeftTitle: "Consumer's Participant ID", + operandLeftDescription: + "Consumer's Participant ID, also called Connector ID, of the counter-party connector.", + operandRightType: 'PARTICIPANT_ID', + supportedOperators: ['EQ', 'IN'], + operandRightPlaceholder: 'MDSL1234XX.C1234YY', + operandRightHint: 'Multiple values can be joined by comma', + adapter: stringArrayOrCommaJoinedAdapter, + }, + { + operandLeftId: policyLeftExpressions.policyEvaluationTime, + operandLeftTitle: 'Time Restriction', + operandLeftDescription: + 'Time at which the policy is evaluated. This can be used to restrict the data offer to certain time periods', + supportedOperators: ['GEQ', 'LEQ', 'GT', 'LT'], + operandRightType: 'DATE', + operandRightPlaceholder: 'DD/MM/YYYY', + operandRightHint: 'DD/MM/YYYY', + adapter: localDateAdapter, + }, + ]; + } + + private buildByIdMap(): Map { + return associateBy( + this.getSupportedPolicyVerbs(), + (it) => it.operandLeftId, + ); + } +} diff --git a/connector-ui/src/app/shared/business/policy-editor/model/tree.ts b/connector-ui/src/app/shared/business/policy-editor/model/tree.ts new file mode 100644 index 000000000..e04e27e4c --- /dev/null +++ b/connector-ui/src/app/shared/business/policy-editor/model/tree.ts @@ -0,0 +1,162 @@ +/** + * Tree data structure with a generic value type. + * + * The tree is mutable, but the TreeNode structure inside is immutable for better + * change detection. + */ +export class Tree { + constructor(public root: TreeNode, private _nextId: number) {} + + remove(path: string[]): void { + this.transform((node) => { + if (this.isEqualPath(node, path)) { + return null; + } + return node; + }); + } + + replaceTree( + path: string[], + value: I, + generatorFn: TreeGeneratorFn, + ): TreeNode { + const parentPath = path.slice(0, -1); + + const newNode = Tree.recursiveFoldNodes({ + parentPath, + original: value, + generatorFn, + idFactory: () => this.nextId(), + }); + + this.transform((node) => { + if (!this.isEqualPath(node, path)) { + return node; + } + + return newNode; + }); + + return newNode; + } + + push(parentPath: string[], value: T): TreeNode { + const id = this.nextId(); + const newNode: TreeNode = { + id, + path: [...parentPath, id], + value: value, + children: [], + }; + this.pushNode(parentPath, newNode); + return newNode; + } + + pushTree( + parentPath: string[], + value: I, + generatorFn: TreeGeneratorFn, + ): TreeNode { + const newNode = Tree.recursiveFoldNodes({ + parentPath, + original: value, + generatorFn, + idFactory: () => this.nextId(), + }); + this.pushNode(parentPath, newNode); + return newNode; + } + + private pushNode(parentPath: string[], newNode: TreeNode): void { + this.transform((node) => { + if (!this.isEqualPath(node, parentPath)) { + return node; + } + + return { + ...node, + children: [...node.children, newNode], + }; + }); + } + + private isEqualPath(node: TreeNode, path: string[]) { + return node.path.join('.') === path.join('.'); + } + + private transform(fn: (node: TreeNode) => TreeNode | null): void { + const transformNode = (node: TreeNode): TreeNode | null => { + const transformed = fn(node); + if (!transformed) { + return null; + } + return { + ...transformed, + children: transformed.children + .map(transformNode) + .filter((it) => it != null) as TreeNode[], + }; + }; + this.root = transformNode(this.root)!; + } + + private nextId() { + return String(this._nextId++); + } + + static ofTreeLikeStructure(opts: { + root: T; + generatorFn: TreeGeneratorFn; + }): Tree { + let currentId = 0; + const nextId = () => String(currentId++); + const rootNode = Tree.recursiveFoldNodes({ + parentPath: [], + original: opts.root, + generatorFn: opts.generatorFn, + idFactory: nextId, + }); + return new Tree(rootNode, currentId); + } + + private static recursiveFoldNodes(opts: { + parentPath: string[]; + original: T; + generatorFn: TreeGeneratorFn; + idFactory: () => string; + }): TreeNode { + const id = opts.idFactory(); + const {value, children} = opts.generatorFn(opts.original, id); + const path = [...opts.parentPath, id]; + const childrenMapped = children.map((child) => + Tree.recursiveFoldNodes({ + parentPath: path, + original: child, + generatorFn: opts.generatorFn, + idFactory: opts.idFactory, + }), + ); + return { + id, + path, + value, + children: childrenMapped, + }; + } +} + +export interface TreeNode { + path: string[]; + id: string; + value: T; + children: TreeNode[]; +} + +/** + * Mapper between a tree-like structure a dedicated TreeNode value type + */ +export type TreeGeneratorFn = ( + value: T, + id: string, +) => {value: R; children: T[]}; diff --git a/connector-ui/src/app/shared/business/policy-editor/model/ui-policy-expression-utils.ts b/connector-ui/src/app/shared/business/policy-editor/model/ui-policy-expression-utils.ts new file mode 100644 index 000000000..871537f1e --- /dev/null +++ b/connector-ui/src/app/shared/business/policy-editor/model/ui-policy-expression-utils.ts @@ -0,0 +1,39 @@ +import { + OperatorDto, + UiPolicyExpression, + UiPolicyExpressionType, +} from '@sovity.de/edc-client'; + +export const constraint = ( + left: string, + operator: OperatorDto, + value: string, +): UiPolicyExpression => ({ + type: 'CONSTRAINT', + constraint: { + left, + operator, + right: {type: 'STRING', value}, + }, +}); + +export const constraintList = ( + left: string, + operator: OperatorDto, + valueList: string[], +): UiPolicyExpression => ({ + type: 'CONSTRAINT', + constraint: { + left, + operator, + right: {type: 'STRING_LIST', valueList}, + }, +}); + +export const multi = ( + type: Exclude, + ...expressions: UiPolicyExpression[] +): UiPolicyExpression => ({ + type, + expressions, +}); diff --git a/connector-ui/src/app/shared/business/policy-editor/renderer/policy-expression/policy-expression.component.html b/connector-ui/src/app/shared/business/policy-editor/renderer/policy-expression/policy-expression.component.html new file mode 100644 index 000000000..e1572b8d2 --- /dev/null +++ b/connector-ui/src/app/shared/business/policy-editor/renderer/policy-expression/policy-expression.component.html @@ -0,0 +1,50 @@ + +
Unrestricted
+ + +
+
+ {{ expression.multiExpression!.title }} +
+
+
+ + +
+
+
+ + +
+ + {{ expression.verb!.operandLeftTitle }} + + + + {{ expression.operator!.title }} + + + {{ + expression.displayValue + }} +
diff --git a/connector-ui/src/app/shared/business/policy-editor/renderer/policy-expression/policy-expression.component.ts b/connector-ui/src/app/shared/business/policy-editor/renderer/policy-expression/policy-expression.component.ts new file mode 100644 index 000000000..c709af69a --- /dev/null +++ b/connector-ui/src/app/shared/business/policy-editor/renderer/policy-expression/policy-expression.component.ts @@ -0,0 +1,16 @@ +import {Component, HostBinding, Input} from '@angular/core'; +import {PolicyExpressionMapped} from '../../model/policy-expression-mapped'; + +@Component({ + selector: 'policy-expression', + templateUrl: './policy-expression.component.html', +}) +export class PolicyExpressionComponent { + @HostBinding('class.flex') + @HostBinding('class.flex-col') + @HostBinding('class.justify-stretch') + cls = true; + + @Input() + expression!: PolicyExpressionMapped; +} diff --git a/connector-ui/src/app/shared/business/policy-editor/renderer/policy-renderer/policy-renderer.component.html b/connector-ui/src/app/shared/business/policy-editor/renderer/policy-renderer/policy-renderer.component.html new file mode 100644 index 000000000..7d3c0a116 --- /dev/null +++ b/connector-ui/src/app/shared/business/policy-editor/renderer/policy-renderer/policy-renderer.component.html @@ -0,0 +1,6 @@ +
+
+ {{ error }} +
+
+ diff --git a/connector-ui/src/app/shared/business/policy-editor/renderer/policy-renderer/policy-renderer.component.ts b/connector-ui/src/app/shared/business/policy-editor/renderer/policy-renderer/policy-renderer.component.ts new file mode 100644 index 000000000..42b0929e5 --- /dev/null +++ b/connector-ui/src/app/shared/business/policy-editor/renderer/policy-renderer/policy-renderer.component.ts @@ -0,0 +1,14 @@ +import {Component, Input} from '@angular/core'; +import {PolicyExpressionMapped} from '../../model/policy-expression-mapped'; + +@Component({ + selector: 'app-policy-renderer', + templateUrl: './policy-renderer.component.html', +}) +export class PolicyRendererComponent { + @Input() + expression!: PolicyExpressionMapped; + + @Input() + errors: string[] = []; +} diff --git a/connector-ui/src/app/shared/business/transfer-history-mini-list/transfer-history-mini-list.component.html b/connector-ui/src/app/shared/business/transfer-history-mini-list/transfer-history-mini-list.component.html new file mode 100644 index 000000000..c343688fe --- /dev/null +++ b/connector-ui/src/app/shared/business/transfer-history-mini-list/transfer-history-mini-list.component.html @@ -0,0 +1,49 @@ + + {{ 'component_library.no_transfer' | translate }} + +
+ {{ + contractAgreement!.direction === 'PROVIDING' ? 'upload' : 'download' + }} + +
+
+ + + + · + + + {{ transfer.state.name }} + {{ + transfer.state.name === 'CUSTOM' + ? ' (' + transfer.state.code + ')' + : '' + }} + + + + warning + + + + +
+ + + {{ transfer.transferProcessId }} +
+
diff --git a/connector-ui/src/app/shared/business/transfer-history-mini-list/transfer-history-mini-list.component.ts b/connector-ui/src/app/shared/business/transfer-history-mini-list/transfer-history-mini-list.component.ts new file mode 100644 index 000000000..cc4c92d49 --- /dev/null +++ b/connector-ui/src/app/shared/business/transfer-history-mini-list/transfer-history-mini-list.component.ts @@ -0,0 +1,16 @@ +import {Component, HostBinding, Input} from '@angular/core'; +import {ContractAgreementCardMapped} from '../../../routes/connector-ui/contract-agreement-page/contract-agreement-cards/contract-agreement-card-mapped'; + +@Component({ + selector: 'transfer-history-mini-list', + templateUrl: 'transfer-history-mini-list.component.html', +}) +export class TransferHistoryMiniListComponent { + @Input() + contractAgreement!: ContractAgreementCardMapped; + + @HostBinding('class.flex') + @HostBinding('class.flex-col') + @HostBinding('class.space-y-[10px]') + cls = true; +} diff --git a/connector-ui/src/app/shared/common/ago/ago.component.ts b/connector-ui/src/app/shared/common/ago/ago.component.ts new file mode 100644 index 000000000..879656d9e --- /dev/null +++ b/connector-ui/src/app/shared/common/ago/ago.component.ts @@ -0,0 +1,18 @@ +import {Component, Input} from '@angular/core'; + +/** + * Displays a date as estimated relative time (e.g. "3 days ago"). + * + * But also shows the full date as a tooltip. + */ +@Component({ + selector: 'ago', + template: `{{ date | ago | async }}`, +}) +export class AgoComponent { + @Input() + date?: Date | null; +} diff --git a/connector-ui/src/app/shared/common/ago/ago.pipe.ts b/connector-ui/src/app/shared/common/ago/ago.pipe.ts new file mode 100644 index 000000000..3b332b436 --- /dev/null +++ b/connector-ui/src/app/shared/common/ago/ago.pipe.ts @@ -0,0 +1,22 @@ +import {Pipe, PipeTransform} from '@angular/core'; +import {Observable, concat, interval, of} from 'rxjs'; +import {distinctUntilChanged, map} from 'rxjs/operators'; +import {TranslateService} from '@ngx-translate/core'; +import {formatDateAgo} from './formatDateAgo'; + +/** + * Displays a date as estimated relative time (e.g. "3 days ago"). + */ +@Pipe({name: 'ago'}) +export class AgoPipe implements PipeTransform { + interval$ = concat(of({}), interval(3000)); + + constructor(private translateService: TranslateService) {} + + transform(date?: Date | null): Observable { + return this.interval$.pipe( + map(() => formatDateAgo(date, this.translateService.currentLang)), + distinctUntilChanged(), + ); + } +} diff --git a/connector-ui/src/app/shared/common/ago/date-input.ts b/connector-ui/src/app/shared/common/ago/date-input.ts new file mode 100644 index 000000000..2f6676956 --- /dev/null +++ b/connector-ui/src/app/shared/common/ago/date-input.ts @@ -0,0 +1 @@ +export type DateInput = Date | string; diff --git a/connector-ui/src/app/shared/common/ago/formatDateAgo.ts b/connector-ui/src/app/shared/common/ago/formatDateAgo.ts new file mode 100644 index 000000000..21f070eb1 --- /dev/null +++ b/connector-ui/src/app/shared/common/ago/formatDateAgo.ts @@ -0,0 +1,21 @@ +import {formatDistanceToNow} from 'date-fns'; +import {de, enUS} from 'date-fns/locale'; +import {DateInput} from './date-input'; + +/** + * Formats date as "{n} {timeUnit} ago" or "in {n} {timeUnit}s". + * @param date date + */ +export function formatDateAgo( + date?: DateInput | null, + locale?: string, +): string { + if (!date) { + return 'never'; + } + if (typeof date === 'string') { + date = new Date(date); + } + const localeDateNfs = locale === 'de' ? de : enUS; + return formatDistanceToNow(date, {addSuffix: true, locale: localeDateNfs}); +} diff --git a/connector-ui/src/app/shared/common/confirmation-dialog/confirmation-dialog.component.html b/connector-ui/src/app/shared/common/confirmation-dialog/confirmation-dialog.component.html new file mode 100644 index 000000000..890b080f6 --- /dev/null +++ b/connector-ui/src/app/shared/common/confirmation-dialog/confirmation-dialog.component.html @@ -0,0 +1,16 @@ +

+ {{ data.title }} +

+ +
+

{{ data.message }}

+
+ +
+ + +
diff --git a/connector-ui/src/app/shared/common/confirmation-dialog/confirmation-dialog.component.scss b/connector-ui/src/app/shared/common/confirmation-dialog/confirmation-dialog.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/connector-ui/src/app/shared/common/confirmation-dialog/confirmation-dialog.component.ts b/connector-ui/src/app/shared/common/confirmation-dialog/confirmation-dialog.component.ts new file mode 100644 index 000000000..caaa35d0f --- /dev/null +++ b/connector-ui/src/app/shared/common/confirmation-dialog/confirmation-dialog.component.ts @@ -0,0 +1,87 @@ +import {Component, Inject, OnInit} from '@angular/core'; +import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog'; +import {TranslateService} from '@ngx-translate/core'; + +@Component({ + selector: 'app-confirmation-dialog', + templateUrl: './confirmation-dialog.component.html', + styleUrls: ['./confirmation-dialog.component.scss'], +}) +export class ConfirmationDialogComponent implements OnInit { + constructor( + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: ConfirmDialogModel, + ) {} + + ngOnInit(): void {} + + onCancel() { + this.dialogRef.close(false); + } + + onConfirm() { + this.dialogRef.close(true); + } +} + +export class ConfirmDialogModel { + private _confirmText: string = 'OK'; + private _cancelText: string = 'Cancel'; + private _cancelColor: 'accent' | 'warn' | 'primary' | '' = ''; + private _confirmColor: 'accent' | 'warn' | 'primary' | '' = ''; + + constructor(public title: string, public message: string) {} + + get cancelColor(): 'accent' | 'warn' | 'primary' | '' { + return this._cancelColor; + } + + set cancelColor(value: 'accent' | 'warn' | 'primary' | '') { + this._cancelColor = value; + } + + get confirmColor(): 'accent' | 'warn' | 'primary' | '' { + return this._confirmColor; + } + + set confirmColor(value: 'accent' | 'warn' | 'primary' | '') { + this._confirmColor = value; + } + + get cancelText(): string { + return this._cancelText; + } + + set cancelText(value: string) { + this._cancelText = value; + } + + get confirmText(): string { + return this._confirmText; + } + + set confirmText(value: string) { + this._confirmText = value; + } + + public static forDelete( + typeKey: string, + identifier: string, + translateService: TranslateService, + ): ConfirmDialogModel { + const dialogData = new ConfirmDialogModel( + `${translateService.instant('component_library.delete_title')}`, + `${translateService.instant( + 'component_library.delete_one', + )} ${translateService.instant( + typeKey, + )} ${identifier}. ${translateService.instant( + 'component_library.delete_two', + )}`, + ); + dialogData.confirmColor = 'warn'; + dialogData.confirmText = translateService.instant('general.delete'); + dialogData.cancelText = translateService.instant('general.close'); + return dialogData; + } +} diff --git a/connector-ui/src/app/shared/common/date/date.component.ts b/connector-ui/src/app/shared/common/date/date.component.ts new file mode 100644 index 000000000..ca5791b00 --- /dev/null +++ b/connector-ui/src/app/shared/common/date/date.component.ts @@ -0,0 +1,12 @@ +import {Component, Input} from '@angular/core'; + +@Component({ + selector: 'date', + template: `{{ + date | date : 'yyyy-MM-dd' + }}`, +}) +export class DateComponent { + @Input() + date?: Date | null; +} diff --git a/connector-ui/src/app/shared/common/empty-state/empty-state.component.html b/connector-ui/src/app/shared/common/empty-state/empty-state.component.html new file mode 100644 index 000000000..a72c3677c --- /dev/null +++ b/connector-ui/src/app/shared/common/empty-state/empty-state.component.html @@ -0,0 +1,3 @@ +
+ {{ emptyMessage }} +
diff --git a/connector-ui/src/app/shared/common/empty-state/empty-state.component.ts b/connector-ui/src/app/shared/common/empty-state/empty-state.component.ts new file mode 100644 index 000000000..672c7fe48 --- /dev/null +++ b/connector-ui/src/app/shared/common/empty-state/empty-state.component.ts @@ -0,0 +1,18 @@ +import {Component, HostBinding, Input} from '@angular/core'; + +@Component({ + selector: 'empty-state', + templateUrl: './empty-state.component.html', +}) +export class EmptyStateComponent { + @HostBinding('class.flex') + @HostBinding('class.flex-col') + @HostBinding('class.justify-center') + @HostBinding('class.items-center') + @HostBinding('class.uppercase') + @HostBinding('class.text-slate') + cls = true; + + @Input() + emptyMessage = ''; +} diff --git a/connector-ui/src/app/shared/common/error-state/error-state.component.html b/connector-ui/src/app/shared/common/error-state/error-state.component.html new file mode 100644 index 000000000..cc1a84c9d --- /dev/null +++ b/connector-ui/src/app/shared/common/error-state/error-state.component.html @@ -0,0 +1,6 @@ +{{ + error?.failureIcon +}} +
+ {{ error?.failureMessage }} +
diff --git a/connector-ui/src/app/shared/common/error-state/error-state.component.ts b/connector-ui/src/app/shared/common/error-state/error-state.component.ts new file mode 100644 index 000000000..97f3b48fb --- /dev/null +++ b/connector-ui/src/app/shared/common/error-state/error-state.component.ts @@ -0,0 +1,17 @@ +import {Component, HostBinding, Input} from '@angular/core'; +import {FetchError} from '../../../core/services/models/fetched'; + +@Component({ + selector: 'error-state', + templateUrl: './error-state.component.html', +}) +export class ErrorStateComponent { + @HostBinding('class.flex') + @HostBinding('class.flex-col') + @HostBinding('class.justify-center') + @HostBinding('class.items-center') + cls = true; + + @Input() + error: FetchError | undefined; +} diff --git a/connector-ui/src/app/shared/common/horizontal-section-divider/horizontal-section-divider.component.html b/connector-ui/src/app/shared/common/horizontal-section-divider/horizontal-section-divider.component.html new file mode 100644 index 000000000..c26641fef --- /dev/null +++ b/connector-ui/src/app/shared/common/horizontal-section-divider/horizontal-section-divider.component.html @@ -0,0 +1,7 @@ +
+
+ {{ text }} +
+
diff --git a/connector-ui/src/app/shared/common/horizontal-section-divider/horizontal-section-divider.component.ts b/connector-ui/src/app/shared/common/horizontal-section-divider/horizontal-section-divider.component.ts new file mode 100644 index 000000000..660458bca --- /dev/null +++ b/connector-ui/src/app/shared/common/horizontal-section-divider/horizontal-section-divider.component.ts @@ -0,0 +1,14 @@ +import {Component, HostBinding, Input} from '@angular/core'; + +@Component({ + selector: 'horizontal-section-divider', + templateUrl: './horizontal-section-divider.component.html', +}) +export class HorizontalSectionDividerComponent { + @HostBinding('class.flex') + @HostBinding('class.items-center') + cls = true; + + @Input() + text: string = ''; +} diff --git a/connector-ui/src/app/shared/common/json-dialog/clean-json.ts b/connector-ui/src/app/shared/common/json-dialog/clean-json.ts new file mode 100644 index 000000000..91f3f6042 --- /dev/null +++ b/connector-ui/src/app/shared/common/json-dialog/clean-json.ts @@ -0,0 +1,12 @@ +import cleanDeep from 'clean-deep'; +import jsonStableStringify from 'json-stable-stringify'; + +/** + * Sorts keys, sorts array values, removes emtpy keys. + * + * @param json any JSON object + */ +export function cleanJson(json: T): Partial { + const cleaned = cleanDeep(json, {emptyStrings: false}); + return JSON.parse(jsonStableStringify(cleaned)); +} diff --git a/connector-ui/src/app/shared/common/json-dialog/json-dialog.component.html b/connector-ui/src/app/shared/common/json-dialog/json-dialog.component.html new file mode 100644 index 000000000..d951c326d --- /dev/null +++ b/connector-ui/src/app/shared/common/json-dialog/json-dialog.component.html @@ -0,0 +1,58 @@ +
+
+ + {{ data.icon }} + +
+
+ {{ data.title }} +
+
+ {{ data.subtitle }} +
+
+
+ +
+ +
+ +
+ +
+
+ + {{ 'component_library.json' | translate }} + +
+
+ +
+
diff --git a/connector-ui/src/app/shared/common/json-dialog/json-dialog.component.ts b/connector-ui/src/app/shared/common/json-dialog/json-dialog.component.ts new file mode 100644 index 000000000..fa37b260c --- /dev/null +++ b/connector-ui/src/app/shared/common/json-dialog/json-dialog.component.ts @@ -0,0 +1,92 @@ +import { + AfterViewInit, + Component, + ElementRef, + Inject, + OnDestroy, + OnInit, + ViewChild, +} from '@angular/core'; +import { + MAT_DIALOG_DATA, + MatDialog, + MatDialogRef, +} from '@angular/material/dialog'; +import {Subject} from 'rxjs'; +import {filter, finalize, takeUntil} from 'rxjs/operators'; +import {NgxJsonViewerComponent} from 'ngx-json-viewer'; +import {ConfirmationDialogComponent} from '../confirmation-dialog/confirmation-dialog.component'; +import {cleanJson} from './clean-json'; +import {DialogToolbarButton, JsonDialogData} from './json-dialog.data'; + +@Component({ + selector: 'app-json-dialog', + templateUrl: './json-dialog.component.html', +}) +export class JsonDialogComponent implements OnInit, AfterViewInit, OnDestroy { + busy = false; + + removeNulls = true; + + visibleJson: unknown = {}; + + @ViewChild(NgxJsonViewerComponent, {read: ElementRef}) + jsonViewer!: ElementRef; + + constructor( + public dialogRef: MatDialogRef, + private matDialog: MatDialog, + @Inject(MAT_DIALOG_DATA) public data: JsonDialogData, + ) {} + + ngOnInit() { + this.updateVisibleJson(); + } + + ngAfterViewInit() { + this.jsonViewer.nativeElement.scrollIntoView(); + } + + updateVisibleJson() { + this.visibleJson = this.removeNulls + ? cleanJson(this.data.objectForJson) + : this.data.objectForJson; + } + + onAction(button: DialogToolbarButton) { + if (button.confirmation) { + const ref = this.matDialog.open(ConfirmationDialogComponent, { + maxWidth: '20%', + data: button.confirmation, + }); + + ref + .afterClosed() + .pipe(filter((it) => it)) + .subscribe(() => this.doAction(button)); + } else { + this.doAction(button); + } + } + + doAction(button: DialogToolbarButton) { + if (this.busy) { + return; + } + this.busy = true; + button + .action() + .pipe( + finalize(() => (this.busy = false)), + takeUntil(this.ngOnDestroy$), + ) + .subscribe(); + } + + ngOnDestroy$ = new Subject(); + + ngOnDestroy(): void { + this.ngOnDestroy$.next(null); + this.ngOnDestroy$.complete(); + } +} diff --git a/connector-ui/src/app/shared/common/json-dialog/json-dialog.data.ts b/connector-ui/src/app/shared/common/json-dialog/json-dialog.data.ts new file mode 100644 index 000000000..00f5abe87 --- /dev/null +++ b/connector-ui/src/app/shared/common/json-dialog/json-dialog.data.ts @@ -0,0 +1,17 @@ +import {Observable} from 'rxjs'; +import {ConfirmDialogModel} from '../confirmation-dialog/confirmation-dialog.component'; + +export interface JsonDialogData { + title: string; + subtitle: string; + icon: string; + objectForJson: unknown; + toolbarButton?: DialogToolbarButton; +} + +export interface DialogToolbarButton { + text: string; + icon: string; + action: () => Observable | any; + confirmation?: ConfirmDialogModel; +} diff --git a/connector-ui/src/app/shared/common/json-dialog/json-dialog.service.ts b/connector-ui/src/app/shared/common/json-dialog/json-dialog.service.ts new file mode 100644 index 000000000..65b54a61e --- /dev/null +++ b/connector-ui/src/app/shared/common/json-dialog/json-dialog.service.ts @@ -0,0 +1,28 @@ +import {Injectable} from '@angular/core'; +import {MatDialog} from '@angular/material/dialog'; +import {NEVER, Observable} from 'rxjs'; +import {showDialogUntil} from '../../../core/utils/mat-dialog-utils'; +import {JsonDialogComponent} from './json-dialog.component'; +import {JsonDialogData} from './json-dialog.data'; + +@Injectable() +export class JsonDialogService { + constructor(private dialog: MatDialog) {} + + /** + * Shows JSON Detail Dialog until until$ emits / completes + * @param data json detail dialog data + * @param until$ observable that controls the lifetime of the dialog + */ + showJsonDetailDialog( + data: JsonDialogData, + until$: Observable = NEVER, + ): Observable { + return showDialogUntil( + this.dialog, + JsonDialogComponent, + {data, autoFocus: 'first-tabbable'}, + until$, + ); + } +} diff --git a/connector-ui/src/app/shared/common/language-selector/language-selector.component.html b/connector-ui/src/app/shared/common/language-selector/language-selector.component.html new file mode 100644 index 000000000..9137fcba1 --- /dev/null +++ b/connector-ui/src/app/shared/common/language-selector/language-selector.component.html @@ -0,0 +1,25 @@ + + + + + diff --git a/connector-ui/src/app/shared/common/language-selector/language-selector.component.ts b/connector-ui/src/app/shared/common/language-selector/language-selector.component.ts new file mode 100644 index 000000000..eb2da2ad9 --- /dev/null +++ b/connector-ui/src/app/shared/common/language-selector/language-selector.component.ts @@ -0,0 +1,37 @@ +import {Component, OnInit} from '@angular/core'; +import {TranslateService} from '@ngx-translate/core'; +import { + AvailableLanguage, + isLanguageSupported, + supportedLanguages, +} from 'src/app/core/utils/i18n-utils'; +import {LocalStoredValue} from '../../../core/utils/local-stored-value'; + +@Component({ + selector: 'app-language-selector', + templateUrl: './language-selector.component.html', +}) +export class LanguageSelectorComponent implements OnInit { + selectedLanguage = new LocalStoredValue( + 'en', + 'selectedLanguage', + isLanguageSupported, + ); + + supportedLanguages = supportedLanguages; + + constructor(private translateService: TranslateService) {} + + ngOnInit(): void { + this.updateSelectedLanguage(); + } + + setSelectedLanguage(language: AvailableLanguage) { + this.selectedLanguage.value = language.code; + this.updateSelectedLanguage(); + } + + updateSelectedLanguage() { + this.translateService.use(this.selectedLanguage.value); + } +} diff --git a/connector-ui/src/app/shared/common/loading-state/loading-state.component.html b/connector-ui/src/app/shared/common/loading-state/loading-state.component.html new file mode 100644 index 000000000..fc8f828b5 --- /dev/null +++ b/connector-ui/src/app/shared/common/loading-state/loading-state.component.html @@ -0,0 +1,6 @@ + + diff --git a/connector-ui/src/app/shared/common/loading-state/loading-state.component.ts b/connector-ui/src/app/shared/common/loading-state/loading-state.component.ts new file mode 100644 index 000000000..3f3bc162d --- /dev/null +++ b/connector-ui/src/app/shared/common/loading-state/loading-state.component.ts @@ -0,0 +1,13 @@ +import {Component, HostBinding} from '@angular/core'; + +@Component({ + selector: 'loading-state', + templateUrl: './loading-state.component.html', +}) +export class LoadingStateComponent { + @HostBinding('class.flex') + @HostBinding('class.flex-col') + @HostBinding('class.justify-center') + @HostBinding('class.items-center') + cls = true; +} diff --git a/connector-ui/src/app/shared/common/markdown-description/markdown-description.component.html b/connector-ui/src/app/shared/common/markdown-description/markdown-description.component.html new file mode 100644 index 000000000..a97a6d460 --- /dev/null +++ b/connector-ui/src/app/shared/common/markdown-description/markdown-description.component.html @@ -0,0 +1,46 @@ +
+
+ + {{ + 'component_library.no_description' | translate + }} + +
+
+ + diff --git a/connector-ui/src/app/shared/common/markdown-description/markdown-description.component.ts b/connector-ui/src/app/shared/common/markdown-description/markdown-description.component.ts new file mode 100644 index 000000000..052f1f962 --- /dev/null +++ b/connector-ui/src/app/shared/common/markdown-description/markdown-description.component.ts @@ -0,0 +1,70 @@ +import { + AfterViewInit, + ChangeDetectorRef, + Component, + ElementRef, + HostBinding, + Input, + OnChanges, + OnInit, + ViewChild, +} from '@angular/core'; +import {HtmlSanitizer} from 'src/app/core/services/html-sanitizer'; +import {MarkdownConverter} from 'src/app/core/services/markdown-converter'; +import {SimpleChangesTyped} from '../../../core/utils/angular-utils'; + +const COLLAPSED_DESCRIPTION_HEIGHT = 280; + +@Component({ + selector: 'markdown-description', + templateUrl: './markdown-description.component.html', +}) +export class MarkdownDescriptionComponent + implements OnInit, OnChanges, AfterViewInit +{ + @HostBinding('class.block') cls = true; + @Input() description: string | undefined; + @ViewChild('content') elementView!: ElementRef; + isLargeDescription = false; + collapsedDescriptionHeight!: number; + + get isCollapsed(): boolean { + return this.isLargeDescription && this.collapsed; + } + + private collapsed = true; + private isAfterViewInit = false; + + constructor( + private cd: ChangeDetectorRef, + public markdownConverter: MarkdownConverter, + public htmlSanitizer: HtmlSanitizer, + ) {} + + ngOnInit(): void { + this.collapsedDescriptionHeight = COLLAPSED_DESCRIPTION_HEIGHT; + } + + ngOnChanges(changes: SimpleChangesTyped) { + if (changes.description && this.isAfterViewInit) { + // We need to wait for the changes to apply first. + // setTimeout(..., 0) appends the task to the end of the microtask queue + setTimeout(() => this.recalculateShowMore(), 0); + } + } + + ngAfterViewInit() { + this.isAfterViewInit = true; + this.recalculateShowMore(); + this.cd.detectChanges(); + } + + onToggleShowMore() { + this.collapsed = !this.collapsed; + } + + private recalculateShowMore() { + const contentHeight = this.elementView.nativeElement.offsetHeight; + this.isLargeDescription = contentHeight > this.collapsedDescriptionHeight; + } +} diff --git a/connector-ui/src/app/shared/common/property-grid-group/property-grid-group.component.html b/connector-ui/src/app/shared/common/property-grid-group/property-grid-group.component.html new file mode 100644 index 000000000..7d2ee5de2 --- /dev/null +++ b/connector-ui/src/app/shared/common/property-grid-group/property-grid-group.component.html @@ -0,0 +1,15 @@ + + +
+ {{ propGroup.groupLabel }} +
+ +
diff --git a/connector-ui/src/app/shared/common/property-grid-group/property-grid-group.component.ts b/connector-ui/src/app/shared/common/property-grid-group/property-grid-group.component.ts new file mode 100644 index 000000000..21fde5b1d --- /dev/null +++ b/connector-ui/src/app/shared/common/property-grid-group/property-grid-group.component.ts @@ -0,0 +1,19 @@ +import {Component, HostBinding, Input} from '@angular/core'; +import {PropertyGridGroup} from './property-grid-group'; + +@Component({ + selector: 'property-grid-group', + templateUrl: './property-grid-group.component.html', +}) +export class PropertyGridGroupComponent { + @Input() + propGroups: PropertyGridGroup[] = []; + + @Input() + columns: number = 3; + + @HostBinding('class.flex') + @HostBinding('class.flex-col') + @HostBinding('class.justify-start') + cls = true; +} diff --git a/connector-ui/src/app/shared/common/property-grid-group/property-grid-group.ts b/connector-ui/src/app/shared/common/property-grid-group/property-grid-group.ts new file mode 100644 index 000000000..0588b938a --- /dev/null +++ b/connector-ui/src/app/shared/common/property-grid-group/property-grid-group.ts @@ -0,0 +1,6 @@ +import {PropertyGridField} from '../property-grid/property-grid-field'; + +export interface PropertyGridGroup { + groupLabel: string | null; + properties: PropertyGridField[]; +} diff --git a/connector-ui/src/app/shared/common/property-grid/property-grid-field.service.ts b/connector-ui/src/app/shared/common/property-grid/property-grid-field.service.ts new file mode 100644 index 000000000..c14dd4bb3 --- /dev/null +++ b/connector-ui/src/app/shared/common/property-grid/property-grid-field.service.ts @@ -0,0 +1,35 @@ +import {formatDate} from '@angular/common'; +import {Inject, Injectable, LOCALE_ID} from '@angular/core'; +import {validUrlPattern} from '../../../core/validators/url-validator'; +import {PropertyGridField} from './property-grid-field'; + +@Injectable({providedIn: 'root'}) +export class PropertyGridFieldService { + constructor(@Inject(LOCALE_ID) private locale: string) {} + + guessValue( + value: string | null | undefined, + ): Pick { + return { + text: value || '-', + url: value?.match(validUrlPattern) ? value : undefined, + additionalClasses: value?.includes(' ') ? undefined : 'break-all', + }; + } + + formatDateWithTime(date: Date | null | undefined): string { + if (!date) { + return ''; + } + + return formatDate(date, 'dd/MM/yyyy HH:mm:ss', this.locale); + } + + formatDate(date: Date | null | undefined): string { + if (!date) { + return ''; + } + + return formatDate(date, 'dd/MM/yyyy', this.locale); + } +} diff --git a/connector-ui/src/app/shared/common/property-grid/property-grid-field.ts b/connector-ui/src/app/shared/common/property-grid/property-grid-field.ts new file mode 100644 index 000000000..703024439 --- /dev/null +++ b/connector-ui/src/app/shared/common/property-grid/property-grid-field.ts @@ -0,0 +1,50 @@ +import {PolicyExpressionMapped} from '../../business/policy-editor/model/policy-expression-mapped'; + +export interface PropertyGridField { + icon: string; + + /** + * Title of Property + */ + label: string; + + /** + * Adds "title"-Attribute to Label HTML Element + */ + labelTitle?: string; + + /** + * Property Value + */ + text?: string; + + url?: string; + onclick?: () => void; + + /** + * Additional classes for the value text. + */ + additionalClasses?: string; + + /** + * Additional classes for the container + */ + additionalContainerClasses?: string; + + /** + * Additional classes for the icon. + */ + additionalIconClasses?: string; + + copyButton?: boolean; + tooltip?: string | null; + textIconAfter?: string | null; + + policy?: PolicyExpressionMapped; + policyErrors?: string[]; + + /** + * Hide text + */ + hideFieldValue?: boolean; +} diff --git a/connector-ui/src/app/shared/common/property-grid/property-grid.component.html b/connector-ui/src/app/shared/common/property-grid/property-grid.component.html new file mode 100644 index 000000000..9d89d46b2 --- /dev/null +++ b/connector-ui/src/app/shared/common/property-grid/property-grid.component.html @@ -0,0 +1,77 @@ + +
+ + + {{ prop.icon }} + +
+ +
+ {{ prop.label }} +
+ + Show {{ prop.label }} + + + + + + {{ prop.text }} + + {{ prop.text }} + + + {{ prop.text }} + + {{ prop.textIconAfter }} + + + +
+ +
diff --git a/connector-ui/src/app/shared/common/property-grid/property-grid.component.ts b/connector-ui/src/app/shared/common/property-grid/property-grid.component.ts new file mode 100644 index 000000000..bed8a0e6e --- /dev/null +++ b/connector-ui/src/app/shared/common/property-grid/property-grid.component.ts @@ -0,0 +1,20 @@ +import {Component, HostBinding, Input, TrackByFunction} from '@angular/core'; +import {PropertyGridField} from './property-grid-field'; + +@Component({ + selector: 'property-grid', + templateUrl: './property-grid.component.html', +}) +export class PropertyGridComponent { + @Input() + props: PropertyGridField[] = []; + + @Input() + columns: number = 3; + @HostBinding('class.grid') + @HostBinding('class.grid-cols-3') + @HostBinding('class.gap-[10px]') + cls = true; + + trackByIndex: TrackByFunction = (index: number) => index; +} diff --git a/connector-ui/src/app/shared/common/translate-with-slot/translate-with-slot.component.html b/connector-ui/src/app/shared/common/translate-with-slot/translate-with-slot.component.html new file mode 100644 index 000000000..e016fac12 --- /dev/null +++ b/connector-ui/src/app/shared/common/translate-with-slot/translate-with-slot.component.html @@ -0,0 +1,7 @@ +{{ textBefore }} + + + + +{{ textAfter }} + diff --git a/connector-ui/src/app/shared/common/translate-with-slot/translate-with-slot.component.ts b/connector-ui/src/app/shared/common/translate-with-slot/translate-with-slot.component.ts new file mode 100644 index 000000000..f84f543df --- /dev/null +++ b/connector-ui/src/app/shared/common/translate-with-slot/translate-with-slot.component.ts @@ -0,0 +1,54 @@ +import {Component, Input, OnChanges, OnDestroy} from '@angular/core'; +import {Subject, switchMap} from 'rxjs'; +import {takeUntil} from 'rxjs/operators'; +import {TranslateService} from '@ngx-translate/core'; +import {SimpleChangesTyped} from '../../../core/utils/angular-utils'; + +@Component({ + selector: 'translate-with-slot', + templateUrl: './translate-with-slot.component.html', +}) +export class TranslateWithSlotComponent implements OnChanges, OnDestroy { + @Input() + key!: string; + + @Input() + html = false; + + key$ = new Subject(); + + textBefore = ''; + hasMiddle = false; + textAfter = ''; + + constructor(private translationService: TranslateService) { + this.splitText(); + } + + splitText() { + this.key$ + .pipe( + switchMap(() => this.translationService.stream(this.key)), + takeUntil(this.ngOnDestroy$), + ) + .subscribe((text) => { + const parts = text.split('{}'); + this.textBefore = parts[0]; + this.hasMiddle = parts.length > 1; + this.textAfter = parts[1] || ''; + }); + } + + ngOnChanges(changes: SimpleChangesTyped) { + if (changes.key) { + this.key$.next(changes.key); + } + } + + ngOnDestroy$ = new Subject(); + + ngOnDestroy() { + this.ngOnDestroy$.next(null); + this.ngOnDestroy$.complete(); + } +} diff --git a/connector-ui/src/app/shared/common/truncated-short-description/truncated-short-description.component.html b/connector-ui/src/app/shared/common/truncated-short-description/truncated-short-description.component.html new file mode 100644 index 000000000..ad639bb03 --- /dev/null +++ b/connector-ui/src/app/shared/common/truncated-short-description/truncated-short-description.component.html @@ -0,0 +1 @@ +{{ text || ('component_library.no_description' | translate) }} diff --git a/connector-ui/src/app/shared/common/truncated-short-description/truncated-short-description.component.ts b/connector-ui/src/app/shared/common/truncated-short-description/truncated-short-description.component.ts new file mode 100644 index 000000000..c9f055643 --- /dev/null +++ b/connector-ui/src/app/shared/common/truncated-short-description/truncated-short-description.component.ts @@ -0,0 +1,16 @@ +import {Component, HostBinding, Input} from '@angular/core'; + +@Component({ + selector: 'truncated-short-description', + templateUrl: './truncated-short-description.component.html', +}) +export class TruncatedShortDescription { + @Input() text!: string | undefined; + @HostBinding('class.whitespace-pre-line') + @HostBinding('class.line-clamp-5') + cls = true; + @HostBinding('class.italic') + get italic(): boolean { + return !this.text; + } +} diff --git a/connector-ui/src/app/shared/common/url-list-dialog/url-list-dialog.component.html b/connector-ui/src/app/shared/common/url-list-dialog/url-list-dialog.component.html new file mode 100644 index 000000000..9b29e28cc --- /dev/null +++ b/connector-ui/src/app/shared/common/url-list-dialog/url-list-dialog.component.html @@ -0,0 +1,42 @@ +
+
+ + {{ data.icon }} + +
+
+ {{ data.title }} +
+
+ {{ data.subtitle }} +
+
+
+
+ +
+
+
{{ 'general.description' | translate }}
+ +
+ +
+
URLs
+
    +
  1. + {{ url }} +
  2. +
+
+
+ +
+ +
diff --git a/connector-ui/src/app/shared/common/url-list-dialog/url-list-dialog.component.ts b/connector-ui/src/app/shared/common/url-list-dialog/url-list-dialog.component.ts new file mode 100644 index 000000000..1934dc154 --- /dev/null +++ b/connector-ui/src/app/shared/common/url-list-dialog/url-list-dialog.component.ts @@ -0,0 +1,14 @@ +import {Component, Inject} from '@angular/core'; +import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog'; +import {UrlListDialogData} from './url-list-dialog.data'; + +@Component({ + selector: 'app-json-dialog', + templateUrl: './url-list-dialog.component.html', +}) +export class UrlListDialogComponent { + constructor( + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: UrlListDialogData, + ) {} +} diff --git a/connector-ui/src/app/shared/common/url-list-dialog/url-list-dialog.data.ts b/connector-ui/src/app/shared/common/url-list-dialog/url-list-dialog.data.ts new file mode 100644 index 000000000..d4b5c4076 --- /dev/null +++ b/connector-ui/src/app/shared/common/url-list-dialog/url-list-dialog.data.ts @@ -0,0 +1,7 @@ +export interface UrlListDialogData { + title: string; + subtitle: string; + icon: string; + urls: string[]; + description?: string; +} diff --git a/connector-ui/src/app/shared/common/url-list-dialog/url-list-dialog.service.ts b/connector-ui/src/app/shared/common/url-list-dialog/url-list-dialog.service.ts new file mode 100644 index 000000000..091bd9c5f --- /dev/null +++ b/connector-ui/src/app/shared/common/url-list-dialog/url-list-dialog.service.ts @@ -0,0 +1,28 @@ +import {Injectable} from '@angular/core'; +import {MatDialog} from '@angular/material/dialog'; +import {NEVER, Observable} from 'rxjs'; +import {showDialogUntil} from '../../../core/utils/mat-dialog-utils'; +import {UrlListDialogComponent} from './url-list-dialog.component'; +import {UrlListDialogData} from './url-list-dialog.data'; + +@Injectable() +export class UrlListDialogService { + constructor(private dialog: MatDialog) {} + + /** + * Shows JSON Detail Dialog until until$ emits / completes + * @param data json detail dialog data + * @param until$ observable that controls the lifetime of the dialog + */ + showUrlListDialog( + data: UrlListDialogData, + until$: Observable = NEVER, + ): Observable { + return showDialogUntil( + this.dialog, + UrlListDialogComponent, + {data, autoFocus: false}, + until$, + ); + } +} diff --git a/connector-ui/src/app/shared/form-elements/data-address-type-select/data-address-type-select-item.ts b/connector-ui/src/app/shared/form-elements/data-address-type-select/data-address-type-select-item.ts new file mode 100644 index 000000000..98d4b3dfd --- /dev/null +++ b/connector-ui/src/app/shared/form-elements/data-address-type-select/data-address-type-select-item.ts @@ -0,0 +1,6 @@ +import {DataAddressType} from './data-address-type'; + +export interface DataAddressTypeSelectItem { + id: DataAddressType; + label: string; +} diff --git a/connector-ui/src/app/shared/form-elements/data-address-type-select/data-address-type-select-items.ts b/connector-ui/src/app/shared/form-elements/data-address-type-select/data-address-type-select-items.ts new file mode 100644 index 000000000..94191e27c --- /dev/null +++ b/connector-ui/src/app/shared/form-elements/data-address-type-select/data-address-type-select-items.ts @@ -0,0 +1,37 @@ +import {ActiveFeatureSet} from 'src/app/core/config/active-feature-set'; +import {DataAddressTypeSelectItem} from './data-address-type-select-item'; +import {DataAddressTypeSelectMode} from './data-address-type-select-mode'; + +export const dataAddressTypeSelectItems = ( + type: DataAddressTypeSelectMode, + activeFeatureSet: ActiveFeatureSet, +): DataAddressTypeSelectItem[] => { + const items: DataAddressTypeSelectItem[] = []; + + if (type.startsWith('Datasource') && activeFeatureSet.hasMdsFields()) { + items.push({ + id: 'On-Request', + label: '"On Request" Data Offer', + }); + } + + items.push( + { + id: 'Http', + label: 'REST-API Endpoint', + }, + { + id: 'Custom-Data-Address-Json', + label: `Custom ${type} Config (JSON)`, + }, + ); + + if (type === 'Datasink') { + items.push({ + id: 'Custom-Transfer-Process-Request', + label: 'Custom Transfer Process Request (JSON)', + }); + } + + return items; +}; diff --git a/connector-ui/src/app/shared/form-elements/data-address-type-select/data-address-type-select-mode.ts b/connector-ui/src/app/shared/form-elements/data-address-type-select/data-address-type-select-mode.ts new file mode 100644 index 000000000..2a9413fc7 --- /dev/null +++ b/connector-ui/src/app/shared/form-elements/data-address-type-select/data-address-type-select-mode.ts @@ -0,0 +1 @@ +export type DataAddressTypeSelectMode = 'Datasource-Create' | 'Datasink'; diff --git a/connector-ui/src/app/shared/form-elements/data-address-type-select/data-address-type-select.component.html b/connector-ui/src/app/shared/form-elements/data-address-type-select/data-address-type-select.component.html new file mode 100644 index 000000000..4785f7e1f --- /dev/null +++ b/connector-ui/src/app/shared/form-elements/data-address-type-select/data-address-type-select.component.html @@ -0,0 +1,9 @@ + + {{ label }} + + + + {{ item.label }} + + + diff --git a/connector-ui/src/app/shared/form-elements/data-address-type-select/data-address-type-select.component.ts b/connector-ui/src/app/shared/form-elements/data-address-type-select/data-address-type-select.component.ts new file mode 100644 index 000000000..816c93863 --- /dev/null +++ b/connector-ui/src/app/shared/form-elements/data-address-type-select/data-address-type-select.component.ts @@ -0,0 +1,35 @@ +import {Component, HostBinding, Input, OnChanges} from '@angular/core'; +import {FormControl} from '@angular/forms'; +import {ActiveFeatureSet} from 'src/app/core/config/active-feature-set'; +import {SimpleChangesTyped} from '../../../core/utils/angular-utils'; +import {DataAddressType} from './data-address-type'; +import {dataAddressTypeSelectItems} from './data-address-type-select-items'; +import {DataAddressTypeSelectMode} from './data-address-type-select-mode'; + +@Component({ + selector: 'data-address-type-select', + templateUrl: 'data-address-type-select.component.html', +}) +export class DataAddressTypeSelectComponent implements OnChanges { + @Input() + label!: string; + + @Input() + control!: FormControl; + + @HostBinding('class.flex') + @HostBinding('class.flex-row') + cls = true; + + @Input() + mode: DataAddressTypeSelectMode = 'Datasource-Create'; + + constructor(private activeFeatureSet: ActiveFeatureSet) {} + + items = dataAddressTypeSelectItems(this.mode, this.activeFeatureSet); + ngOnChanges(changes: SimpleChangesTyped) { + if (changes.mode) { + this.items = dataAddressTypeSelectItems(this.mode, this.activeFeatureSet); + } + } +} diff --git a/connector-ui/src/app/shared/form-elements/data-address-type-select/data-address-type.ts b/connector-ui/src/app/shared/form-elements/data-address-type-select/data-address-type.ts new file mode 100644 index 000000000..3699d0f80 --- /dev/null +++ b/connector-ui/src/app/shared/form-elements/data-address-type-select/data-address-type.ts @@ -0,0 +1,6 @@ +export type DataAddressType = + | 'Unchanged' + | 'Custom-Data-Address-Json' + | 'Custom-Transfer-Process-Request' + | 'On-Request' + | 'Http'; diff --git a/connector-ui/src/app/shared/form-elements/data-category-select/data-category-select-data.ts b/connector-ui/src/app/shared/form-elements/data-category-select/data-category-select-data.ts new file mode 100644 index 000000000..611e3f020 --- /dev/null +++ b/connector-ui/src/app/shared/form-elements/data-category-select/data-category-select-data.ts @@ -0,0 +1,48 @@ +import {DataCategorySelectItem} from './data-category-select-item'; + +export const DATA_CATEGORY_SELECT_DATA: DataCategorySelectItem[] = [ + { + id: 'Traffic Information', + label: 'Traffic Information', + }, + { + id: 'Roadworks and Road Conditions', + label: 'Roadworks and Road Conditions', + }, + { + id: 'Traffic Flow Information', + label: 'Traffic Flow Information', + }, + { + id: 'Parking Information', + label: 'Parking Information', + }, + { + id: 'Electromobility', + label: 'Electromobility', + }, + { + id: 'Traffic Signs and Speed Information', + label: 'Traffic Signs and Speed Information', + }, + { + id: 'Weather Information', + label: 'Weather Information', + }, + { + id: 'Public Transport Information', + label: 'Public Transport Information', + }, + { + id: 'Shared and On-Demand Mobility', + label: 'Shared and On-Demand Mobility', + }, + { + id: 'Infrastructure and Logistics', + label: 'Infrastructure and Logistics', + }, + { + id: 'Various', + label: 'Various', + }, +]; diff --git a/connector-ui/src/app/shared/form-elements/data-category-select/data-category-select-item.service.ts b/connector-ui/src/app/shared/form-elements/data-category-select/data-category-select-item.service.ts new file mode 100644 index 000000000..2a4d27614 --- /dev/null +++ b/connector-ui/src/app/shared/form-elements/data-category-select/data-category-select-item.service.ts @@ -0,0 +1,34 @@ +import {Injectable} from '@angular/core'; +import {DATA_CATEGORY_SELECT_DATA} from './data-category-select-data'; +import {DataCategorySelectItem} from './data-category-select-item'; + +/** + * Access list of available DataCategorySelectItems + */ +@Injectable({providedIn: 'root'}) +export class DataCategorySelectItemService { + itemsById: Map; + + constructor() { + this.itemsById = this.buildItemsMap(); + } + + /** + * Find DataCategorySelectItem by id + * @param id language select item id + */ + findById(id: string): DataCategorySelectItem { + const item = this.itemsById.get(id); + if (item != null) { + return item; + } + return { + id, + label: id, + }; + } + + private buildItemsMap(): Map { + return new Map(DATA_CATEGORY_SELECT_DATA.map((it) => [it.id, it])); + } +} diff --git a/connector-ui/src/app/shared/form-elements/data-category-select/data-category-select-item.ts b/connector-ui/src/app/shared/form-elements/data-category-select/data-category-select-item.ts new file mode 100644 index 000000000..dbf30de59 --- /dev/null +++ b/connector-ui/src/app/shared/form-elements/data-category-select/data-category-select-item.ts @@ -0,0 +1,4 @@ +export interface DataCategorySelectItem { + id: string; + label: string; +} diff --git a/connector-ui/src/app/shared/form-elements/data-category-select/data-category-select.component.html b/connector-ui/src/app/shared/form-elements/data-category-select/data-category-select.component.html new file mode 100644 index 000000000..9bce0ab73 --- /dev/null +++ b/connector-ui/src/app/shared/form-elements/data-category-select/data-category-select.component.html @@ -0,0 +1,16 @@ + + + + + + {{ item.label }} + + + diff --git a/connector-ui/src/app/shared/form-elements/data-category-select/data-category-select.component.ts b/connector-ui/src/app/shared/form-elements/data-category-select/data-category-select.component.ts new file mode 100644 index 000000000..b19189147 --- /dev/null +++ b/connector-ui/src/app/shared/form-elements/data-category-select/data-category-select.component.ts @@ -0,0 +1,18 @@ +import {Component, Input} from '@angular/core'; +import {FormControl} from '@angular/forms'; +import {DATA_CATEGORY_SELECT_DATA} from './data-category-select-data'; +import {DataCategorySelectItem} from './data-category-select-item'; + +@Component({ + selector: 'data-category-select', + templateUrl: 'data-category-select.component.html', +}) +export class DataCategorySelectComponent { + @Input() + label!: string; + + @Input() + control!: FormControl; + + items = DATA_CATEGORY_SELECT_DATA; +} diff --git a/connector-ui/src/app/shared/form-elements/data-subcategory-select/data-subcategory-items.pipe.ts b/connector-ui/src/app/shared/form-elements/data-subcategory-select/data-subcategory-items.pipe.ts new file mode 100644 index 000000000..00c8b8c18 --- /dev/null +++ b/connector-ui/src/app/shared/form-elements/data-subcategory-select/data-subcategory-items.pipe.ts @@ -0,0 +1,17 @@ +import {Pipe, PipeTransform} from '@angular/core'; +import {DataCategorySelectItem} from '../data-category-select/data-category-select-item'; +import {DataSubcategorySelectItem} from './data-subcategory-select-item'; +import {DataSubcategorySelectItemService} from './data-subcategory-select-item.service'; + +@Pipe({ + name: 'dataSubcategoryItems', +}) +export class DataSubcategoryItemsPipe implements PipeTransform { + constructor(private items: DataSubcategorySelectItemService) {} + + transform( + dataCategory: DataCategorySelectItem | null, + ): DataSubcategorySelectItem[] { + return this.items.findByDataCategory(dataCategory); + } +} diff --git a/connector-ui/src/app/shared/form-elements/data-subcategory-select/data-subcategory-select-data.ts b/connector-ui/src/app/shared/form-elements/data-subcategory-select/data-subcategory-select-data.ts new file mode 100644 index 000000000..ebe31cdc8 --- /dev/null +++ b/connector-ui/src/app/shared/form-elements/data-subcategory-select/data-subcategory-select-data.ts @@ -0,0 +1,189 @@ +import {DataSubcategorySelectItem} from './data-subcategory-select-item'; + +export const DATA_SUBCATEGORY_SELECT_DATA: DataSubcategorySelectItem[] = [ + { + id: 'Accidents', + dataCategoryId: 'Traffic Information', + label: 'Accidents', + }, + + { + id: 'Hazard Warnings', + dataCategoryId: 'Traffic Information', + label: 'Hazard Warnings', + }, + + { + id: 'Roadworks', + dataCategoryId: 'Roadworks and Road Conditions', + label: 'Roadworks', + }, + + { + id: 'Road Conditions', + dataCategoryId: 'Roadworks and Road Conditions', + label: 'Road Conditions', + }, + + { + id: 'Realtime Traffic Flow Data', + dataCategoryId: 'Traffic Flow Information', + label: 'Realtime Traffic Flow Data', + }, + + { + id: 'Forecast Traffic Flow Data', + dataCategoryId: 'Traffic Flow Information', + label: 'Forecast Traffic Flow Data', + }, + + { + id: 'Availability and Forecast', + dataCategoryId: 'Parking Information', + label: 'Availability and Forecast', + }, + + { + id: 'Prices', + dataCategoryId: 'Parking Information', + label: 'Prices', + }, + + { + id: 'Location of Charging Station', + dataCategoryId: 'Electromobility', + label: 'Location of Charging Station', + }, + + { + id: 'Prices at Charging Station', + dataCategoryId: 'Electromobility', + label: 'Prices at Charging Station', + }, + + { + id: 'Availability of Charging Station', + dataCategoryId: 'Electromobility', + label: 'Availability of Charging Station', + }, + + { + id: 'Dynamic Speed Information', + dataCategoryId: 'Traffic Signs and Speed Information', + label: 'Dynamic Speed Information', + }, + + { + id: 'Dynamic Traffic Signs', + dataCategoryId: 'Traffic Signs and Speed Information', + label: 'Dynamic Traffic Signs', + }, + + { + id: 'Static Traffic Signs', + dataCategoryId: 'Traffic Signs and Speed Information', + label: 'Static Traffic Signs', + }, + + { + id: 'Current Weather Conditions', + dataCategoryId: 'Weather Information', + label: 'Current weather conditions', + }, + + { + id: 'Weather Forecast ', + dataCategoryId: 'Weather Information', + label: 'Weather Forecast ', + }, + + { + id: 'Special Events or Disruptions', + dataCategoryId: 'Weather Information', + label: 'Special Events or Disruptions', + }, + + { + id: 'Timetables', + dataCategoryId: 'Public Transport Information', + label: 'Timetables', + }, + + { + id: 'Fare', + dataCategoryId: 'Public Transport Information', + label: 'Fare', + }, + + { + id: 'Location Information', + dataCategoryId: 'Public Transport Information', + label: 'Location Information', + }, + + { + id: 'Vehicle Information ', + dataCategoryId: 'Shared and On-Demand Mobility', + label: 'Vehicle information', + }, + + { + id: 'Availability ', + dataCategoryId: 'Shared and On-Demand Mobility', + label: 'Availability', + }, + + { + id: 'Location ', + dataCategoryId: 'Shared and On-Demand Mobility', + label: 'Location', + }, + + { + id: 'Range ', + dataCategoryId: 'Shared and On-Demand Mobility', + label: 'Range', + }, + + { + id: 'General Information About Planning Of Routes', + dataCategoryId: 'Infrastructure and Logistics', + label: 'General Information About Planning Of Routes', + }, + + { + id: 'Pedestrian Networks', + dataCategoryId: 'Infrastructure and Logistics', + label: 'Pedestrian Networks', + }, + + { + id: 'Cycling Networks', + dataCategoryId: 'Infrastructure and Logistics', + label: 'Cycling Networks', + }, + + { + id: 'Road Network', + dataCategoryId: 'Infrastructure and Logistics', + label: 'Road Network', + }, + + { + id: 'Water Routes', + dataCategoryId: 'Infrastructure and Logistics', + label: 'Water Routes', + }, + + { + id: 'Cargo Logistics', + dataCategoryId: 'Infrastructure and Logistics', + label: 'Cargo and Logistics', + }, + + { + id: 'Toll Information', + dataCategoryId: 'Infrastructure and Logistics', + label: 'Toll Information', + }, +]; diff --git a/connector-ui/src/app/shared/form-elements/data-subcategory-select/data-subcategory-select-item.service.ts b/connector-ui/src/app/shared/form-elements/data-subcategory-select/data-subcategory-select-item.service.ts new file mode 100644 index 000000000..89f39282d --- /dev/null +++ b/connector-ui/src/app/shared/form-elements/data-subcategory-select/data-subcategory-select-item.service.ts @@ -0,0 +1,44 @@ +import {Injectable} from '@angular/core'; +import {associateBy, groupedBy} from 'src/app/core/utils/map-utils'; +import {DataCategorySelectItem} from '../data-category-select/data-category-select-item'; +import {DATA_SUBCATEGORY_SELECT_DATA} from './data-subcategory-select-data'; +import {DataSubcategorySelectItem} from './data-subcategory-select-item'; + +/** + * Access list of available DataSubcategorySelectItems + */ +@Injectable({providedIn: 'root'}) +export class DataSubcategorySelectItemService { + itemsById = associateBy(DATA_SUBCATEGORY_SELECT_DATA, (it) => it.id); + itemsByDataCategory = groupedBy( + DATA_SUBCATEGORY_SELECT_DATA, + (it) => it.dataCategoryId, + ); + + /** + * Find DataSubcategorySelectItem by id + * @param id language select item id + */ + findById(id: string): DataSubcategorySelectItem { + const item = this.itemsById.get(id); + if (item != null) { + return item; + } + return { + id, + dataCategoryId: '', + label: id, + }; + } + + /** + * Find DataCategorySelectItems by (parent) data category + */ + findByDataCategory( + dataCategory: DataCategorySelectItem | null, + ): DataSubcategorySelectItem[] { + return dataCategory + ? this.itemsByDataCategory.get(dataCategory?.id) ?? [] + : []; + } +} diff --git a/connector-ui/src/app/shared/form-elements/data-subcategory-select/data-subcategory-select-item.ts b/connector-ui/src/app/shared/form-elements/data-subcategory-select/data-subcategory-select-item.ts new file mode 100644 index 000000000..cef088969 --- /dev/null +++ b/connector-ui/src/app/shared/form-elements/data-subcategory-select/data-subcategory-select-item.ts @@ -0,0 +1,5 @@ +export interface DataSubcategorySelectItem { + id: string; + dataCategoryId: string; + label: string; +} diff --git a/connector-ui/src/app/shared/form-elements/data-subcategory-select/data-subcategory-select.component.html b/connector-ui/src/app/shared/form-elements/data-subcategory-select/data-subcategory-select.component.html new file mode 100644 index 000000000..2207dec16 --- /dev/null +++ b/connector-ui/src/app/shared/form-elements/data-subcategory-select/data-subcategory-select.component.html @@ -0,0 +1,18 @@ + + + + - + + {{ item.label }} + + + diff --git a/connector-ui/src/app/shared/form-elements/data-subcategory-select/data-subcategory-select.component.ts b/connector-ui/src/app/shared/form-elements/data-subcategory-select/data-subcategory-select.component.ts new file mode 100644 index 000000000..c6ea3fc87 --- /dev/null +++ b/connector-ui/src/app/shared/form-elements/data-subcategory-select/data-subcategory-select.component.ts @@ -0,0 +1,19 @@ +import {Component, Input} from '@angular/core'; +import {FormControl} from '@angular/forms'; +import {DataCategorySelectItem} from '../data-category-select/data-category-select-item'; +import {DataSubcategorySelectItem} from './data-subcategory-select-item'; + +@Component({ + selector: 'data-subcategory-select', + templateUrl: 'data-subcategory-select.component.html', +}) +export class DataSubcategorySelectComponent { + @Input() + label!: string; + + @Input() + dataCategory: DataCategorySelectItem | null = null; + + @Input() + control!: FormControl; +} diff --git a/connector-ui/src/app/shared/form-elements/edit-asset-form-data-address-type-select/edit-asset-form-data-address-type-select.component.html b/connector-ui/src/app/shared/form-elements/edit-asset-form-data-address-type-select/edit-asset-form-data-address-type-select.component.html new file mode 100644 index 000000000..24d023919 --- /dev/null +++ b/connector-ui/src/app/shared/form-elements/edit-asset-form-data-address-type-select/edit-asset-form-data-address-type-select.component.html @@ -0,0 +1,9 @@ + + + + + + {{ item.label }} + + + diff --git a/connector-ui/src/app/shared/form-elements/edit-asset-form-data-address-type-select/edit-asset-form-data-address-type-select.component.ts b/connector-ui/src/app/shared/form-elements/edit-asset-form-data-address-type-select/edit-asset-form-data-address-type-select.component.ts new file mode 100644 index 000000000..68081dfcd --- /dev/null +++ b/connector-ui/src/app/shared/form-elements/edit-asset-form-data-address-type-select/edit-asset-form-data-address-type-select.component.ts @@ -0,0 +1,26 @@ +import {Component, Input} from '@angular/core'; +import {FormControl} from '@angular/forms'; +import {DataAddressType} from '../data-address-type-select/data-address-type'; + +@Component({ + selector: 'edit-asset-form-data-address-type-select', + templateUrl: 'edit-asset-form-data-address-type-select.component.html', +}) +export class EditAssetFormDataAddressTypeSelectComponent { + @Input() + label!: string; + + @Input() + control!: FormControl; + + items = [ + { + id: 'Http', + label: 'REST-API Endpoint', + }, + { + id: 'Custom-Data-Address-Json', + label: `Custom Datasource-Create Config (JSON)`, + }, + ]; +} diff --git a/connector-ui/src/app/shared/form-elements/edit-asset-form-group/edit-asset-form-group.component.html b/connector-ui/src/app/shared/form-elements/edit-asset-form-group/edit-asset-form-group.component.html new file mode 100644 index 000000000..3ec1f6c84 --- /dev/null +++ b/connector-ui/src/app/shared/form-elements/edit-asset-form-group/edit-asset-form-group.component.html @@ -0,0 +1,15 @@ +
+
+

+ {{ myTitle }} +

+

+ {{ description }} +

+
+ +
+ +
+
diff --git a/connector-ui/src/app/shared/form-elements/edit-asset-form-group/edit-asset-form-group.component.ts b/connector-ui/src/app/shared/form-elements/edit-asset-form-group/edit-asset-form-group.component.ts new file mode 100644 index 000000000..ebc142103 --- /dev/null +++ b/connector-ui/src/app/shared/form-elements/edit-asset-form-group/edit-asset-form-group.component.ts @@ -0,0 +1,10 @@ +import {Component, Input} from '@angular/core'; + +@Component({ + selector: 'edit-asset-form-group', + templateUrl: './edit-asset-form-group.component.html', +}) +export class EditAssetFormGroupComponent { + @Input() myTitle!: string; + @Input() description!: string; +} diff --git a/connector-ui/src/app/shared/form-elements/edit-asset-form-input/edit-asset-form-input.component.html b/connector-ui/src/app/shared/form-elements/edit-asset-form-input/edit-asset-form-input.component.html new file mode 100644 index 000000000..66cd9945c --- /dev/null +++ b/connector-ui/src/app/shared/form-elements/edit-asset-form-input/edit-asset-form-input.component.html @@ -0,0 +1,53 @@ + + + + + + + {{ validationMessages.invalidUrlMessage }} + + + {{ validationMessages.invalidEmailMessage }} + + + {{ validationMessages.invalidWhitespacesOrColonsMessage }} + + + {{ validationMessages.invalidPrefix('ID', 'urn:artifact') }} + + + {{ ctrl.errors?.exists }} + + + {{ hint }} + + + + + + Describes the content type of the data as a MIME type, see + common types + + diff --git a/connector-ui/src/app/shared/form-elements/edit-asset-form-input/edit-asset-form-input.component.ts b/connector-ui/src/app/shared/form-elements/edit-asset-form-input/edit-asset-form-input.component.ts new file mode 100644 index 000000000..ec28a4f62 --- /dev/null +++ b/connector-ui/src/app/shared/form-elements/edit-asset-form-input/edit-asset-form-input.component.ts @@ -0,0 +1,20 @@ +import {Component, Input} from '@angular/core'; +import {FormControl} from '@angular/forms'; +import {ValidationMessages} from 'src/app/core/validators/validation-messages'; + +@Component({ + selector: 'edit-asset-form-input', + templateUrl: './edit-asset-form-input.component.html', +}) +export class EditAssetFormInputComponent { + @Input() ctrl!: FormControl; + @Input() fieldId = 'missing-id-' + Math.random().toString(36).substring(7); + @Input() label!: string; + @Input() placeholder: string = '...'; + @Input() tooltip: string = ''; + @Input() hint: string = ''; + @Input() hideHint: boolean = false; + @Input() contentTypeHint: boolean = false; + + constructor(public validationMessages: ValidationMessages) {} +} diff --git a/connector-ui/src/app/shared/form-elements/edit-asset-form-label/edit-asset-form-label.component.html b/connector-ui/src/app/shared/form-elements/edit-asset-form-label/edit-asset-form-label.component.html new file mode 100644 index 000000000..8c4339a97 --- /dev/null +++ b/connector-ui/src/app/shared/form-elements/edit-asset-form-label/edit-asset-form-label.component.html @@ -0,0 +1,9 @@ + diff --git a/connector-ui/src/app/shared/form-elements/edit-asset-form-label/edit-asset-form-label.component.ts b/connector-ui/src/app/shared/form-elements/edit-asset-form-label/edit-asset-form-label.component.ts new file mode 100644 index 000000000..c469a05f1 --- /dev/null +++ b/connector-ui/src/app/shared/form-elements/edit-asset-form-label/edit-asset-form-label.component.ts @@ -0,0 +1,16 @@ +import {Component, Input} from '@angular/core'; +import {AbstractControl, Validators} from '@angular/forms'; + +@Component({ + selector: 'edit-asset-form-label', + templateUrl: './edit-asset-form-label.component.html', +}) +export class EditAssetFormLabelComponent { + @Input() label!: string; + @Input() htmlFor?: string; + @Input() ctrl?: AbstractControl; + + isRequired(): boolean { + return this.ctrl?.hasValidator(Validators.required) || false; + } +} diff --git a/connector-ui/src/app/shared/form-elements/edit-asset-form-textarea/edit-asset-form-textarea.component.html b/connector-ui/src/app/shared/form-elements/edit-asset-form-textarea/edit-asset-form-textarea.component.html new file mode 100644 index 000000000..01917df80 --- /dev/null +++ b/connector-ui/src/app/shared/form-elements/edit-asset-form-textarea/edit-asset-form-textarea.component.html @@ -0,0 +1,19 @@ + + + + + + {{ validationMessages.invalidJsonMessage }} + + + + + diff --git a/connector-ui/src/app/shared/form-elements/edit-asset-form-textarea/edit-asset-form-textarea.component.ts b/connector-ui/src/app/shared/form-elements/edit-asset-form-textarea/edit-asset-form-textarea.component.ts new file mode 100644 index 000000000..93d1ee63e --- /dev/null +++ b/connector-ui/src/app/shared/form-elements/edit-asset-form-textarea/edit-asset-form-textarea.component.ts @@ -0,0 +1,18 @@ +import {Component, Input} from '@angular/core'; +import {FormControl} from '@angular/forms'; +import {ValidationMessages} from 'src/app/core/validators/validation-messages'; + +@Component({ + selector: 'edit-asset-form-textarea', + templateUrl: './edit-asset-form-textarea.component.html', +}) +export class EditAssetFormTextareaComponent { + @Input() ctrl!: FormControl; + @Input() fieldId = 'missing-id-' + Math.random().toString(36).substring(7); + @Input() label!: string; + @Input() placeholder: string = '...'; + @Input() hideHint: boolean = false; + @Input() textareaClasses: string = 'h-36'; + + constructor(public validationMessages: ValidationMessages) {} +} diff --git a/connector-ui/src/app/shared/form-elements/keyword-select/keyword-select.component.html b/connector-ui/src/app/shared/form-elements/keyword-select/keyword-select.component.html new file mode 100644 index 000000000..e7bf8d321 --- /dev/null +++ b/connector-ui/src/app/shared/form-elements/keyword-select/keyword-select.component.html @@ -0,0 +1,33 @@ + + + + + {{ keyword }} + + + + + + diff --git a/connector-ui/src/app/shared/form-elements/keyword-select/keyword-select.component.ts b/connector-ui/src/app/shared/form-elements/keyword-select/keyword-select.component.ts new file mode 100644 index 000000000..81e602161 --- /dev/null +++ b/connector-ui/src/app/shared/form-elements/keyword-select/keyword-select.component.ts @@ -0,0 +1,34 @@ +import {COMMA, ENTER, SEMICOLON} from '@angular/cdk/keycodes'; +import {Component, Input} from '@angular/core'; +import {FormControl} from '@angular/forms'; +import {MatChipInputEvent} from '@angular/material/chips'; +import {removeOnce} from 'src/app/core/utils/array-utils'; + +@Component({ + selector: 'keyword-select', + templateUrl: 'keyword-select.component.html', +}) +export class KeywordSelectComponent { + separatorKeysCodes: number[] = [ENTER, COMMA, SEMICOLON]; + + @Input() + label!: string; + + @Input() + control!: FormControl; + + remove(keyword: string) { + this.control.setValue(removeOnce(this.control.value, keyword)); + } + + add(event: MatChipInputEvent): void { + const keywords = (event.value || '') + .split(/[,;]/) + .map((it) => it.trim()) + .filter((it) => it); + if (keywords.length) { + this.control.setValue([...this.control.value, ...keywords]); + } + event.chipInput.clear(); + } +} diff --git a/connector-ui/src/app/shared/form-elements/language-select/language-select-data.ts b/connector-ui/src/app/shared/form-elements/language-select/language-select-data.ts new file mode 100644 index 000000000..79740613f --- /dev/null +++ b/connector-ui/src/app/shared/form-elements/language-select/language-select-data.ts @@ -0,0 +1,1299 @@ +import {LanguageSelectItem} from './language-select-item'; + +export const LANGUAGE_SELECT_DATA: LanguageSelectItem[] = [ + { + id: 'https://w3id.org/idsa/code/MULTI_LINGUAL', + idShort: 'MULTI_LINGUAL', + label: 'Multilingual', + comment: + 'Code indicates that several languages are used or no concrete language can be determined.', + }, + + { + id: 'https://w3id.org/idsa/code/AB', + idShort: 'AB', + label: 'Abkhaz', + sameAs: 'https://dbpedia.org/resource/ISO_639:ab', + }, + + { + id: 'https://w3id.org/idsa/code/AA', + idShort: 'AA', + label: 'Afar', + sameAs: 'https://dbpedia.org/resource/ISO_639:aa', + }, + + { + id: 'https://w3id.org/idsa/code/AF', + idShort: 'AF', + label: 'Afrikaans', + sameAs: 'https://dbpedia.org/resource/ISO_639:af', + }, + + { + id: 'https://w3id.org/idsa/code/AK', + idShort: 'AK', + label: 'Akan', + sameAs: 'https://dbpedia.org/resource/ISO_639:ak', + }, + + { + id: 'https://w3id.org/idsa/code/SQ', + idShort: 'SQ', + label: 'Albanian', + sameAs: 'https://dbpedia.org/resource/ISO_639:sq', + }, + + { + id: 'https://w3id.org/idsa/code/AM', + idShort: 'AM', + label: 'Amharic', + sameAs: 'https://dbpedia.org/resource/ISO_639:am', + }, + + { + id: 'https://w3id.org/idsa/code/AR', + idShort: 'AR', + label: 'Arabic', + sameAs: 'https://dbpedia.org/resource/ISO_639:ar', + }, + + { + id: 'https://w3id.org/idsa/code/AN', + idShort: 'AN', + label: 'Aragonese', + sameAs: 'https://dbpedia.org/resource/ISO_639:an', + }, + + { + id: 'https://w3id.org/idsa/code/HY', + idShort: 'HY', + label: 'Armenian', + sameAs: 'https://dbpedia.org/resource/ISO_639:hy', + }, + + { + id: 'https://w3id.org/idsa/code/AS', + idShort: 'AS', + label: 'Assamese', + sameAs: 'https://dbpedia.org/resource/ISO_639:as', + }, + + { + id: 'https://w3id.org/idsa/code/AV', + idShort: 'AV', + label: 'Avaric', + sameAs: 'https://dbpedia.org/resource/ISO_639:av', + }, + + { + id: 'https://w3id.org/idsa/code/AE', + idShort: 'AE', + label: 'Avestan', + sameAs: 'https://dbpedia.org/resource/ISO_639:ae', + }, + + { + id: 'https://w3id.org/idsa/code/AY', + idShort: 'AY', + label: 'Aymara', + sameAs: 'https://dbpedia.org/resource/ISO_639:ay', + }, + + { + id: 'https://w3id.org/idsa/code/AZ', + idShort: 'AZ', + label: 'Azerbaijani', + sameAs: 'https://dbpedia.org/resource/ISO_639:az', + }, + + { + id: 'https://w3id.org/idsa/code/BM', + idShort: 'BM', + label: 'Bambara', + sameAs: 'https://dbpedia.org/resource/ISO_639:bm', + }, + + { + id: 'https://w3id.org/idsa/code/BA', + idShort: 'BA', + label: 'Bashkir', + sameAs: 'https://dbpedia.org/resource/ISO_639:ba', + }, + + { + id: 'https://w3id.org/idsa/code/EU', + idShort: 'EU', + label: 'Basque', + sameAs: 'https://dbpedia.org/resource/ISO_639:eu', + }, + + { + id: 'https://w3id.org/idsa/code/BE', + idShort: 'BE', + label: 'Belarusian', + sameAs: 'https://dbpedia.org/resource/ISO_639:be', + }, + + { + id: 'https://w3id.org/idsa/code/BN', + idShort: 'BN', + label: 'Bengali, Bangla', + sameAs: 'https://dbpedia.org/resource/ISO_639:bn', + }, + + { + id: 'https://w3id.org/idsa/code/BH', + idShort: 'BH', + label: 'Bihari', + sameAs: 'https://dbpedia.org/resource/ISO_639:bh', + }, + + { + id: 'https://w3id.org/idsa/code/BI', + idShort: 'BI', + label: 'Bislama', + sameAs: 'https://dbpedia.org/resource/ISO_639:bi', + }, + + { + id: 'https://w3id.org/idsa/code/BS', + idShort: 'BS', + label: 'Bosnian', + sameAs: 'https://dbpedia.org/resource/ISO_639:bs', + }, + + { + id: 'https://w3id.org/idsa/code/BR', + idShort: 'BR', + label: 'Breton', + sameAs: 'https://dbpedia.org/resource/ISO_639:br', + }, + + { + id: 'https://w3id.org/idsa/code/BG', + idShort: 'BG', + label: 'Bulgarian', + sameAs: 'https://dbpedia.org/resource/ISO_639:bg', + }, + + { + id: 'https://w3id.org/idsa/code/MY', + idShort: 'MY', + label: 'Burmese', + sameAs: 'https://dbpedia.org/resource/ISO_639:my', + }, + + { + id: 'https://w3id.org/idsa/code/CA', + idShort: 'CA', + label: 'Catalan', + sameAs: 'https://dbpedia.org/resource/ISO_639:ca', + }, + + { + id: 'https://w3id.org/idsa/code/CH', + idShort: 'CH', + label: 'Chamorro', + sameAs: 'https://dbpedia.org/resource/ISO_639:ch', + }, + + { + id: 'https://w3id.org/idsa/code/CE', + idShort: 'CE', + label: 'Chechen', + sameAs: 'https://dbpedia.org/resource/ISO_639:ce', + }, + + { + id: 'https://w3id.org/idsa/code/NY', + idShort: 'NY', + label: 'Chichewa, Chewa, Nyanja', + sameAs: 'https://dbpedia.org/resource/ISO_639:ny', + }, + + { + id: 'https://w3id.org/idsa/code/ZH', + idShort: 'ZH', + label: 'Chinese', + sameAs: 'https://dbpedia.org/resource/ISO_639:zh', + }, + + { + id: 'https://w3id.org/idsa/code/CV', + idShort: 'CV', + label: 'Chuvash', + sameAs: 'https://dbpedia.org/resource/ISO_639:cv', + }, + + { + id: 'https://w3id.org/idsa/code/KW', + idShort: 'KW', + label: 'Cornish', + sameAs: 'https://dbpedia.org/resource/ISO_639:kw', + }, + + { + id: 'https://w3id.org/idsa/code/CO', + idShort: 'CO', + label: 'Corsican', + sameAs: 'https://dbpedia.org/resource/ISO_639:co', + }, + + { + id: 'https://w3id.org/idsa/code/CR', + idShort: 'CR', + label: 'Cree', + sameAs: 'https://dbpedia.org/resource/ISO_639:cr', + }, + + { + id: 'https://w3id.org/idsa/code/HR', + idShort: 'HR', + label: 'Croatian', + sameAs: 'https://dbpedia.org/resource/ISO_639:hr', + }, + + { + id: 'https://w3id.org/idsa/code/CS', + idShort: 'CS', + label: 'Czech', + sameAs: 'https://dbpedia.org/resource/ISO_639:cs', + }, + + { + id: 'https://w3id.org/idsa/code/DA', + idShort: 'DA', + label: 'Danish', + sameAs: 'https://dbpedia.org/resource/ISO_639:da', + }, + + { + id: 'https://w3id.org/idsa/code/DV', + idShort: 'DV', + label: 'Divehi, Dhivehi, Maldivian', + sameAs: 'https://dbpedia.org/resource/ISO_639:dv', + }, + + { + id: 'https://w3id.org/idsa/code/NL', + idShort: 'NL', + label: 'Dutch', + sameAs: 'https://dbpedia.org/resource/ISO_639:nl', + }, + + { + id: 'https://w3id.org/idsa/code/DZ', + idShort: 'DZ', + label: 'Dzongkha', + sameAs: 'https://dbpedia.org/resource/ISO_639:dz', + }, + + { + id: 'https://w3id.org/idsa/code/EN', + idShort: 'EN', + label: 'English', + sameAs: 'https://dbpedia.org/resource/ISO_639:en', + }, + + { + id: 'https://w3id.org/idsa/code/EO', + idShort: 'EO', + label: 'Esperanto', + sameAs: 'https://dbpedia.org/resource/ISO_639:eo', + }, + + { + id: 'https://w3id.org/idsa/code/ET', + idShort: 'ET', + label: 'Estonian', + sameAs: 'https://dbpedia.org/resource/ISO_639:et', + }, + + { + id: 'https://w3id.org/idsa/code/EE', + idShort: 'EE', + label: 'Ewe', + sameAs: 'https://dbpedia.org/resource/ISO_639:ee', + }, + + { + id: 'https://w3id.org/idsa/code/FO', + idShort: 'FO', + label: 'Faroese', + sameAs: 'https://dbpedia.org/resource/ISO_639:fo', + }, + + { + id: 'https://w3id.org/idsa/code/FJ', + idShort: 'FJ', + label: 'Fijian', + sameAs: 'https://dbpedia.org/resource/ISO_639:fj', + }, + + { + id: 'https://w3id.org/idsa/code/FI', + idShort: 'FI', + label: 'Finnish', + sameAs: 'https://dbpedia.org/resource/ISO_639:fi', + }, + + { + id: 'https://w3id.org/idsa/code/FR', + idShort: 'FR', + label: 'French', + sameAs: 'https://dbpedia.org/resource/ISO_639:fr', + }, + + { + id: 'https://w3id.org/idsa/code/FF', + idShort: 'FF', + label: 'Fula, Fulah, Pulaar, Pular', + sameAs: 'https://dbpedia.org/resource/ISO_639:ff', + }, + + { + id: 'https://w3id.org/idsa/code/GL', + idShort: 'GL', + label: 'Galician', + sameAs: 'https://dbpedia.org/resource/ISO_639:gl', + }, + + { + id: 'https://w3id.org/idsa/code/KA', + idShort: 'KA', + label: 'Georgian', + sameAs: 'https://dbpedia.org/resource/ISO_639:ka', + }, + + { + id: 'https://w3id.org/idsa/code/DE', + idShort: 'DE', + label: 'German', + sameAs: 'https://dbpedia.org/resource/ISO_639:de', + }, + + { + id: 'https://w3id.org/idsa/code/EL', + idShort: 'EL', + label: 'Greek (modern)', + sameAs: 'https://dbpedia.org/resource/ISO_639:el', + }, + + { + id: 'https://w3id.org/idsa/code/GN', + idShort: 'GN', + label: 'Guaraní', + sameAs: 'https://dbpedia.org/resource/ISO_639:gn', + }, + + { + id: 'https://w3id.org/idsa/code/GU', + idShort: 'GU', + label: 'Gujarati', + sameAs: 'https://dbpedia.org/resource/ISO_639:gu', + }, + + { + id: 'https://w3id.org/idsa/code/HT', + idShort: 'HT', + label: 'Haitian, Haitian Creole', + sameAs: 'https://dbpedia.org/resource/ISO_639:ht', + }, + + { + id: 'https://w3id.org/idsa/code/HA', + idShort: 'HA', + label: 'Hausa', + sameAs: 'https://dbpedia.org/resource/ISO_639:ha', + }, + + { + id: 'https://w3id.org/idsa/code/HE', + idShort: 'HE', + label: 'Hebrew (modern)', + sameAs: 'https://dbpedia.org/resource/ISO_639:he', + }, + + { + id: 'https://w3id.org/idsa/code/HZ', + idShort: 'HZ', + label: 'Herero', + sameAs: 'https://dbpedia.org/resource/ISO_639:hz', + }, + + { + id: 'https://w3id.org/idsa/code/HI', + idShort: 'HI', + label: 'Hindi', + sameAs: 'https://dbpedia.org/resource/ISO_639:hi', + }, + + { + id: 'https://w3id.org/idsa/code/HO', + idShort: 'HO', + label: 'Hiri Motu', + sameAs: 'https://dbpedia.org/resource/ISO_639:ho', + }, + + { + id: 'https://w3id.org/idsa/code/HU', + idShort: 'HU', + label: 'Hungarian', + sameAs: 'https://dbpedia.org/resource/ISO_639:hu', + }, + + { + id: 'https://w3id.org/idsa/code/IA', + idShort: 'IA', + label: 'Interlingua', + sameAs: 'https://dbpedia.org/resource/ISO_639:ia', + }, + + { + id: 'https://w3id.org/idsa/code/ID', + idShort: 'ID', + label: 'Indonesian', + sameAs: 'https://dbpedia.org/resource/ISO_639:id', + }, + + { + id: 'https://w3id.org/idsa/code/IE', + idShort: 'IE', + label: 'Interlingue', + sameAs: 'https://dbpedia.org/resource/ISO_639:ie', + }, + + { + id: 'https://w3id.org/idsa/code/GA', + idShort: 'GA', + label: 'Irish', + sameAs: 'https://dbpedia.org/resource/ISO_639:ga', + }, + + { + id: 'https://w3id.org/idsa/code/IG', + idShort: 'IG', + label: 'Igbo', + sameAs: 'https://dbpedia.org/resource/ISO_639:ig', + }, + + { + id: 'https://w3id.org/idsa/code/IK', + idShort: 'IK', + label: 'Inupiaq', + sameAs: 'https://dbpedia.org/resource/ISO_639:ik', + }, + + { + id: 'https://w3id.org/idsa/code/IO', + idShort: 'IO', + label: 'Ido', + sameAs: 'https://dbpedia.org/resource/ISO_639:io', + }, + + { + id: 'https://w3id.org/idsa/code/IS', + idShort: 'IS', + label: 'Icelandic', + sameAs: 'https://dbpedia.org/resource/ISO_639:is', + }, + + { + id: 'https://w3id.org/idsa/code/IT', + idShort: 'IT', + label: 'Italian', + sameAs: 'https://dbpedia.org/resource/ISO_639:it', + }, + + { + id: 'https://w3id.org/idsa/code/IU', + idShort: 'IU', + label: 'Inuktitut', + sameAs: 'https://dbpedia.org/resource/ISO_639:iu', + }, + + { + id: 'https://w3id.org/idsa/code/JA', + idShort: 'JA', + label: 'Japanese', + sameAs: 'https://dbpedia.org/resource/ISO_639:ja', + }, + + { + id: 'https://w3id.org/idsa/code/JV', + idShort: 'JV', + label: 'Javanese', + sameAs: 'https://dbpedia.org/resource/ISO_639:jv', + }, + + { + id: 'https://w3id.org/idsa/code/KL', + idShort: 'KL', + label: 'Kalaallisut, Greenlandic', + sameAs: 'https://dbpedia.org/resource/ISO_639:kl', + }, + + { + id: 'https://w3id.org/idsa/code/KN', + idShort: 'KN', + label: 'Kannada', + sameAs: 'https://dbpedia.org/resource/ISO_639:kn', + }, + + { + id: 'https://w3id.org/idsa/code/KR', + idShort: 'KR', + label: 'Kanuri', + sameAs: 'https://dbpedia.org/resource/ISO_639:kr', + }, + + { + id: 'https://w3id.org/idsa/code/KS', + idShort: 'KS', + label: 'Kashmiri', + sameAs: 'https://dbpedia.org/resource/ISO_639:ks', + }, + + { + id: 'https://w3id.org/idsa/code/KK', + idShort: 'KK', + label: 'Kazakh', + sameAs: 'https://dbpedia.org/resource/ISO_639:kk', + }, + + { + id: 'https://w3id.org/idsa/code/KM', + idShort: 'KM', + label: 'Khmer', + sameAs: 'https://dbpedia.org/resource/ISO_639:km', + }, + + { + id: 'https://w3id.org/idsa/code/KI', + idShort: 'KI', + label: 'Kikuyu, Gikuyu', + sameAs: 'https://dbpedia.org/resource/ISO_639:ki', + }, + + { + id: 'https://w3id.org/idsa/code/RW', + idShort: 'RW', + label: 'Kinyarwanda', + sameAs: 'https://dbpedia.org/resource/ISO_639:rw', + }, + + { + id: 'https://w3id.org/idsa/code/KY', + idShort: 'KY', + label: 'Kyrgyz', + sameAs: 'https://dbpedia.org/resource/ISO_639:ky', + }, + + { + id: 'https://w3id.org/idsa/code/KV', + idShort: 'KV', + label: 'Komi', + sameAs: 'https://dbpedia.org/resource/ISO_639:kv', + }, + + { + id: 'https://w3id.org/idsa/code/KG', + idShort: 'KG', + label: 'Kongo', + sameAs: 'https://dbpedia.org/resource/ISO_639:kg', + }, + + { + id: 'https://w3id.org/idsa/code/KO', + idShort: 'KO', + label: 'Korean', + sameAs: 'https://dbpedia.org/resource/ISO_639:ko', + }, + + { + id: 'https://w3id.org/idsa/code/KU', + idShort: 'KU', + label: 'Kurdish', + sameAs: 'https://dbpedia.org/resource/ISO_639:ku', + }, + + { + id: 'https://w3id.org/idsa/code/KJ', + idShort: 'KJ', + label: 'Kwanyama, Kuanyama', + sameAs: 'https://dbpedia.org/resource/ISO_639:kj', + }, + + { + id: 'https://w3id.org/idsa/code/LA', + idShort: 'LA', + label: 'Latin', + sameAs: 'https://dbpedia.org/resource/ISO_639:la', + }, + + { + id: 'https://w3id.org/idsa/code/LB', + idShort: 'LB', + label: 'Luxembourgish, Letzeburgesch', + sameAs: 'https://dbpedia.org/resource/ISO_639:lb', + }, + + { + id: 'https://w3id.org/idsa/code/LG', + idShort: 'LG', + label: 'Ganda', + sameAs: 'https://dbpedia.org/resource/ISO_639:lg', + }, + + { + id: 'https://w3id.org/idsa/code/LI', + idShort: 'LI', + label: 'Limburgish, Limburgan, Limburger', + sameAs: 'https://dbpedia.org/resource/ISO_639:li', + }, + + { + id: 'https://w3id.org/idsa/code/LN', + idShort: 'LN', + label: 'Lingala', + sameAs: 'https://dbpedia.org/resource/ISO_639:ln', + }, + + { + id: 'https://w3id.org/idsa/code/LO', + idShort: 'LO', + label: 'Lao', + sameAs: 'https://dbpedia.org/resource/ISO_639:lo', + }, + + { + id: 'https://w3id.org/idsa/code/LT', + idShort: 'LT', + label: 'Lithuanian', + sameAs: 'https://dbpedia.org/resource/ISO_639:lt', + }, + + { + id: 'https://w3id.org/idsa/code/LU', + idShort: 'LU', + label: 'Luba-Katanga', + sameAs: 'https://dbpedia.org/resource/ISO_639:lu', + }, + + { + id: 'https://w3id.org/idsa/code/LV', + idShort: 'LV', + label: 'Latvian', + sameAs: 'https://dbpedia.org/resource/ISO_639:lv', + }, + + { + id: 'https://w3id.org/idsa/code/GV', + idShort: 'GV', + label: 'Manx', + sameAs: 'https://dbpedia.org/resource/ISO_639:gv', + }, + + { + id: 'https://w3id.org/idsa/code/MK', + idShort: 'MK', + label: 'Macedonian', + sameAs: 'https://dbpedia.org/resource/ISO_639:mk', + }, + + { + id: 'https://w3id.org/idsa/code/MG', + idShort: 'MG', + label: 'Malagasy', + sameAs: 'https://dbpedia.org/resource/ISO_639:mg', + }, + + { + id: 'https://w3id.org/idsa/code/MS', + idShort: 'MS', + label: 'Malay', + sameAs: 'https://dbpedia.org/resource/ISO_639:ms', + }, + + { + id: 'https://w3id.org/idsa/code/ML', + idShort: 'ML', + label: 'Malayalam', + sameAs: 'https://dbpedia.org/resource/ISO_639:ml', + }, + + { + id: 'https://w3id.org/idsa/code/MT', + idShort: 'MT', + label: 'Maltese', + sameAs: 'https://dbpedia.org/resource/ISO_639:mt', + }, + + { + id: 'https://w3id.org/idsa/code/MI', + idShort: 'MI', + label: 'Māori', + sameAs: 'https://dbpedia.org/resource/ISO_639:mi', + }, + + { + id: 'https://w3id.org/idsa/code/MR', + idShort: 'MR', + label: 'Marathi (Marāṭhī)', + sameAs: 'https://dbpedia.org/resource/ISO_639:mr', + }, + + { + id: 'https://w3id.org/idsa/code/MH', + idShort: 'MH', + label: 'Marshallese', + sameAs: 'https://dbpedia.org/resource/ISO_639:mh', + }, + + { + id: 'https://w3id.org/idsa/code/MN', + idShort: 'MN', + label: 'Mongolian', + sameAs: 'https://dbpedia.org/resource/ISO_639:mn', + }, + + { + id: 'https://w3id.org/idsa/code/NA', + idShort: 'NA', + label: 'Nauruan', + sameAs: 'https://dbpedia.org/resource/ISO_639:na', + }, + + { + id: 'https://w3id.org/idsa/code/NV', + idShort: 'NV', + label: 'Navajo, Navaho', + sameAs: 'https://dbpedia.org/resource/ISO_639:nv', + }, + + { + id: 'https://w3id.org/idsa/code/ND', + idShort: 'ND', + label: 'Northern Ndebele', + sameAs: 'https://dbpedia.org/resource/ISO_639:nd', + }, + + { + id: 'https://w3id.org/idsa/code/NE', + idShort: 'NE', + label: 'Nepali', + sameAs: 'https://dbpedia.org/resource/ISO_639:ne', + }, + + { + id: 'https://w3id.org/idsa/code/NG', + idShort: 'NG', + label: 'Ndonga', + sameAs: 'https://dbpedia.org/resource/ISO_639:ng', + }, + + { + id: 'https://w3id.org/idsa/code/NB', + idShort: 'NB', + label: 'Norwegian Bokmål', + sameAs: 'https://dbpedia.org/resource/ISO_639:nb', + }, + + { + id: 'https://w3id.org/idsa/code/NN', + idShort: 'NN', + label: 'Norwegian Nynorsk', + sameAs: 'https://dbpedia.org/resource/ISO_639:nn', + }, + + { + id: 'https://w3id.org/idsa/code/NO', + idShort: 'NO', + label: 'Norwegian', + sameAs: 'https://dbpedia.org/resource/ISO_639:no', + }, + + { + id: 'https://w3id.org/idsa/code/II', + idShort: 'II', + label: 'Nuosu', + sameAs: 'https://dbpedia.org/resource/ISO_639:ii', + }, + + { + id: 'https://w3id.org/idsa/code/NR', + idShort: 'NR', + label: 'Southern Ndebele', + sameAs: 'https://dbpedia.org/resource/ISO_639:nr', + }, + + { + id: 'https://w3id.org/idsa/code/OC', + idShort: 'OC', + label: 'Occitan', + sameAs: 'https://dbpedia.org/resource/ISO_639:oc', + }, + + { + id: 'https://w3id.org/idsa/code/OJ', + idShort: 'OJ', + label: 'Ojibwe, Ojibwa', + sameAs: 'https://dbpedia.org/resource/ISO_639:oj', + }, + + { + id: 'https://w3id.org/idsa/code/CU', + idShort: 'CU', + label: 'Old Church Slavonic, Church Slavonic, Old Bulgarian', + sameAs: 'https://dbpedia.org/resource/ISO_639:cu', + }, + + { + id: 'https://w3id.org/idsa/code/OM', + idShort: 'OM', + label: 'Oromo', + sameAs: 'https://dbpedia.org/resource/ISO_639:om', + }, + + { + id: 'https://w3id.org/idsa/code/OR', + idShort: 'OR', + label: 'Oriya', + sameAs: 'https://dbpedia.org/resource/ISO_639:or', + }, + + { + id: 'https://w3id.org/idsa/code/OS', + idShort: 'OS', + label: 'Ossetian, Ossetic', + sameAs: 'https://dbpedia.org/resource/ISO_639:os', + }, + + { + id: 'https://w3id.org/idsa/code/PA', + idShort: 'PA', + label: '(Eastern) Punjabi', + sameAs: 'https://dbpedia.org/resource/ISO_639:pa', + }, + + { + id: 'https://w3id.org/idsa/code/PI', + idShort: 'PI', + label: 'Pāli', + sameAs: 'https://dbpedia.org/resource/ISO_639:pi', + }, + + { + id: 'https://w3id.org/idsa/code/FA', + idShort: 'FA', + label: 'Persian (Farsi)', + sameAs: 'https://dbpedia.org/resource/ISO_639:fa', + }, + + { + id: 'https://w3id.org/idsa/code/PL', + idShort: 'PL', + label: 'Polish', + sameAs: 'https://dbpedia.org/resource/ISO_639:pl', + }, + + { + id: 'https://w3id.org/idsa/code/PS', + idShort: 'PS', + label: 'Pashto, Pushto', + sameAs: 'https://dbpedia.org/resource/ISO_639:ps', + }, + + { + id: 'https://w3id.org/idsa/code/PT', + idShort: 'PT', + label: 'Portuguese', + sameAs: 'https://dbpedia.org/resource/ISO_639:pt', + }, + + { + id: 'https://w3id.org/idsa/code/QU', + idShort: 'QU', + label: 'Quechua', + sameAs: 'https://dbpedia.org/resource/ISO_639:qu', + }, + + { + id: 'https://w3id.org/idsa/code/RM', + idShort: 'RM', + label: 'Romansh', + sameAs: 'https://dbpedia.org/resource/ISO_639:rm', + }, + + { + id: 'https://w3id.org/idsa/code/RN', + idShort: 'RN', + label: 'Kirundi', + sameAs: 'https://dbpedia.org/resource/ISO_639:rn', + }, + + { + id: 'https://w3id.org/idsa/code/RO', + idShort: 'RO', + label: 'Romanian', + sameAs: 'https://dbpedia.org/resource/ISO_639:ro', + }, + + { + id: 'https://w3id.org/idsa/code/RU', + idShort: 'RU', + label: 'Russian', + sameAs: 'https://dbpedia.org/resource/ISO_639:ru', + }, + + { + id: 'https://w3id.org/idsa/code/SA', + idShort: 'SA', + label: 'Sanskrit (Saṁskṛta)', + sameAs: 'https://dbpedia.org/resource/ISO_639:sa', + }, + + { + id: 'https://w3id.org/idsa/code/SC', + idShort: 'SC', + label: 'Sardinian', + sameAs: 'https://dbpedia.org/resource/ISO_639:sc', + }, + + { + id: 'https://w3id.org/idsa/code/SD', + idShort: 'SD', + label: 'Sindhi', + sameAs: 'https://dbpedia.org/resource/ISO_639:sd', + }, + + { + id: 'https://w3id.org/idsa/code/SE', + idShort: 'SE', + label: 'Northern Sami', + sameAs: 'https://dbpedia.org/resource/ISO_639:se', + }, + + { + id: 'https://w3id.org/idsa/code/SM', + idShort: 'SM', + label: 'Samoan', + sameAs: 'https://dbpedia.org/resource/ISO_639:sm', + }, + + { + id: 'https://w3id.org/idsa/code/SG', + idShort: 'SG', + label: 'Sango', + sameAs: 'https://dbpedia.org/resource/ISO_639:sg', + }, + + { + id: 'https://w3id.org/idsa/code/SR', + idShort: 'SR', + label: 'Serbian', + sameAs: 'https://dbpedia.org/resource/ISO_639:sr', + }, + + { + id: 'https://w3id.org/idsa/code/GD', + idShort: 'GD', + label: 'Scottish Gaelic, Gaelic', + sameAs: 'https://dbpedia.org/resource/ISO_639:gd', + }, + + { + id: 'https://w3id.org/idsa/code/SN', + idShort: 'SN', + label: 'Shona', + sameAs: 'https://dbpedia.org/resource/ISO_639:sn', + }, + + { + id: 'https://w3id.org/idsa/code/SI', + idShort: 'SI', + label: 'Sinhalese, Sinhala', + sameAs: 'https://dbpedia.org/resource/ISO_639:si', + }, + + { + id: 'https://w3id.org/idsa/code/SK', + idShort: 'SK', + label: 'Slovak', + sameAs: 'https://dbpedia.org/resource/ISO_639:sk', + }, + + { + id: 'https://w3id.org/idsa/code/SL', + idShort: 'SL', + label: 'Slovene', + sameAs: 'https://dbpedia.org/resource/ISO_639:sl', + }, + + { + id: 'https://w3id.org/idsa/code/SO', + idShort: 'SO', + label: 'Somali', + sameAs: 'https://dbpedia.org/resource/ISO_639:so', + }, + + { + id: 'https://w3id.org/idsa/code/ST', + idShort: 'ST', + label: 'outhern Sotho', + sameAs: 'https://dbpedia.org/resource/ISO_639:st', + }, + + { + id: 'https://w3id.org/idsa/code/ES', + idShort: 'ES', + label: 'Spanish', + sameAs: 'https://dbpedia.org/resource/ISO_639:es', + }, + + { + id: 'https://w3id.org/idsa/code/SU', + idShort: 'SU', + label: 'Sundanese', + sameAs: 'https://dbpedia.org/resource/ISO_639:su', + }, + + { + id: 'https://w3id.org/idsa/code/SW', + idShort: 'SW', + label: 'Swahili', + sameAs: 'https://dbpedia.org/resource/ISO_639:sw', + }, + + { + id: 'https://w3id.org/idsa/code/SS', + idShort: 'SS', + label: 'Swati', + sameAs: 'https://dbpedia.org/resource/ISO_639:ss', + }, + + { + id: 'https://w3id.org/idsa/code/SV', + idShort: 'SV', + label: 'Swedish', + sameAs: 'https://dbpedia.org/resource/ISO_639:sv', + }, + + { + id: 'https://w3id.org/idsa/code/TA', + idShort: 'TA', + label: 'Tamil', + sameAs: 'https://dbpedia.org/resource/ISO_639:ta', + }, + + { + id: 'https://w3id.org/idsa/code/TE', + idShort: 'TE', + label: 'Telugu', + sameAs: 'https://dbpedia.org/resource/ISO_639:te', + }, + + { + id: 'https://w3id.org/idsa/code/TG', + idShort: 'TG', + label: 'Tajik', + sameAs: 'https://dbpedia.org/resource/ISO_639:tg', + }, + + { + id: 'https://w3id.org/idsa/code/TH', + idShort: 'TH', + label: 'Thai', + sameAs: 'https://dbpedia.org/resource/ISO_639:th', + }, + + { + id: 'https://w3id.org/idsa/code/TI', + idShort: 'TI', + label: 'Tigrinya', + sameAs: 'https://dbpedia.org/resource/ISO_639:ti', + }, + + { + id: 'https://w3id.org/idsa/code/BO', + idShort: 'BO', + label: 'Tibetan Standard, Tibetan, Central', + sameAs: 'https://dbpedia.org/resource/ISO_639:bo', + }, + + { + id: 'https://w3id.org/idsa/code/TK', + idShort: 'TK', + label: 'Turkmen', + sameAs: 'https://dbpedia.org/resource/ISO_639:tk', + }, + + { + id: 'https://w3id.org/idsa/code/TL', + idShort: 'TL', + label: 'Tagalog', + sameAs: 'https://dbpedia.org/resource/ISO_639:tl', + }, + + { + id: 'https://w3id.org/idsa/code/TN', + idShort: 'TN', + label: 'Tswana', + sameAs: 'https://dbpedia.org/resource/ISO_639:tn', + }, + + { + id: 'https://w3id.org/idsa/code/TO', + idShort: 'TO', + label: 'Tonga (Tonga Islands)', + sameAs: 'https://dbpedia.org/resource/ISO_639:to', + }, + + { + id: 'https://w3id.org/idsa/code/TR', + idShort: 'TR', + label: 'Turkish', + sameAs: 'https://dbpedia.org/resource/ISO_639:tr', + }, + + { + id: 'https://w3id.org/idsa/code/TS', + idShort: 'TS', + label: 'Tsonga', + sameAs: 'https://dbpedia.org/resource/ISO_639:ts', + }, + + { + id: 'https://w3id.org/idsa/code/TT', + idShort: 'TT', + label: 'Tatar', + sameAs: 'https://dbpedia.org/resource/ISO_639:tt', + }, + + { + id: 'https://w3id.org/idsa/code/TW', + idShort: 'TW', + label: 'Twi', + sameAs: 'https://dbpedia.org/resource/ISO_639:tw', + }, + + { + id: 'https://w3id.org/idsa/code/TY', + idShort: 'TY', + label: 'Tahitian', + sameAs: 'https://dbpedia.org/resource/ISO_639:ty', + }, + + { + id: 'https://w3id.org/idsa/code/UG', + idShort: 'UG', + label: 'Uyghur', + sameAs: 'https://dbpedia.org/resource/ISO_639:ug', + }, + + { + id: 'https://w3id.org/idsa/code/UK', + idShort: 'UK', + label: 'Ukrainian', + sameAs: 'https://dbpedia.org/resource/ISO_639:uk', + }, + + { + id: 'https://w3id.org/idsa/code/UR', + idShort: 'UR', + label: 'Urdu', + sameAs: 'https://dbpedia.org/resource/ISO_639:ur', + }, + + { + id: 'https://w3id.org/idsa/code/UZ', + idShort: 'UZ', + label: 'Uzbek', + sameAs: 'https://dbpedia.org/resource/ISO_639:uz', + }, + + { + id: 'https://w3id.org/idsa/code/VE', + idShort: 'VE', + label: 'Venda', + sameAs: 'https://dbpedia.org/resource/ISO_639:ve', + }, + + { + id: 'https://w3id.org/idsa/code/VI', + idShort: 'VI', + label: 'Vietnamese', + sameAs: 'https://dbpedia.org/resource/ISO_639:vi', + }, + + { + id: 'https://w3id.org/idsa/code/VO', + idShort: 'VO', + label: 'Volapük', + sameAs: 'https://dbpedia.org/resource/ISO_639:vo', + }, + + { + id: 'https://w3id.org/idsa/code/WA', + idShort: 'WA', + label: 'Walloon', + sameAs: 'https://dbpedia.org/resource/ISO_639:wa', + }, + + { + id: 'https://w3id.org/idsa/code/CY', + idShort: 'CY', + label: 'Welsh', + sameAs: 'https://dbpedia.org/resource/ISO_639:cy', + }, + + { + id: 'https://w3id.org/idsa/code/WO', + idShort: 'WO', + label: 'Wolof', + sameAs: 'https://dbpedia.org/resource/ISO_639:wo', + }, + + { + id: 'https://w3id.org/idsa/code/FY', + idShort: 'FY', + label: 'Western Frisian', + sameAs: 'https://dbpedia.org/resource/ISO_639:fy', + }, + + { + id: 'https://w3id.org/idsa/code/XH', + idShort: 'XH', + label: 'Xhosa', + sameAs: 'https://dbpedia.org/resource/ISO_639:xh', + }, + + { + id: 'https://w3id.org/idsa/code/YI', + idShort: 'YI', + label: 'Yiddish', + sameAs: 'https://dbpedia.org/resource/ISO_639:yi', + }, + + { + id: 'https://w3id.org/idsa/code/YO', + idShort: 'YO', + label: 'Yoruba', + sameAs: 'https://dbpedia.org/resource/ISO_639:yo', + }, + + { + id: 'https://w3id.org/idsa/code/ZA', + idShort: 'ZA', + label: 'Zhuang, Chuang', + sameAs: 'https://dbpedia.org/resource/ISO_639:za', + }, + + { + id: 'https://w3id.org/idsa/code/ZU', + idShort: 'ZU', + label: 'Zulu', + sameAs: 'https://dbpedia.org/resource/ISO_639:zu', + }, +]; diff --git a/connector-ui/src/app/shared/form-elements/language-select/language-select-item.service.ts b/connector-ui/src/app/shared/form-elements/language-select/language-select-item.service.ts new file mode 100644 index 000000000..4c1f8da9a --- /dev/null +++ b/connector-ui/src/app/shared/form-elements/language-select/language-select-item.service.ts @@ -0,0 +1,70 @@ +import {Injectable} from '@angular/core'; +import {LANGUAGE_SELECT_DATA} from './language-select-data'; +import {LanguageSelectItem} from './language-select-item'; + +/** + * Access list of available LanguageSelectItems + */ +@Injectable({providedIn: 'root'}) +export class LanguageSelectItemService { + /** + * Partition LanguageSelectItems into highlighted and other. + * Usability: See important options first and close to each other. + */ + highlightItemIds = [ + 'https://w3id.org/idsa/code/MULTI_LINGUAL', + 'https://w3id.org/idsa/code/DE', + 'https://w3id.org/idsa/code/EN', + ]; + highlightItems: LanguageSelectItem[]; + otherItems: LanguageSelectItem[]; + itemsByKeyword: Map; + + constructor() { + this.highlightItems = this.buildHighlightItems(); + this.otherItems = this.buildOtherItems(); + this.itemsByKeyword = this.buildItemLookupMap(); + } + + /** + * Find LanguageSelectItem by id + * @param id language select item id + */ + findById(id: string): LanguageSelectItem { + let item = this.itemsByKeyword.get(id); + if (!item) { + item = {id, label: id}; + } + return item; + } + + english(): LanguageSelectItem { + return this.findById('https://w3id.org/idsa/code/EN'); + } + + private buildHighlightItems(): LanguageSelectItem[] { + return LANGUAGE_SELECT_DATA.filter((it) => + this.highlightItemIds.includes(it.id), + ); + } + + private buildOtherItems(): LanguageSelectItem[] { + return LANGUAGE_SELECT_DATA.filter( + (it) => !this.highlightItemIds.includes(it.id), + ); + } + + private buildItemLookupMap(): Map { + const map = new Map(); + LANGUAGE_SELECT_DATA.forEach((it) => { + map.set(it.id, it); + if (it.idShort) { + map.set(it.idShort, it); + } + if (it.sameAs) { + map.set(it.sameAs, it); + } + }); + return map; + } +} diff --git a/connector-ui/src/app/shared/form-elements/language-select/language-select-item.ts b/connector-ui/src/app/shared/form-elements/language-select/language-select-item.ts new file mode 100644 index 000000000..175da8983 --- /dev/null +++ b/connector-ui/src/app/shared/form-elements/language-select/language-select-item.ts @@ -0,0 +1,7 @@ +export interface LanguageSelectItem { + id: string; + label: string; + comment?: string; + idShort?: string; + sameAs?: string; +} diff --git a/connector-ui/src/app/shared/form-elements/language-select/language-select.component.html b/connector-ui/src/app/shared/form-elements/language-select/language-select.component.html new file mode 100644 index 000000000..fee39aa15 --- /dev/null +++ b/connector-ui/src/app/shared/form-elements/language-select/language-select.component.html @@ -0,0 +1,18 @@ + + + + - + + {{ item.label }} + + + + {{ item.label }} + + + diff --git a/connector-ui/src/app/shared/form-elements/language-select/language-select.component.ts b/connector-ui/src/app/shared/form-elements/language-select/language-select.component.ts new file mode 100644 index 000000000..8d244a96d --- /dev/null +++ b/connector-ui/src/app/shared/form-elements/language-select/language-select.component.ts @@ -0,0 +1,18 @@ +import {Component, Input} from '@angular/core'; +import {FormControl} from '@angular/forms'; +import {LanguageSelectItem} from './language-select-item'; +import {LanguageSelectItemService} from './language-select-item.service'; + +@Component({ + selector: 'language-select', + templateUrl: 'language-select.component.html', +}) +export class LanguageSelectComponent { + @Input() + label: string | null = null; + + @Input() + control!: FormControl; + + constructor(public items: LanguageSelectItemService) {} +} diff --git a/connector-ui/src/app/shared/form-elements/participant-id-select/participant-id-select.component.html b/connector-ui/src/app/shared/form-elements/participant-id-select/participant-id-select.component.html new file mode 100644 index 000000000..d7804ad1a --- /dev/null +++ b/connector-ui/src/app/shared/form-elements/participant-id-select/participant-id-select.component.html @@ -0,0 +1,29 @@ + + Consumer's Participant IDs + + + {{ participantId }} + + + + + + You can find the connector’s Participant ID under “Connector Properties” on + its dashboard. Ask for it from the data consumers you want to share your + data offer with. + + diff --git a/connector-ui/src/app/shared/form-elements/participant-id-select/participant-id-select.component.ts b/connector-ui/src/app/shared/form-elements/participant-id-select/participant-id-select.component.ts new file mode 100644 index 000000000..931c06645 --- /dev/null +++ b/connector-ui/src/app/shared/form-elements/participant-id-select/participant-id-select.component.ts @@ -0,0 +1,38 @@ +import {COMMA, ENTER, SEMICOLON} from '@angular/cdk/keycodes'; +import {Component, HostBinding, Input} from '@angular/core'; +import {FormControl} from '@angular/forms'; +import {MatChipInputEvent} from '@angular/material/chips'; +import {ParticipantIdLocalization} from '../../../core/services/participant-id-localization'; +import {removeOnce} from '../../../core/utils/array-utils'; + +@Component({ + selector: 'participant-id-select', + templateUrl: 'participant-id-select.component.html', +}) +export class ParticipantIdSelectComponent { + separatorKeysCodes: number[] = [ENTER, COMMA, SEMICOLON]; + + @Input() + control!: FormControl; + + @HostBinding('class.flex') + @HostBinding('class.flex-row') + cls = true; + + constructor(public participantIdLocalization: ParticipantIdLocalization) {} + + remove(participantId: string) { + this.control.setValue(removeOnce(this.control.value, participantId)); + } + + add(event: MatChipInputEvent): void { + const participantIds = (event.value || '') + .split(/[,;]/) + .map((it) => it.trim()) + .filter((it) => it); + if (participantIds.length) { + this.control.setValue([...this.control.value, ...participantIds]); + } + event.chipInput.clear(); + } +} diff --git a/connector-ui/src/app/shared/form-elements/policy-operator-select/policy-operator-select.component.html b/connector-ui/src/app/shared/form-elements/policy-operator-select/policy-operator-select.component.html new file mode 100644 index 000000000..039f427a2 --- /dev/null +++ b/connector-ui/src/app/shared/form-elements/policy-operator-select/policy-operator-select.component.html @@ -0,0 +1,11 @@ + + {{ label }} + + + {{ operator.title }} + + + diff --git a/connector-ui/src/app/shared/form-elements/policy-operator-select/policy-operator-select.component.ts b/connector-ui/src/app/shared/form-elements/policy-operator-select/policy-operator-select.component.ts new file mode 100644 index 000000000..3ae0c22c2 --- /dev/null +++ b/connector-ui/src/app/shared/form-elements/policy-operator-select/policy-operator-select.component.ts @@ -0,0 +1,24 @@ +import {Component, HostBinding, Input} from '@angular/core'; +import {UntypedFormControl} from '@angular/forms'; +import {TranslateService} from '@ngx-translate/core'; +import {PolicyOperatorConfig} from '../../business/policy-editor/model/policy-operators'; + +@Component({ + selector: 'policy-operator-select', + templateUrl: 'policy-operator-select.component.html', +}) +export class PolicyOperatorSelectComponent { + @Input() + operators: PolicyOperatorConfig[] = []; + + @Input() + control!: UntypedFormControl; + + @HostBinding('class.flex') + @HostBinding('class.flex-row') + cls = true; + + constructor(public translationService: TranslateService) {} + + label = this.translationService.instant('general.operator'); +} diff --git a/connector-ui/src/app/shared/form-elements/transport-mode-select/transport-mode-select-data.ts b/connector-ui/src/app/shared/form-elements/transport-mode-select/transport-mode-select-data.ts new file mode 100644 index 000000000..9444eb723 --- /dev/null +++ b/connector-ui/src/app/shared/form-elements/transport-mode-select/transport-mode-select-data.ts @@ -0,0 +1,23 @@ +import {TransportModeSelectItem} from './transport-mode-select-item'; + +export const TRANSPORT_MODE_SELECT_DATA: TransportModeSelectItem[] = [ + { + id: 'Rail', + label: 'Rail', + }, + + { + id: 'Road', + label: 'Road', + }, + + { + id: 'Water', + label: 'Water', + }, + + { + id: 'Air', + label: 'Air', + }, +]; diff --git a/connector-ui/src/app/shared/form-elements/transport-mode-select/transport-mode-select-item.service.ts b/connector-ui/src/app/shared/form-elements/transport-mode-select/transport-mode-select-item.service.ts new file mode 100644 index 000000000..241865c62 --- /dev/null +++ b/connector-ui/src/app/shared/form-elements/transport-mode-select/transport-mode-select-item.service.ts @@ -0,0 +1,27 @@ +import {Injectable} from '@angular/core'; +import {associateBy} from 'src/app/core/utils/map-utils'; +import {TRANSPORT_MODE_SELECT_DATA} from './transport-mode-select-data'; +import {TransportModeSelectItem} from './transport-mode-select-item'; + +/** + * Access list of available TransportModeSelectItems + */ +@Injectable({providedIn: 'root'}) +export class TransportModeSelectItemService { + itemsById = associateBy(TRANSPORT_MODE_SELECT_DATA, (it) => it.id); + + /** + * Find TransportModeSelectItem by id + * @param id language select item id + */ + findById(id: string): TransportModeSelectItem { + const item = this.itemsById.get(id); + if (item != null) { + return item; + } + return { + id, + label: id, + }; + } +} diff --git a/connector-ui/src/app/shared/form-elements/transport-mode-select/transport-mode-select-item.ts b/connector-ui/src/app/shared/form-elements/transport-mode-select/transport-mode-select-item.ts new file mode 100644 index 000000000..f516b6cfa --- /dev/null +++ b/connector-ui/src/app/shared/form-elements/transport-mode-select/transport-mode-select-item.ts @@ -0,0 +1,4 @@ +export interface TransportModeSelectItem { + id: string; + label: string; +} diff --git a/connector-ui/src/app/shared/form-elements/transport-mode-select/transport-mode-select.component.html b/connector-ui/src/app/shared/form-elements/transport-mode-select/transport-mode-select.component.html new file mode 100644 index 000000000..fd8e9a3c3 --- /dev/null +++ b/connector-ui/src/app/shared/form-elements/transport-mode-select/transport-mode-select.component.html @@ -0,0 +1,12 @@ + + + + + {{ item.label }} + + + diff --git a/connector-ui/src/app/shared/form-elements/transport-mode-select/transport-mode-select.component.ts b/connector-ui/src/app/shared/form-elements/transport-mode-select/transport-mode-select.component.ts new file mode 100644 index 000000000..4a5edd6e8 --- /dev/null +++ b/connector-ui/src/app/shared/form-elements/transport-mode-select/transport-mode-select.component.ts @@ -0,0 +1,18 @@ +import {Component, Input} from '@angular/core'; +import {FormControl} from '@angular/forms'; +import {TRANSPORT_MODE_SELECT_DATA} from './transport-mode-select-data'; +import {TransportModeSelectItem} from './transport-mode-select-item'; + +@Component({ + selector: 'transport-mode-select', + templateUrl: 'transport-mode-select.component.html', +}) +export class TransportModeSelectComponent { + @Input() + label!: string; + + @Input() + control!: FormControl; + + items = TRANSPORT_MODE_SELECT_DATA; +} diff --git a/connector-ui/src/app/shared/pipes-and-directives/autofocus.direcitive.ts b/connector-ui/src/app/shared/pipes-and-directives/autofocus.direcitive.ts new file mode 100644 index 000000000..20f0377ac --- /dev/null +++ b/connector-ui/src/app/shared/pipes-and-directives/autofocus.direcitive.ts @@ -0,0 +1,16 @@ +import {AfterViewInit, Directive, ElementRef} from '@angular/core'; + +@Directive({ + selector: '[autofocus]', +}) +export class AutofocusDirective implements AfterViewInit { + constructor(private elementRef: ElementRef) {} + + ngAfterViewInit() { + const ele = this.elementRef.nativeElement as HTMLInputElement; + setTimeout(() => { + ele.focus(); + ele.select(); + }, 100); + } +} diff --git a/connector-ui/src/app/shared/pipes-and-directives/compare-by-field.pipe.ts b/connector-ui/src/app/shared/pipes-and-directives/compare-by-field.pipe.ts new file mode 100644 index 000000000..59b6f4b34 --- /dev/null +++ b/connector-ui/src/app/shared/pipes-and-directives/compare-by-field.pipe.ts @@ -0,0 +1,11 @@ +import {Pipe, PipeTransform} from '@angular/core'; + +/** + * Creates Compare By Function for Angular Material compareWith parameters + */ +@Pipe({name: 'compareByField'}) +export class CompareByFieldPipe implements PipeTransform { + transform(key: string): (a: any, b: any) => boolean { + return (a, b) => a === b || (a != null && b != null && a[key] === b[key]); + } +} diff --git a/connector-ui/src/app/shared/pipes-and-directives/external-link.directive.ts b/connector-ui/src/app/shared/pipes-and-directives/external-link.directive.ts new file mode 100644 index 000000000..c47bcbbc2 --- /dev/null +++ b/connector-ui/src/app/shared/pipes-and-directives/external-link.directive.ts @@ -0,0 +1,14 @@ +import {AfterViewInit, Directive, ElementRef} from '@angular/core'; + +@Directive({ + selector: '[externalLink]', +}) +export class ExternalLinkDirective implements AfterViewInit { + constructor(private elementRef: ElementRef) {} + + ngAfterViewInit() { + const element = this.elementRef.nativeElement as HTMLAnchorElement; + element.setAttribute('target', '_blank'); + element.setAttribute('rel', 'noopener noreferrer'); + } +} diff --git a/connector-ui/src/app/shared/pipes-and-directives/is-active-feature.pipe.ts b/connector-ui/src/app/shared/pipes-and-directives/is-active-feature.pipe.ts new file mode 100644 index 000000000..f39cf3ad8 --- /dev/null +++ b/connector-ui/src/app/shared/pipes-and-directives/is-active-feature.pipe.ts @@ -0,0 +1,15 @@ +import {Pipe, PipeTransform} from '@angular/core'; +import {ActiveFeatureSet} from '../../core/config/active-feature-set'; +import {EdcUiFeature} from '../../core/config/profiles/edc-ui-feature'; + +/** + * Easily check for active features in angular templates. + */ +@Pipe({name: 'isActiveFeature'}) +export class IsActiveFeaturePipe implements PipeTransform { + constructor(private activeFeatureSet: ActiveFeatureSet) {} + + transform(feature: EdcUiFeature): boolean { + return this.activeFeatureSet.has(feature); + } +} diff --git a/connector-ui/src/app/shared/pipes-and-directives/remove-class.directive.ts b/connector-ui/src/app/shared/pipes-and-directives/remove-class.directive.ts new file mode 100644 index 000000000..c782b8e37 --- /dev/null +++ b/connector-ui/src/app/shared/pipes-and-directives/remove-class.directive.ts @@ -0,0 +1,37 @@ +import { + AfterViewChecked, + AfterViewInit, + Directive, + ElementRef, + Input, +} from '@angular/core'; + +/** + * Angular Material automatically adds CSS classes when you use their directives. + * + * But if you don't use their directives, the scrollbars in dialogs break, for example. + */ +@Directive({ + selector: '[removeClass]', +}) +export class RemoveClassDirective implements AfterViewChecked, AfterViewInit { + @Input() + removeClass!: string; + + constructor(private elementRef: ElementRef) {} + + ngAfterViewInit() { + this.removeClassIfNecessary(); + } + + ngAfterViewChecked() { + this.removeClassIfNecessary(); + } + + private removeClassIfNecessary() { + const classList = (this.elementRef.nativeElement as HTMLElement).classList; + if (classList.contains(this.removeClass)) { + classList.remove(this.removeClass); + } + } +} diff --git a/connector-ui/src/app/shared/pipes-and-directives/track-by-field.directive.ts b/connector-ui/src/app/shared/pipes-and-directives/track-by-field.directive.ts new file mode 100644 index 000000000..3e39e5e79 --- /dev/null +++ b/connector-ui/src/app/shared/pipes-and-directives/track-by-field.directive.ts @@ -0,0 +1,24 @@ +import {NgForOf} from '@angular/common'; +import {Attribute, Directive, Host, TrackByFunction} from '@angular/core'; + +export const newTrackByFn = + (key: keyof T): TrackByFunction => + (_, item: T) => + item == null ? null : item[key] ?? item; + +/** + * Creates Track By Function for ngFor loops + */ +@Directive({ + selector: '[trackByField]', +}) +export class TrackByFieldDirective { + constructor( + @Host() ngForOf: NgForOf, + @Attribute('trackByField') private readonly trackByField: string, + ) { + if (!ngForOf.ngForTrackBy) { + ngForOf.ngForTrackBy = newTrackByFn(this.trackByField); + } + } +} diff --git a/connector-ui/src/app/shared/pipes-and-directives/values.pipe.ts b/connector-ui/src/app/shared/pipes-and-directives/values.pipe.ts new file mode 100644 index 000000000..13165b904 --- /dev/null +++ b/connector-ui/src/app/shared/pipes-and-directives/values.pipe.ts @@ -0,0 +1,11 @@ +import {Pipe, PipeTransform} from '@angular/core'; + +/** + * `Object.values(...)` can't be used from angular templates. + */ +@Pipe({name: 'values'}) +export class ValuesPipe implements PipeTransform { + transform(obj: T): T[keyof T][] { + return Object.values(obj || {}); + } +} diff --git a/connector-ui/src/app/shared/shared.module.ts b/connector-ui/src/app/shared/shared.module.ts new file mode 100644 index 000000000..25b656730 --- /dev/null +++ b/connector-ui/src/app/shared/shared.module.ts @@ -0,0 +1,287 @@ +import {ClipboardModule} from '@angular/cdk/clipboard'; +import {CommonModule} from '@angular/common'; +import {NgModule} from '@angular/core'; +import {FormsModule, ReactiveFormsModule} from '@angular/forms'; +import {MatBadgeModule} from '@angular/material/badge'; +import {MatButtonModule} from '@angular/material/button'; +import {MatButtonToggleModule} from '@angular/material/button-toggle'; +import {MatCardModule} from '@angular/material/card'; +import {MatCheckboxModule} from '@angular/material/checkbox'; +import {MatChipsModule} from '@angular/material/chips'; +import { + DateAdapter, + MAT_DATE_LOCALE, + MatNativeDateModule, +} from '@angular/material/core'; +import {MatDatepickerModule} from '@angular/material/datepicker'; +import {MatDialogModule} from '@angular/material/dialog'; +import {MatDividerModule} from '@angular/material/divider'; +import {MatExpansionModule} from '@angular/material/expansion'; +import { + MAT_FORM_FIELD_DEFAULT_OPTIONS, + MatFormFieldDefaultOptions, + MatFormFieldModule, +} from '@angular/material/form-field'; +import {MatGridListModule} from '@angular/material/grid-list'; +import {MatIconModule} from '@angular/material/icon'; +import {MatInputModule} from '@angular/material/input'; +import {MatListModule} from '@angular/material/list'; +import {MatMenuModule} from '@angular/material/menu'; +import {MatPaginatorModule} from '@angular/material/paginator'; +import {MatProgressBarModule} from '@angular/material/progress-bar'; +import {MatProgressSpinnerModule} from '@angular/material/progress-spinner'; +import {MatRadioModule} from '@angular/material/radio'; +import {MatSelectModule} from '@angular/material/select'; +import {MatSidenavModule} from '@angular/material/sidenav'; +import {MatSlideToggleModule} from '@angular/material/slide-toggle'; +import {MatSnackBarModule} from '@angular/material/snack-bar'; +import {MatStepperModule} from '@angular/material/stepper'; +import {MatTableModule} from '@angular/material/table'; +import {MatTabsModule} from '@angular/material/tabs'; +import {MatToolbarModule} from '@angular/material/toolbar'; +import { + MAT_TOOLTIP_DEFAULT_OPTIONS, + MAT_TOOLTIP_DEFAULT_OPTIONS_FACTORY, + MatTooltipDefaultOptions, + MatTooltipModule, +} from '@angular/material/tooltip'; +import {TranslateModule} from '@ngx-translate/core'; +import {NgChartsModule} from 'ng2-charts'; +import {NgxJsonViewerModule} from 'ngx-json-viewer'; +import {CustomDateAdapter} from '../core/adapters/custom-date-adapter'; +import {AssetCardTagListComponent} from './business/asset-card-tag-list/asset-card-tag-list.component'; +import {AssetDetailDialogDataService} from './business/asset-detail-dialog/asset-detail-dialog-data.service'; +import {AssetDetailDialogComponent} from './business/asset-detail-dialog/asset-detail-dialog.component'; +import {AssetDetailDialogService} from './business/asset-detail-dialog/asset-detail-dialog.service'; +import {AssetPropertyGridGroupBuilder} from './business/asset-detail-dialog/asset-property-grid-group-builder'; +import {PolicyPropertyFieldBuilder} from './business/asset-detail-dialog/policy-property-field-builder'; +import {ConditionsForUseDialogComponent} from './business/conditions-for-use-dialog/conditions-for-use-dialog.component'; +import {ConditionsForUseDialogService} from './business/conditions-for-use-dialog/conditions-for-use-dialog.service'; +import {ContractOfferIconComponent} from './business/contract-offer-icon/contract-offer-icon.component'; +import {ContractOfferMiniListComponent} from './business/contract-offer-mini-list/contract-offer-mini-list.component'; +import {DataOfferCardsComponent} from './business/data-offer-cards/data-offer-cards.component'; +import {EditAssetFormComponent} from './business/edit-asset-form/edit-asset-form.component'; +import {InitiateNegotiationConfirmTosDialogComponent} from './business/initiate-negotiation-confirm-tos-dialog/initiate-negotiation-confirm-tos-dialog.component'; +import {PolicyFormAddMenuComponent} from './business/policy-editor/editor/policy-form-add-menu/policy-form-add-menu.component'; +import {PolicyFormExpressionConstraintComponent} from './business/policy-editor/editor/policy-form-expression-constraint/policy-form-expression-constraint.component'; +import {PolicyFormExpressionEmptyComponent} from './business/policy-editor/editor/policy-form-expression-empty/policy-form-expression-empty.component'; +import {PolicyFormExpressionMultiComponent} from './business/policy-editor/editor/policy-form-expression-multi/policy-form-expression-multi.component'; +import {PolicyFormExpressionComponent} from './business/policy-editor/editor/policy-form-expression/policy-form-expression.component'; +import {PolicyFormRemoveButton} from './business/policy-editor/editor/policy-form-remove-button/policy-form-remove-button.component'; +import {PolicyExpressionRecipeService} from './business/policy-editor/editor/recipes/policy-expression-recipe.service'; +import {TimespanRestrictionDialogComponent} from './business/policy-editor/editor/recipes/timespan-restriction-dialog/timespan-restriction-dialog.component'; +import {PolicyMapper} from './business/policy-editor/model/policy-mapper'; +import {PolicyMultiExpressionService} from './business/policy-editor/model/policy-multi-expressions'; +import {PolicyOperatorService} from './business/policy-editor/model/policy-operators'; +import {PolicyVerbService} from './business/policy-editor/model/policy-verbs'; +import {PolicyExpressionComponent} from './business/policy-editor/renderer/policy-expression/policy-expression.component'; +import {PolicyRendererComponent} from './business/policy-editor/renderer/policy-renderer/policy-renderer.component'; +import {TransferHistoryMiniListComponent} from './business/transfer-history-mini-list/transfer-history-mini-list.component'; +import {AgoComponent} from './common/ago/ago.component'; +import {AgoPipe} from './common/ago/ago.pipe'; +import {ConfirmationDialogComponent} from './common/confirmation-dialog/confirmation-dialog.component'; +import {DateComponent} from './common/date/date.component'; +import {EmptyStateComponent} from './common/empty-state/empty-state.component'; +import {ErrorStateComponent} from './common/error-state/error-state.component'; +import {HorizontalSectionDividerComponent} from './common/horizontal-section-divider/horizontal-section-divider.component'; +import {JsonDialogComponent} from './common/json-dialog/json-dialog.component'; +import {JsonDialogService} from './common/json-dialog/json-dialog.service'; +import {LanguageSelectorComponent} from './common/language-selector/language-selector.component'; +import {LoadingStateComponent} from './common/loading-state/loading-state.component'; +import {MarkdownDescriptionComponent} from './common/markdown-description/markdown-description.component'; +import {PropertyGridGroupComponent} from './common/property-grid-group/property-grid-group.component'; +import {PropertyGridComponent} from './common/property-grid/property-grid.component'; +import {TranslateWithSlotComponent} from './common/translate-with-slot/translate-with-slot.component'; +import {TruncatedShortDescription} from './common/truncated-short-description/truncated-short-description.component'; +import {UrlListDialogComponent} from './common/url-list-dialog/url-list-dialog.component'; +import {UrlListDialogService} from './common/url-list-dialog/url-list-dialog.service'; +import {DataAddressTypeSelectComponent} from './form-elements/data-address-type-select/data-address-type-select.component'; +import {DataCategorySelectComponent} from './form-elements/data-category-select/data-category-select.component'; +import {DataSubcategoryItemsPipe} from './form-elements/data-subcategory-select/data-subcategory-items.pipe'; +import {DataSubcategorySelectComponent} from './form-elements/data-subcategory-select/data-subcategory-select.component'; +import {EditAssetFormDataAddressTypeSelectComponent} from './form-elements/edit-asset-form-data-address-type-select/edit-asset-form-data-address-type-select.component'; +import {EditAssetFormGroupComponent} from './form-elements/edit-asset-form-group/edit-asset-form-group.component'; +import {EditAssetFormInputComponent} from './form-elements/edit-asset-form-input/edit-asset-form-input.component'; +import {EditAssetFormLabelComponent} from './form-elements/edit-asset-form-label/edit-asset-form-label.component'; +import {EditAssetFormTextareaComponent} from './form-elements/edit-asset-form-textarea/edit-asset-form-textarea.component'; +import {KeywordSelectComponent} from './form-elements/keyword-select/keyword-select.component'; +import {LanguageSelectComponent} from './form-elements/language-select/language-select.component'; +import {ParticipantIdSelectComponent} from './form-elements/participant-id-select/participant-id-select.component'; +import {PolicyOperatorSelectComponent} from './form-elements/policy-operator-select/policy-operator-select.component'; +import {TransportModeSelectComponent} from './form-elements/transport-mode-select/transport-mode-select.component'; +import {AutofocusDirective} from './pipes-and-directives/autofocus.direcitive'; +import {CompareByFieldPipe} from './pipes-and-directives/compare-by-field.pipe'; +import {ExternalLinkDirective} from './pipes-and-directives/external-link.directive'; +import {IsActiveFeaturePipe} from './pipes-and-directives/is-active-feature.pipe'; +import {RemoveClassDirective} from './pipes-and-directives/remove-class.directive'; +import {TrackByFieldDirective} from './pipes-and-directives/track-by-field.directive'; +import {ValuesPipe} from './pipes-and-directives/values.pipe'; + +const COMPONENTS: NgModule['declarations'] = [ + // ./common + AgoComponent, + AgoPipe, + ConfirmationDialogComponent, + DateComponent, + EmptyStateComponent, + ErrorStateComponent, + HorizontalSectionDividerComponent, + JsonDialogComponent, + LanguageSelectorComponent, + LoadingStateComponent, + MarkdownDescriptionComponent, + PropertyGridComponent, + PropertyGridGroupComponent, + TruncatedShortDescription, + UrlListDialogComponent, + TranslateWithSlotComponent, + + // ./business + AssetCardTagListComponent, + AssetDetailDialogComponent, + ConditionsForUseDialogComponent, + ContractOfferIconComponent, + ContractOfferMiniListComponent, + DataOfferCardsComponent, + InitiateNegotiationConfirmTosDialogComponent, + TransferHistoryMiniListComponent, + + // ./business/asset-edit-form + EditAssetFormComponent, + EditAssetFormGroupComponent, + EditAssetFormLabelComponent, + EditAssetFormInputComponent, + EditAssetFormTextareaComponent, + DataSubcategorySelectComponent, + DataSubcategoryItemsPipe, + + // ./business/policy-editor + PolicyFormAddMenuComponent, + PolicyFormExpressionComponent, + PolicyFormExpressionEmptyComponent, + PolicyFormExpressionConstraintComponent, + PolicyFormExpressionMultiComponent, + PolicyFormRemoveButton, + TimespanRestrictionDialogComponent, + PolicyRendererComponent, + PolicyExpressionComponent, + + // ./form-elements + DataAddressTypeSelectComponent, + DataCategorySelectComponent, + DataSubcategorySelectComponent, + DataSubcategoryItemsPipe, + EditAssetFormDataAddressTypeSelectComponent, + EditAssetFormGroupComponent, + EditAssetFormInputComponent, + EditAssetFormLabelComponent, + EditAssetFormTextareaComponent, + KeywordSelectComponent, + LanguageSelectorComponent, + LanguageSelectComponent, + ParticipantIdSelectComponent, + PolicyOperatorSelectComponent, + TransportModeSelectComponent, + + // ./pipes-and-directives + AutofocusDirective, + CompareByFieldPipe, + ExternalLinkDirective, + IsActiveFeaturePipe, + RemoveClassDirective, + TrackByFieldDirective, + ValuesPipe, +]; + +const MODULES = [ + // Angular + TranslateModule, + + // Angular CDK + ClipboardModule, + + // Angular Material + MatBadgeModule, + MatButtonModule, + MatButtonToggleModule, + MatCardModule, + MatCheckboxModule, + MatChipsModule, + MatDatepickerModule, + MatDialogModule, + MatDividerModule, + MatExpansionModule, + MatFormFieldModule, + MatGridListModule, + MatIconModule, + MatInputModule, + MatListModule, + MatMenuModule, + MatNativeDateModule, + MatPaginatorModule, + MatProgressBarModule, + MatProgressSpinnerModule, + MatRadioModule, + MatSelectModule, + MatSidenavModule, + MatSlideToggleModule, + MatSnackBarModule, + MatStepperModule, + MatTableModule, + MatTabsModule, + MatToolbarModule, + MatTooltipModule, + + // NGX Json Viewer + NgxJsonViewerModule, + + // NgCharts + NgChartsModule, +]; + +@NgModule({ + imports: [ + // Angular + CommonModule, + FormsModule, + ReactiveFormsModule, + + // Angular Material + ...MODULES, + ], + exports: [...MODULES, ...COMPONENTS], + declarations: COMPONENTS, + providers: [ + AssetDetailDialogDataService, + AssetDetailDialogService, + AssetPropertyGridGroupBuilder, + ConditionsForUseDialogService, + PolicyPropertyFieldBuilder, + JsonDialogService, + PolicyExpressionRecipeService, + UrlListDialogService, + PolicyMultiExpressionService, + PolicyOperatorService, + PolicyVerbService, + PolicyMapper, + + {provide: DateAdapter, useClass: CustomDateAdapter}, + + {provide: MAT_DATE_LOCALE, useValue: 'en-GB'}, + { + provide: MAT_FORM_FIELD_DEFAULT_OPTIONS, + useValue: { + appearance: 'outline', + color: 'accent', + } as MatFormFieldDefaultOptions, + }, + { + provide: MAT_TOOLTIP_DEFAULT_OPTIONS, + useValue: { + ...MAT_TOOLTIP_DEFAULT_OPTIONS_FACTORY(), + disableTooltipInteractivity: true, + }, + }, + ], +}) +export class SharedModule {} diff --git a/connector-ui/src/assets/config/.gitignore b/connector-ui/src/assets/config/.gitignore new file mode 100644 index 000000000..f6bcf9c06 --- /dev/null +++ b/connector-ui/src/assets/config/.gitignore @@ -0,0 +1,8 @@ +# This file will be generated +# - in local dev before running ng serve +# - in docker container before starting nginx +app-config.json +app-configuration.json + +# Any backup files / old files +app.config*.json diff --git a/connector-ui/src/assets/config/ui-build-date.txt b/connector-ui/src/assets/config/ui-build-date.txt new file mode 100644 index 000000000..162dad10f --- /dev/null +++ b/connector-ui/src/assets/config/ui-build-date.txt @@ -0,0 +1 @@ +2023-05-25T14:30:00Z diff --git a/connector-ui/src/assets/config/version.txt b/connector-ui/src/assets/config/version.txt new file mode 100644 index 000000000..25ac521fa --- /dev/null +++ b/connector-ui/src/assets/config/version.txt @@ -0,0 +1,3 @@ +The GitHub Pipeline .github/workflows/build-and-release-image.yaml will replace this file during build time with +the commit information (SHA, author and commit message) of the built image. +This file will be served to allow checking for the exact source code version of a deployed edc-ui. diff --git a/connector-ui/src/assets/fonts/Inter-Bold.woff2 b/connector-ui/src/assets/fonts/Inter-Bold.woff2 new file mode 100644 index 000000000..0f1b15763 Binary files /dev/null and b/connector-ui/src/assets/fonts/Inter-Bold.woff2 differ diff --git a/connector-ui/src/assets/fonts/Inter-Light.woff2 b/connector-ui/src/assets/fonts/Inter-Light.woff2 new file mode 100644 index 000000000..dbe61437a Binary files /dev/null and b/connector-ui/src/assets/fonts/Inter-Light.woff2 differ diff --git a/connector-ui/src/assets/fonts/Inter-Medium.woff2 b/connector-ui/src/assets/fonts/Inter-Medium.woff2 new file mode 100644 index 000000000..0fd2ee737 Binary files /dev/null and b/connector-ui/src/assets/fonts/Inter-Medium.woff2 differ diff --git a/connector-ui/src/assets/fonts/Inter-Regular.woff2 b/connector-ui/src/assets/fonts/Inter-Regular.woff2 new file mode 100644 index 000000000..b8699af29 Binary files /dev/null and b/connector-ui/src/assets/fonts/Inter-Regular.woff2 differ diff --git a/connector-ui/src/assets/fonts/Inter-SemiBold.woff2 b/connector-ui/src/assets/fonts/Inter-SemiBold.woff2 new file mode 100644 index 000000000..95c48b184 Binary files /dev/null and b/connector-ui/src/assets/fonts/Inter-SemiBold.woff2 differ diff --git a/connector-ui/src/assets/fonts/material-icons-v140-latin-regular.woff2 b/connector-ui/src/assets/fonts/material-icons-v140-latin-regular.woff2 new file mode 100644 index 000000000..5492a6e75 Binary files /dev/null and b/connector-ui/src/assets/fonts/material-icons-v140-latin-regular.woff2 differ diff --git a/connector-ui/src/assets/fonts/open-sans-v40-latin-regular.woff2 b/connector-ui/src/assets/fonts/open-sans-v40-latin-regular.woff2 new file mode 100644 index 000000000..eaae94217 Binary files /dev/null and b/connector-ui/src/assets/fonts/open-sans-v40-latin-regular.woff2 differ diff --git a/connector-ui/src/assets/i18n/de.json b/connector-ui/src/assets/i18n/de.json new file mode 100644 index 000000000..bd16c0bd2 --- /dev/null +++ b/connector-ui/src/assets/i18n/de.json @@ -0,0 +1,295 @@ +{ + "general": { + "access_pol": "Zugriffsrichtlinie", + "ad_info": "Weitere Informationen", + "add_add_header": "Füge zusätzliche Kopfzeile hinzu", + "add_auth": "Authentifizierung hinzufügen", + "add_header": "Zusätzliche Kopfzeilen", + "add": "Hinzufügen", + "additional_properties": "Weitere Eigenschaften", + "asset": "Datenbestand", + "assets": "Datenbestände", + "auth_header": "Authentifizierungskopfzeile Name", + "auth_value": "Authentifizierungskopfzeile Wert", + "auth": "Authentifizierung", + "body": "Körper", + "cancel": "Abbrechen", + "close": "Schließen", + "con_def": "Vertragsdefinition", + "conditions": "Nutzungsbedingungen", + "confirm": "Bestätigen", + "cons": "Einschränkungen", + "consuming": "Eingehend", + "content_type": "Inhaltstyp", + "contract": "Vertragsabschluss", + "coverage": "Zeitlicher Geltungsbereich", + "create": "Erstellen", + "data_category": "Datenkategorie", + "data_model": "Datenmodell", + "data_subcategory": "Datenunterkategorie", + "data": "Datenbeispiel", + "delete": "Löschen", + "description": "Beschreibung", + "detail": "Details", + "details": "Zeige Details", + "direction": "Richtung", + "disable": "Deaktivieren", + "edit": "Bearbeiten", + "enable": "Aktivieren", + "endpoint_doc": "Endpunkt Dokumentation", + "endpoint": "Konnektor Endpunkt", + "error": "Fehler", + "files": "Referenzdateien", + "frequency": "Häufigkeit der Datenaktualisierung", + "geo_location": "Geo-Standort", + "geo_reference_method": "Georeferenz-Methode", + "header_name": "Kopfzeilen-Name", + "header_sec": "Kopfzeile mit Vault Passwort", + "header_val": "Kopfzeile mit Wert", + "header_value": "Kopfzeilen-Wert", + "hide": "Verberge", + "id": "Vertragsangebot ID", + "irr_pol": "Irreguläre Richtlinie", + "language": "Sprache", + "loading": "Lädt...", + "loading1": "Lädt", + "method_para": "Parametrisierung der Methode", + "method": "Methode", + "nuts": "NUTS-Standort", + "offer": "Vertragsangebot", + "oth_connector": "Gegenseite", + "params": "Abfrageparameter", + "path": "Pfad", + "pol": "Richtlinie", + "policies": "Richtlinien", + "policy": "Vertragsrichtlinien", + "providing": "Ausgehend", + "publisher": "Anbieter", + "refresh": "Aktualisieren", + "rem_auth": "Entferne Authentifizierung", + "remove": "Entfernen", + "show_data": "Zeige Datenbeispiel", + "show_files": "Zeige Referenzdateien", + "show": "Zeige", + "signed": "Abgeschlossen", + "sovereign": "Souverän", + "standard_license": "Standardlizenz", + "state": "Status", + "still_loading": "Lädt noch...", + "title": "Titel", + "total": "Total", + "transport_mode": "Transportmodus", + "type": "Typ", + "update": "Aktualisieren", + "updated": "Zuletzt aktualisiert", + "value": "Wert", + "vault_secret": "Vault-Passwort-Name", + "warn": "Warnung" + }, + "tooltip": { + "clipboard": "In die Zwischenablage kopieren", + "details": "Klicken Sie hier für Details", + "failed_details": "Klicken Sie hier für fehlgeschlagene Katalogdetails", + "negotiate": "Sie können keine Verträge mit Ihrem eigenen Konnektor aushandeln." + }, + "notification": { + "asset": "Datei erfolgreich gespeichert.", + "compl_negotiation": "Vertragsverhandlungen abgeschlossen!", + "failed_asset": "Fehlgeschlagene Speichervorgang!", + "failed_create_policy": "Fehlgeschlagene Richtlinie", + "failed_refresh": "Aktualisierung der Datenbestände fehlgeschlagen!", + "failed_transfer_detail_fetch": "Datei-Details konnten nicht abgerufen werden!", + "negotiation": "Gescheiterte Vertragsverhandlungen.", + "starting_neg": "Scheitern bei der Aufnahme von Verhandlungen.", + "succ_pol": "Erfolgreich erstellte Richtlinie." + }, + "component_library": { + "accept_licence": "Hiermit erkläre ich mich damit einverstanden, dass ich durch Drücken der Schaltfläche \"Bestätigen\" die mit dem Angebot des Anbieters verbundenen Lizenzbedingungen, Richtlinien und zusätzlichen Nutzungsbedingungen, einschließlich aller Urheberrechtshinweise, akzeptiere.", + "agree": "Ich stimme den Allgemeinen Geschäftsbedingungen zu.", + "at": "Erstellt am", + "connector_id": "Konnector ID", + "content_type": "Inhaltstyp", + "data_offer": "Allgemeine Geschäftsbedingungen für das Datenangebot", + "delete_one": "Bitte bestätigen Sie, dass Sie löschen möchten", + "delete_title": "Bestätigung der Löschung", + "delete_two": ". Diese Aktion kann nicht rückgängig gemacht werden.", + "http_param": "HTTP-Datenquellen-Parametrisierung", + "json_ld": "Zeige JSON-LD", + "json": "Bereinigtes JSON", + "negotiate": "Verhandeln", + "negotiating": "Verhandeln...", + "no_description": "Keine Beschreibung", + "no_transfer": "Kein Übertragungsprozess bisher gestartet.", + "organization": "Organisation", + "oth_connector": "Gegenpart", + "participant_id": "Teilnehmer ID", + "policy_details": "Zeige Richtlinien Details", + "show_less": "Zeige weniger", + "show_more": "Zeige mehr", + "succ_negotiating": "Erfolgreich verhandelt", + "t_history": "Übertragungshistorie", + "transfer": "Übertragung", + "up_at": "Aktualisiert am" + }, + "services": { + "curator_org": "Name der Kurator Organisation", + "curator_url": "Kurator-URL", + "env_version": "Umgebungs Version", + "failed_loading": "Fehler beim Laden von Verbindungsinformationen", + "main_url": "URL betreuende Organisation", + "maintainer": "Name der betreuenden Organisation" + }, + "asset_page": { + "add_file": "Referenzdatei hinzufügen", + "add_loc": "Füge Ort hinzu", + "add_sample": "Füge Beispieldaten hinzu", + "asset_id": "Datenbestand-ID", + "create_asset": " Erstelle neuen Datenbestand", + "create_assets": "Erstelle Datenbestände", + "datasource_config": "Benutzerdefinierte Datenquellenkonfiguration (JSON)", + "datasource_info": "Datenquellen Information", + "datasource": "Datenquelle", + "default_query": "Wenn die Parametrisierung von Abfrageparametern aktiviert ist, werden die Standardabfrageparameter und die vom Verbraucher bereitgestellten Abfrageparameter zusammengeführt.", + "descrip": "Die Beschreibung nutzt", + "edit_asset": "Bearbeite Datenbestand", + "file_des": "Beschreibung der Referenzdateien", + "http_subpath": "Die konsumierende Seite muss einen benutzerdefinierten HTTP-Unterpfad bereitstellen, bei dem die Methodenparametrisierung aktiviert ist. Der benutzerdefinierte HTTP-Teilpfad wird an den Basispfad angehängt.", + "info_body": "Der Anfragekörper kann nur von der Verbraucherseite aus festgelegt werden, wenn die Parametrisierung aktiviert ist.", + "info_file": "Zusätzliche Informationen zu den Referenzdateien", + "information": "Generelle Informationen", + "instructions": "Zusätzliche, rechtlich nicht relevante Nutzungshinweise (z. B. wie der Datensatz zu zitieren ist)", + "keywords": "Passwort", + "legal_name": "Rechtlicher Name des Dateninhabers", + "no_assets": "Keine Datenbestände mit gegebenem Filter gefunden.", + "path_para": "Pfadparametrisierung", + "provide": "Die konsumierende Seite muss eine benutzerdefinierte HTTP-Methode mit aktivierter Methodenparametrisierung bereitstellen.", + "query_name": "Abfrage Parametername", + "query_para": "Abfrageparameter", + "query_parameter": " Parametrisierung von Abfrageparametern", + "request_body": "Anfragekörper", + "request_para": "Parametrisierung des Anfragekörpers", + "search_assets": "Durchsuche Datenbestände" + }, + "catalog_browser_page": { + "con_endpoints": "Konnektor Endpunkte", + "contract": "Vertragsangebot", + "endpoint_catalogs": "Andere Kataloge für Verbindungs-Endpunkte", + "enter_endpoints": "Bitte geben Sie andere Connector-Endpunkte an, um Kataloge zu holen.", + "fetch": "Status abrufen", + "no_contract_offers": "Keine Vertragsangebote mit diesem Filter gefunden", + "search": "Katalogssuche", + "usage": "Bereits verwendet" + }, + "contract_agreement_page": { + "add_cus_query": "Benutzerdefinierten Abfrage-Parameter hinzufügen", + "con_agree": "Verbrauchen von Vertragsvereinbarungen", + "con_def": "Keine Vertragsdefinition mit gegebenem Filter gefunden.", + "create_def": "Vertragsdefinition erstellen", + "cus_datasink": "Benutzerdefinierte Datasenke-Konfiguration (JSON)", + "cus_meth": "Benutzerdefinierte Methode", + "cus_query": "Benutzerdefinierte Abfrage Parameter Name", + "cus_sub": "Benutzerdefinierter Unterpfad", + "cus_transfer": "Benutzerdefinierte Übertragungsprozess-Anforderung (JSON)", + "datasink": "Datasenke", + "http_fields": "Http-Datenquellenparametrisierung Felder", + "http_message": "Wenn das Datenangebot auf der Anbieterseite vom Typ HttpData ist und bestimmte Datenquellenfelder gesetzt sind, können bestimmte Teile der Anfrage an die Datenquelle von der Verbraucherseite aus angepasst werden und werden bei der Einleitung der Übertragung an den anderen Konnektor übergeben. Dadurch kann ein Asset mehr als nur eine Art von Daten enthalten, was zusätzliche Filterung oder sogar die gemeinsame Nutzung ganzer APIs mit mehreren Datensätzen über ein einziges Asset und einen einzigen Vertrag ermöglicht.", + "http_para": "Http-Datenquellen-Parametrisierung", + "ini_transfer": "Übertragung einleiten", + "json_hint": "JSON-LD Werte für edc:connectorId, edc:contractId, edc:connectorId und edc:connectorAddress werden überschrieben.", + "new_def": "Erstelle neue Vertragsdefinition", + "no_agree_found": "Keine Vertragsvereinbarungen mit gegebenem Filter gefunden.", + "no_agree": "Noch keine vertraglichen Vereinbarungen.", + "prov_agree": "Bereitstellung von Vertragsvereinbarungen", + "proxy_body": "Erfordert, dass Anfragekörper wahr ist.", + "proxy_method": "Erfordert, dass Anfragemethode wahr ist. ", + "proxy_path": "Erfordert, dass Anfragepfad wahr ist.", + "proxy_query": "Erfordert, dass Anfrageparameter wahr sind.", + "req_body": "Anfragekörper", + "req_cont": "Benutzerdefinierter Inhaltstyp für Anfragen", + "res_url": "Die resultierende URL sieht wie folgt aus", + "search_agree": "Suche nach Vertragsvereinbarungen...", + "search_def": "Suche Vertragsdefinition" + }, + "dashboard_page": { + "about_ui": "Über EDC UI", + "about": "Über EDC", + "add_prop": "Zusätzliche Eigenschaften", + "api_url": "Management API URL", + "catalog": "Datenkatalog", + "completed": "Abgeschlossen", + "con_agree": "Vertragliche Vereinbarungen", + "con_def": "Vertragsefinitions", + "conn_end": "Konnektor Endpunkte", + "conn_prop": "Konnektor Eigenschaften", + "conn_service": "Konnektor als Dienstleistung", + "contact": "Kontaktieren Sie uns", + "contracts": "Verträge", + "data_dashboard": "Daten Dashboard", + "descrip": "Geben Sie den folgenden Konnektor-Endpunkt frei, um anderen den Zugriff auf den Katalog Ihres EDC Konnektors zu ermöglichen. Dies ist vor allem bei der Verwendung von Datenangeboten mit eingeschränktem Konnektor nützlich, die in Brokern nicht angezeigt werden.", + "eclipse": "Eclipse-Datenraum-Komponenten", + "edc_conn": "EDC Konnektor", + "edition_edc": "Basisausgabe EDC", + "error": "Fehler", + "failed_dashboard": "Abruf von Dashboard-Seitendaten fehlgeschlagen", + "failed_env": "Abruf von Env und Jar letzten Commit Daten fehlgeschlagen", + "failed_offers": "DATENÜBERTRAGUNG FEHLGESCHLAGEN", + "failed_ui_build": "Abruf der Daten des letzten Baudatums der Benutzeroberfläche fehlgeschlagen", + "failed_ui": "Abruf der letzten UI Commit Daten fehlgeschlagen", + "inc_data": "Eingehende Daten", + "managed_edc": "Managed EDC", + "marketing_about_ui_assets_view_and_create": "Betrachten und erstellen Sie Datenbstände indem Sie auf", + "marketing_about_ui_catalog_negotiate": "Verhandeln Sie einen Vertrag für die gemeinsame Nutzung von Daten in Ihrem Datenraum unter", + "marketing_about_ui_catalog_view_offers": "Zeigen Sie den Datenbestands-Katalog an, der Ihnen in Ihrem Datenraum zur Verfügung steht, unter", + "marketing_about_ui_contract_definitions_view_and_create": "Veröffentlichen Sie ein neues Element in Ihrem Datenraum unter", + "marketing_about_ui_contracts_transfer": "Übertragen Sie ein Element in Ihren Datenraum indem Sie auf ", + "marketing_about_ui_contracts_view_existing": "Betrachten Sie ihre existierenden Verträge, indem Sie auf", + "marketing_about_ui_policies_view_and_create": "Betrachten und erstellen Sie Richtlinien und wenden Sie diese auf Datenbestände in Ihrem Datenraum an, indem Sie auf", + "marketing_about_ui_transfer_history_view": "Sehen Sie sich an, welche Datenbestände in Ihrem Datenraum übertragen wurden, indem Sie auf", + "marketing_about_ui": "Beispielhafte Anwendungsfälle, die Sie mit dieser Anwendung ausprobieren können, sind:", + "marketing_about": "Das Eclipse Dataspace Components Framework ermöglicht einen souveränen, organisationsübergreifenden Datenaustausch.", + "marketing_about2": "Es implementiert den internationalen Datenraumstandard (IDS) sowie die mit GAIA-X verbundenen relevanten Protokolle.", + "marketing_about3": "Der Aufbau ist so erweiterbar wie möglich gestaltet, um die Integration in verschiedene Datenökosysteme zu fördern.", + "marketing_ce_intro": "Um Datenräume wie Mobility Data Space oder Catena-X innerhalb weniger Minuten zu verbinden, betrachten Sie die verwaltete Lösung von", + "marketing_ce_intro2": "- den Connector-as-a-Service (CaaS), der auf Open-Source-Software basiert und mit wichtigen Unternehmensfunktionen angereichert ist.", + "marketing_mds_basic_intro": "Dieser EDC Konnektor auf der Abo-Ebene Basic wird von sovity bereitgestellt, um Ihre ersten Schritte im Mobility Data Space (MDS) zu ermöglichen.", + "marketing_mds_basic_intro2": "Für zusätzliche Funktionen und erweiterte Kapazitäten können Sie uns gerne kontaktieren.", + "no_transfer": "Keine ausgehenden Übertragungen", + "no_transfer2": "Keine eingehenden Übertragungen", + "num_transfer": "Anzahl der Übertragungsvorgänge", + "out_data": "Ausgehende Daten", + "page": "klicken.", + "pre_cat": "Vorkonfigurierte Kataloge", + "progress": "In Arbeit", + "provided": "Provided by", + "trans_pro": "Übertragungsvorgänge", + "transfer": "Übertragungshistorie", + "your_assets": "Deine Datenbestände", + "your_def": "Deine Vertragsdefinitionen", + "your_pol": "Deine Richtlinien" + }, + "policy_definition_page": { + "conn_res": "Anschluss mit vom Konnektor eingeschränkter Nutzung", + "create_pol": "Erstelle eine neue Richtlinie", + "create_policy": "Erstelle eine Richtlinie", + "date_range": "Datumsbereich", + "no_pol": "Keine Richtlinie mit gegebenem Filter gefunden.", + "search_pol": "Durchsuche Richtlinien", + "time_res": "Zeitlich begrenzter Zeitraum" + }, + "transfer_history_page": { + "counter_endpoint": "Gegenpart Konnektorendpunkt", + "counter_id": "Gegenpart Teilnehmer ID", + "no_trans": "Keine Übertragungshistorie gefunden.", + "subtitle": "Übertragungshistorie Details" + }, + "connector_ui": { + "assets": "Datenbestände", + "catalog": "Katalogssuche", + "contract": "Vertragsdefinition", + "contracts": "Verträge", + "dashboard": "Übersicht", + "logout": "Ausloggen", + "policies": "Richtlinien", + "transfer": "Übertragungshistorie" + } +} diff --git a/connector-ui/src/assets/i18n/en.json b/connector-ui/src/assets/i18n/en.json new file mode 100644 index 000000000..c699fbb19 --- /dev/null +++ b/connector-ui/src/assets/i18n/en.json @@ -0,0 +1,480 @@ +{ + "asset_detail_dialog.limit_exceeded_tooltip": "You have reached the maximum number of consuming contracts. Please terminate one of the existing contracts to negotiate a new one.", + "asset_detail_dialog.on_request_data_offer_description_html": "This data offer is not available for immediate consumption. However, the creator left an email address to contact them if interested.
Click Contact to email the data offer provider. Alternatively, you can also copy the contact email address from the Contact Information section above.", + "asset_detail_dialog.on_request_data_offer_title": "On Request Data Offer", + "asset_list_page.add_file": "Add reference file", + "asset_list_page.add_keyword": "Add keyword...", + "asset_list_page.add_loc": "Add location", + "asset_list_page.add_sample": "Add data sample", + "asset_list_page.asset_id": "Asset ID", + "asset_list_page.create_asset": " Create New Asset", + "asset_list_page.datasource_config": "Custom Datasource Config (JSON)", + "asset_list_page.datasource_info": "Datasource Information", + "asset_list_page.datasource": "Datasource", + "asset_list_page.default_query": "With query param parameterization enabled, the default query params and the query params provided by the consumer will be merged.", + "asset_list_page.descrip": "The description uses", + "asset_list_page.edit_asset": "Edit Asset", + "asset_list_page.file_des_hint1": "Additional information regarding the reference files.", + "asset_list_page.file_des_hint2": "Supports", + "asset_list_page.file_des": "Reference files description", + "asset_list_page.general_information_description": "Fill out general information about the asset.", + "asset_list_page.general_information": "General Information", + "asset_list_page.http_subpath": "The consuming side must provide a Custom HTTP Subpath with method parameterization is enabled. The Custom HTTP Subpath will be appended to the base path.", + "asset_list_page.info_body": "The request body can only be set from the consumer side, if parameterization is enabled.", + "asset_list_page.info_file": "Additional information regarding the reference files", + "asset_list_page.instructions": "Additional not legally relevant usage instructions (e.g. how to cite the dataset)", + "asset_list_page.keywords": "Keywords", + "asset_list_page.legal_name": "Legal name of the data owner", + "asset_list_page.method_para": "Method Parameterization", + "asset_list_page.my_asset": "My Asset", + "asset_list_page.name": "Title", + "asset_list_page.no_assets": "No assets found with given filter.", + "asset_list_page.page": "Asset Page", + "asset_list_page.path_para": "Path Parameterization", + "asset_list_page.provide": "The consuming side must provide a Custom HTTP Method with method parameterization enabled.", + "asset_list_page.query_name": "Query Param Name", + "asset_list_page.query_para_single": "Query Param", + "asset_list_page.query_para": "Query Params", + "asset_list_page.query_parameter": " Query Param Parameterization", + "asset_list_page.request_body": "Request Body", + "asset_list_page.request_para": "Request Body Parameterization", + "asset_list_page.search_assets": "Search assets", + "asset_list_page.title": "Assets", + "asset_list_page.version": "Version", + "catalog_browser_page.con_endpoints": "Connector Endpoints", + "catalog_browser_page.contract": "Contract Offer", + "catalog_browser_page.endpoint_catalogs": "Other Connector Endpoint Catalogs", + "catalog_browser_page.enter_endpoints": "Please enter other connector endpoints to fetch catalogs.", + "catalog_browser_page.fetch": "Fetch Status", + "catalog_browser_page.no_contract_offers": "No contract offers found with this filter", + "catalog_browser_page.page": "Catalog Browser", + "catalog_browser_page.search": "Search catalog", + "catalog_browser_page.title": "Catalog Browser", + "catalog_browser_page.usage": "Already using", + "component_library.accept_licence": "Hereby I agree that by pressing the 'Confirm' button, I accept the license\n terms, policies, and additional conditions for use, including any copyright\n notices, associated with the provider's offer.", + "component_library.agree": "I agree to the Data Offer Terms & Conditions", + "component_library.at": "Created At", + "component_library.connector_id_plural": "Connector IDs", + "component_library.connector_id": "Connector ID", + "component_library.content_type": "Content Type", + "component_library.data_offer": "Data Offer Terms & Conditions", + "component_library.delete_one": "Please confirm you want to delete", + "component_library.delete_title": "Deletion confirmation", + "component_library.delete_two": "This action cannot be undone.", + "component_library.http_param": "HTTP Data Source Parameterization", + "component_library.json_ld": "Show JSON-LD", + "component_library.json": "Cleaned JSON", + "component_library.negotiate": "Negotiate", + "component_library.negotiating": "Negotiating...", + "component_library.no_description": "No Description", + "component_library.no_transfer": "No transfer processes started yet.", + "component_library.organization": "Organization", + "component_library.oth_connector": "Other Connector", + "component_library.participant_id_plural": "Participant IDs", + "component_library.participant_id": "Participant ID", + "component_library.policy_details": "Show Policy Details", + "component_library.show_less": "Show less", + "component_library.show_more": "Show more", + "component_library.succ_negotiating": "Successfully Negotiated", + "component_library.t_history": "Transfer History", + "component_library.transfer": "Transfer", + "component_library.up_at": "Updated At", + "connector_ui.assets": "Assets", + "connector_ui.catalog": "Catalog Browser", + "connector_ui.contract": "Contract Definitions", + "connector_ui.contracts": "Contracts", + "connector_ui.dashboard": "Dashboard", + "connector_ui.logout": "Logout", + "connector_ui.policies": "Policies", + "connector_ui.transfer": "Transfer History", + "contract_agreement_page.active_contracts": "Active Contracts", + "contract_agreement_page.add_cus_query": "Add Custom Query Param", + "contract_agreement_page.all_contracts": "All Contracts", + "contract_agreement_page.con_agree": "Consuming Contract Agreements", + "contract_agreement_page.con_def": "No data offer found with given filter.", + "contract_agreement_page.create_def": "Publish Data Offer", + "contract_agreement_page.cus_datasink": "Custom Datasink Config (JSON)", + "contract_agreement_page.cus_meth": "Custom Method", + "contract_agreement_page.cus_query": "Custom Query Param Name", + "contract_agreement_page.cus_sub": "Custom Subpath", + "contract_agreement_page.cus_transfer": "Custom Transfer Process Request (JSON)", + "contract_agreement_page.datasink": "Datasink", + "contract_agreement_page.http_fields": "Http Datasource Parameterization Fields", + "contract_agreement_page.http_message": "When the data offer on the provider side is of the type HttpData and certain data source fields are set, certain parts of the request to the data source can be customized from the consumer side and will be passed to the other connector when initiating the transfer. This allows an asset to contain more than just one kind of data, allowing additional filtering or even sharing of entire APIs with multiple data sets via a single asset and a single contract.", + "contract_agreement_page.http_para": "Http Datasource Parameterization", + "contract_agreement_page.ini_transfer": "Initiate Transfer", + "contract_agreement_page.json_hint": "JSON-LD values for edc:connectorId, edc:contractId, edc:connectorId and edc:connectorAddress will be overridden.", + "contract_agreement_page.new_def": "Publish New Data Offer", + "contract_agreement_page.no_agree_found": "No contract agreements found with given filter.", + "contract_agreement_page.no_agree": "No contract agreements yet.", + "contract_agreement_page.page": "Contract Page", + "contract_agreement_page.prov_agree": "Providing Contract Agreements", + "contract_agreement_page.proxy_body": "Requires proxyBody to be true.", + "contract_agreement_page.proxy_method": "Requires proxyMethod to be true. ", + "contract_agreement_page.proxy_path": "Requires proxyPath to be true. ", + "contract_agreement_page.proxy_query": "Requires proxyQueryParams to be true", + "contract_agreement_page.req_body": "Custom Request Body", + "contract_agreement_page.req_cont": "Custom Request Body Content Type", + "contract_agreement_page.res_url": "The resulting URL will look like", + "contract_agreement_page.search_agree": "Search contract agreements...", + "contract_agreement_page.search_def": "Search data offer", + "contract_agreement_page.terminated_contracts": "Terminated Contracts", + "contract_agreement_page.title": "Contracts", + "contract_definition_page.page": "Data Offer Page", + "contract_definition_page.title": "Data Offers", + "create_asset.asset_id_tooltip": "Asset ID, used internally, is an auto-generated string in a URL-compatible format, combining the asset name and version with a urn:artifact: prefix. You can customize it if needed.", + "create_asset.content_type_hint": "Describes the content type of the data as a MIME type, see", + "create_asset.data_model_tooltip": "Model for data exchange, e.g. DATEX II, TPEG for traffic and travel information, etc.", + "create_asset.email_tooltip": "This email address will be offered to potential consumers for contacting you. This is done in place of having an actual data source connected.", + "create_asset.endpoint_documentation_tooltip": "URL to the technical documentation about the data to be received.", + "create_asset.prefered_email_subject_tooltip": "When potential customers reach out to you via email, you’ll receive messages with this subject line.", + "create_asset.prefered_email_subject": "Preferred E-Mail Subject", + "create_asset.standard_license_tooltip": "URL of the license under which the data is offered", + "create_asset.title_tooltip": "The main title of your asset. It will also be the title of the data offering displayed in the catalog.", + "create_asset.version_tooltip": "The version of your asset. Especially useful if you have iterations of the same asset.", + "create_data_offer_page.accept_placeholder": "Accept", + "create_data_offer_page.add_data_sample": "Add data sample", + "create_data_offer_page.add_location": "Add location", + "create_data_offer_page.add_reference_file": "Add reference file", + "create_data_offer_page.additional_headers": "Additional Headers", + "create_data_offer_page.asset_id_label": "Asset ID", + "create_data_offer_page.asset_id_tooltip": "Asset ID, used internally, is an auto-generated string in a URL-compatible format, combining the asset name and version with a urn:artifact: prefix. You can customize it if needed.", + "create_data_offer_page.asset_id": "Asset ID", + "create_data_offer_page.auth_header_name": "Auth Header Name", + "create_data_offer_page.auth_header_value": "Auth Header Value", + "create_data_offer_page.authorization_header_placeholder": "Authorization", + "create_data_offer_page.bearer_placeholder": "Bearer ...", + "create_data_offer_page.conditions_for_use_description_hint": "Additional not legally relevant usage instructions (e.g. how to cite the dataset). The field supports", + "create_data_offer_page.conditions_for_use_label": "Conditions for use", + "create_data_offer_page.conditions_for_use_placeholder": "Please cite the dataset as...", + "create_data_offer_page.contact_email_placeholder": "Contact E-Mail", + "create_data_offer_page.contact_email_tooltip": "This email address will be offered to potential consumers for contacting you. This is done in place of having an actual data source connected.", + "create_data_offer_page.contact_email": "Contact E-Mail", + "create_data_offer_page.content_type": "Content Type", + "create_data_offer_page.context_information_description": "Provide context information about the datasource", + "create_data_offer_page.custom_datasource_config_json_label": "Custom Datasource Config (JSON)", + "create_data_offer_page.custom_http_method_hint": "The consuming side must provide a Custom HTTP Method with method parameterization enabled.", + "create_data_offer_page.custom_http_subpath_hint": "The consuming side must provide a Custom HTTP Subpath with method parameterization is enabled. The Custom HTTP Subpath will be appended to the base path.", + "create_data_offer_page.data_category": "Data Category", + "create_data_offer_page.data_model_placeholder": "proprietary", + "create_data_offer_page.data_model_tooltip": "Model for data exchange, e.g. DATEX II, TPEG for traffic and travel information, etc.", + "create_data_offer_page.data_model": "Data Model", + "create_data_offer_page.data_offer_type": "Data offer type", + "create_data_offer_page.data_samples_tooltip": "URLs of Dataset samples if available", + "create_data_offer_page.data_samples": "Data samples", + "create_data_offer_page.data_subcategory": "Data Subcategory", + "create_data_offer_page.data_update_frequency_label": "Data update frequency", + "create_data_offer_page.data_update_frequency_placeholder": "every month", + "create_data_offer_page.data_update_frequency_tooltip": "How often is the dataset updated, e.g\t'Every 5 min.", + "create_data_offer_page.datasource_information": "Datasource Information", + "create_data_offer_page.datasource": "Datasource", + "create_data_offer_page.define_data_offer": "Define the type of your offer", + "create_data_offer_page.description_supports": "The description supports", + "create_data_offer_page.description_uses": "The description uses", + "create_data_offer_page.description": "Description", + "create_data_offer_page.documentation": "Documentation", + "create_data_offer_page.email_subject_placeholder": "Preferred E-Mail Subject", + "create_data_offer_page.email_subject_tooltip": "When potential customers reach out to you via email, you’ll receive messages with this subject line.", + "create_data_offer_page.email_subject": "Preferred E-Mail Subject", + "create_data_offer_page.end_date_inclusive": "End date (inclusive)", + "create_data_offer_page.endpoint_documentation_tooltip": "URL to the technical documentation about the data to be received.", + "create_data_offer_page.endpoint_documentation": "Endpoint Documentation", + "create_data_offer_page.general_information_description": "Fill out general information about the asset.", + "create_data_offer_page.general_information": "General Information", + "create_data_offer_page.geo_location_description": "Simple description of the relevant geolocation, e.g. Hamburg and vicinity.", + "create_data_offer_page.geo_location_label": "Geo location", + "create_data_offer_page.geo_location_tooltip": "Simple description of the relevant geolocation, e.g. Hamburg and vicinity.", + "create_data_offer_page.geo_reference_method": "Geo reference method", + "create_data_offer_page.geo_reference_placeholder": "Lat/Lon", + "create_data_offer_page.geo_reference_tooltip": "The method used for representing of geographical data, e.g GeoJSON, OpenLR, etc.", + "create_data_offer_page.header_name": "Header Name", + "create_data_offer_page.header_value": "Header Value", + "create_data_offer_page.header_with_value": "Header with Value", + "create_data_offer_page.header_with_vault_secret": "Header with Vault Secret", + "create_data_offer_page.header": "Header", + "create_data_offer_page.keywords": "Keywords", + "create_data_offer_page.language": "Language", + "create_data_offer_page.legal_information_description": "Provide legal information and define the conditions for use", + "create_data_offer_page.legal_information_title": "Legal Information", + "create_data_offer_page.location_time_description": "Fill out location and time information about the asset", + "create_data_offer_page.location_time_title": "Location / Time", + "create_data_offer_page.method_parameterization": "Method Parameterization", + "create_data_offer_page.method": "Method", + "create_data_offer_page.mobility_information_description": "Fill out the asset's mobility information", + "create_data_offer_page.mobility_information_my_title": "Mobility Information", + "create_data_offer_page.nuts_location": "NUTS location", + "create_data_offer_page.nuts_locations_label": "NUTS locations", + "create_data_offer_page.nuts_locations_tooltip": "NUTS codes are regional identifiers in Germany used for statistical and administrative purposes, covering states, districts, and municipalities. (e.g DE60)", + "create_data_offer_page.offer_type": "Offer Type", + "create_data_offer_page.on_request": "On Request (without data source)", + "create_data_offer_page.parameterization": "Parameterization", + "create_data_offer_page.path_parameterization": "Path Parameterization", + "create_data_offer_page.publish_asset_only_tooltip": "Create the asset but do not publish your data offer. You can do it later.", + "create_data_offer_page.publish_asset_only": "Create asset only (without data offer)", + "create_data_offer_page.publish_restricted_tooltip": "Your data offer is published with restrictions of your choice.", + "create_data_offer_page.publish_restricted": "Publish restricted", + "create_data_offer_page.publish_unrestricted_tooltip": "Your data offer is published and can be accessed by everyone.", + "create_data_offer_page.publish_unrestricted": "Publish unrestricted", + "create_data_offer_page.publisher_label": "Publisher", + "create_data_offer_page.publisher_tooltip": "URL of the original publisher of the data", + "create_data_offer_page.publisher": "Publisher", + "create_data_offer_page.publishing_description": "Publish data offer to other data space participants", + "create_data_offer_page.publishing_mode_label": "Publishing Mode", + "create_data_offer_page.publishing": "Publishing", + "create_data_offer_page.query_param_enabled_hint": "With query param parameterization enabled, the default query params and the query params provided by the consumer will be merged.", + "create_data_offer_page.query_param_name": "Query Param Name", + "create_data_offer_page.query_param_parametrization": "Query Param Parameterization", + "create_data_offer_page.query_param": "Query Param", + "create_data_offer_page.query_params": "Query Params", + "create_data_offer_page.readily_available": "Available (with data source)", + "create_data_offer_page.ref_files_description_placeholder": "# My Asset\\n\\nAt vero eos et accusam et justo duo dolores et ea rebum.\\n\\n## Details\\n\\nAt vero eos et accusam et justo duo dolores et ea **rebum**.", + "create_data_offer_page.reference_files_description_label": "Reference files description", + "create_data_offer_page.reference_files_description_title": "Reference files description", + "create_data_offer_page.reference_files_description": "Additional information regarding the reference files. Supports", + "create_data_offer_page.reference_files_tooltip": "URLs of Dataset schemas or other references", + "create_data_offer_page.reference_files": "Reference files", + "create_data_offer_page.request_body_hint": "The request body can only be set from the consumer side, if parameterization is enabled.", + "create_data_offer_page.request_body_parameterization": "Request Body Parameterization", + "create_data_offer_page.request_body": "Request Body", + "create_data_offer_page.show_advanced_fields": "Show Advanced Fields", + "create_data_offer_page.sovereign_label": "Sovereign", + "create_data_offer_page.sovereign_placeholder": "Data Owning Company GMBH", + "create_data_offer_page.sovereign_tooltip": "Legal name of the data owner", + "create_data_offer_page.standard_license_label": "Standard License", + "create_data_offer_page.standard_license_tooltip": "URL of the license under which the data is offered.", + "create_data_offer_page.start_date": "Start date", + "create_data_offer_page.temporal_coverage_hint": "Start and/or end date when the dataset is available for consumption. DD/MM/YYYY (optional) – DD/MM/YYYY (optional)", + "create_data_offer_page.temporal_coverage": "Temporal coverage", + "create_data_offer_page.title_label": "Title", + "create_data_offer_page.title_placeholder": "My Asset", + "create_data_offer_page.title_tooltip": "The main title of your asset. It will also be the title of the data offering displayed in the catalog.", + "create_data_offer_page.title": "Create Data Offer", + "create_data_offer_page.transport_mode": "Transport Mode", + "create_data_offer_page.type": "Type", + "create_data_offer_page.unchanged": "Keep the datasource unchanged.", + "create_data_offer_page.vault_secret_name": "Vault Secret Name", + "create_data_offer_page.version_tooltip": "The version of your asset. Especially useful if you have iterations of the same asset.", + "create_data_offer_page.version": "Version", + "create_policy_page.title": "Create Policy", + "dashboard_page.about_ui": "About EDC UI", + "dashboard_page.about": "About EDC", + "dashboard_page.add_prop": "Additional Properties", + "dashboard_page.api_url": "Management API URL", + "dashboard_page.catalog_browser": "Catalog Browser", + "dashboard_page.catalog": "Catalog Browser", + "dashboard_page.completed": "Completed", + "dashboard_page.con_agree": "Contract Agreements", + "dashboard_page.con_def": "Contract Definitions", + "dashboard_page.conn_end": "Connector Endpoint", + "dashboard_page.conn_prop": "Connector Properties", + "dashboard_page.conn_service": "Connector-as-a-Service", + "dashboard_page.contact_us": "Contact Us", + "dashboard_page.contact": "Contact", + "dashboard_page.contracts": "Contracts", + "dashboard_page.data_dashboard": "Data Dashboard", + "dashboard_page.descrip": "Share the following Connector Endpoint to let others access your EDC Connector's catalog. This is especially useful when using connector-restricted data offers which won't show up in brokers.", + "dashboard_page.eclipse": "Eclipse Dataspace Components", + "dashboard_page.edc_conn": "EDC Connector", + "dashboard_page.edition_edc": "Basic Edition EDC", + "dashboard_page.error": "Error", + "dashboard_page.failed_dashboard": "Failed fetching Dashboard Page Data", + "dashboard_page.failed_env": "Failed fetching Env and Jar Last Commit Data", + "dashboard_page.failed_offers": "Failed fetching data offers.", + "dashboard_page.failed_ui_build": "Failed fetching UI Last Build Date Data", + "dashboard_page.failed_ui": "Failed fetching UI Last Commit Data", + "dashboard_page.inc_data": "Incoming Data", + "dashboard_page.managed_edc": "Get Managed EDC", + "dashboard_page.marketing_about_ui_assets_view_and_create": "View and create assets using the {}", + "dashboard_page.marketing_about_ui_catalog_negotiate": "Negotiate a contract for data sharing in your Dataspace using the {}", + "dashboard_page.marketing_about_ui_catalog_view_offers": "View the asset catalog available to you in your Dataspace using the {}", + "dashboard_page.marketing_about_ui_contract_definitions_view_and_create": "Publish an asset into your Dataspace using the {}", + "dashboard_page.marketing_about_ui_contracts_transfer": "Transfer an asset in your Dataspace using the {}", + "dashboard_page.marketing_about_ui_contracts_view_existing": "View your existing contracts in the {}", + "dashboard_page.marketing_about_ui_policies_view_and_create": "View and create policies and apply these to assets in your Dataspace using the {}", + "dashboard_page.marketing_about_ui_transfer_history_view": "View which assets have been transferred in your Dataspace in the {}", + "dashboard_page.marketing_about_ui": "Example use cases, that you can try out with this application, are:", + "dashboard_page.marketing_about": "The Eclipse Dataspace Components framework facilitates sovereign, inter-organizational data exchange.", + "dashboard_page.marketing_about2": "It implements the International Data Spaces standard (IDS) as well as relevant protocols associated with GAIA-X.", + "dashboard_page.marketing_about3": "The framework is designed as extensible as possible to encourage integrations into various data ecosystems.", + "dashboard_page.marketing_ce_intro": "To join data spaces like Mobility Data Space or Catena-X within minutes, consider the managed solution by {}.", + "dashboard_page.marketing_ce_intro2": "- the Connector-as-a-Service (CaaS) based on open-source software enriched with key enterprise features.", + "dashboard_page.marketing_mds_basic_intro": "This EDC Connector on the subscription level Basic is provided by sovity to enable your first steps in the Mobility Data Space (MDS).", + "dashboard_page.marketing_mds_basic_intro2": "For additional features and advanced capacities, please feel free to contact us.", + "dashboard_page.no_transfer": "NO PROVIDING TRANSFER PROCESSES", + "dashboard_page.no_transfer2": "NO CONSUMING TRANSFER PROCESSES", + "dashboard_page.num_transfer": "Number of Transfer Processes", + "dashboard_page.out_data": "Outgoing Data", + "dashboard_page.page": "Dashboard Page", + "dashboard_page.pre_cat": "Preconfigured Catalogs", + "dashboard_page.progress": "In Progress", + "dashboard_page.provided": "Provided by", + "dashboard_page.title": "Dashboard", + "dashboard_page.trans_pro": "Transfer Processes", + "dashboard_page.transfer": "Transfer History", + "dashboard_page.your_assets": "Your Assets", + "dashboard_page.your_data_offers": "Your Data Offers", + "dashboard_page.your_def": "Your Contract Definitions", + "dashboard_page.your_pol": "Your Policies", + "edit_asset_page.title": "Edit Asset", + "general.accept": "Accept", + "general.access_pol": "Access Policy", + "general.ad_inf": "Advanced Information", + "general.ad_info": "Advanced Information", + "general.add_add_header": "Add Additional Header", + "general.add_auth": "Add Authentication", + "general.add_header": "Additional Headers", + "general.add": "Add", + "general.additional_properties": "Additional Properties", + "general.asset": "Asset", + "general.assets": "Assets", + "general.auth_header": "Auth Header Name", + "general.auth_value": "Auth Header Value", + "general.auth": "Authentication", + "general.body": "Body", + "general.cancel": "Cancel", + "general.close": "Close", + "general.combine": "Combine", + "general.con_def": "Data Offer", + "general.conditions": "Conditions For Use", + "general.confirm": "Confirm", + "general.cons": "Constraints", + "general.consuming": "Consuming", + "general.contact": "Contact", + "general.content_type": "Content Type", + "general.contract_offer": "Contract Offer", + "general.contract_policy": "Contract Policy", + "general.contract": "Contract Agreement", + "general.copy_to_clipboard_tooltip": "Copy to clipboard", + "general.coverage": "Temporal Coverage", + "general.create": "Create", + "general.data_category": "Data Category", + "general.data_model": "Data Model", + "general.data_subcategory": "Data Subcategory", + "general.data": "Data Samples", + "general.date": "Date", + "general.delete": "Delete", + "general.description": "Description", + "general.details": "Show Details", + "general.direction": "Direction", + "general.disable": "Disable", + "general.doc": "Documentation", + "general.edit": "Edit", + "general.enable": "Enable", + "general.end_date": "End date", + "general.endpoint_doc": "Endpoint Documentation", + "general.endpoint": "Connector Endpoint", + "general.error": "Error", + "general.files": "Reference Files", + "general.frequency": "Data Update Frequency", + "general.geo_location": "Geo Location", + "general.geo_reference_method_tooltip": "The method used for representing of geographical data, e.g GeoJSON, OpenLR, etc.", + "general.geo_reference_method": "Geo reference method", + "general.header_name": "Header Name", + "general.header_sec": "Header with Vault Secret", + "general.header_val": "Header with Value", + "general.header_value": "Header Value", + "general.hide": "Hide", + "general.id": "Contract Offer ID", + "general.irr_pol": "Irregular Policy", + "general.language": "Language", + "general.limit_reached": "You have reached the maximum number of consuming contracts. Please terminate one of the existing contracts to negotiate a new one.", + "general.loading": "Loading...", + "general.loading1": "Loading", + "general.method_para": "Method Parameterization", + "general.method": "Method", + "general.name": "Name", + "general.nuts": "NUTS Locations", + "general.operator": "Operator", + "general.oth_connector": "Other Connector", + "general.page": "Page", + "general.parametrization": "Parameterization", + "general.params": "Query Params", + "general.path": "Path", + "general.policies": "Policies", + "general.policy": "Policy", + "general.providing": "Providing", + "general.pub_asset_only_tooltip": "Create the asset but do not publish your data offer. You can do it later.", + "general.pub_asset_only": "Create asset only (without data offer)", + "general.pub_desc": "Publish data offer to other data space participants", + "general.pub_mode": "Publishing Mode", + "general.pub_restr_tooltip": "Your data offer is published with restrictions of your choice.", + "general.pub_restr": "Publish restricted", + "general.pub_unrestr_tooltip": "Your data offer is published and can be accessed by everyone.", + "general.pub_unrestr": "Publish unrestricted", + "general.publish": "Publish", + "general.publisher": "Publisher", + "general.publishing": "Publishing", + "general.refresh": "Refresh", + "general.rem_auth": "Remove Authentication", + "general.remove": "Remove", + "general.select_an_option": "Select an option", + "general.show_adv_fields": "Show Advanced Fields", + "general.show_data": "Show Data Samples", + "general.show_files": "Show Reference Files", + "general.show": "Show", + "general.signed": "Signed", + "general.sovereign": "Sovereign", + "general.standard_license": "Standard License", + "general.start_date": "Start date", + "general.state": "State", + "general.still_loading": "Still Loading...", + "general.tags": "Tags", + "general.terminate": "Terminate", + "general.terminated_by": "terminated by", + "general.title": "Title", + "general.total": "Total", + "general.transport_mode": "Transport Mode", + "general.type": "Type", + "general.update": "Update", + "general.updated": "Last updated", + "general.value": "Value", + "general.vault_secret": "Vault Secret Name", + "general.warn": "Warn", + "logout_page.title": "Logout", + "notification.asset": "Successfully saved asset.", + "notification.compl_negotiation": "Contract Negotiation complete!", + "notification.failed_asset": "Failed saving asset!", + "notification.failed_create_policy": "Failed creating Policy!", + "notification.failed_refresh": "Failed refreshing asset list!", + "notification.failed_transfer_detail_fetch": "Failed to fetch asset details!", + "notification.negotiation": "Failed negotiating contract.", + "notification.starting_neg": "Failure starting negotiation.", + "notification.succ_pol": "Successfully created policy.", + "policy_definition_page.conn_res": "Connector-Restricted-Usage", + "policy_definition_page.create_pol": "Create New Policy", + "policy_definition_page.create_policy": "Create Policy", + "policy_definition_page.date_range": "Date Range", + "policy_definition_page.expression": "Policy Expression", + "policy_definition_page.metadata": "Policy Metadata", + "policy_definition_page.no_pol": "No policy found with given filter.", + "policy_definition_page.page": "Policy Page", + "policy_definition_page.search_pol": "Search policies", + "policy_definition_page.time_res": "Time-Period-Restricted", + "policy_definition_page.title": "Policies", + "policy_expression.add_tooltip": "Conjunction of several expressions. Evaluates to true if and only if all child expressions are true", + "policy_expression.or_tooltip": "Disjunction of several expressions. Evaluates to true if and only if at least one child expression is true", + "policy_expression.participant_id_description": "Consumer's Participant ID, also called Connector ID, of the counter-party connector.", + "policy_expression.participant_id_hint": "Multiple values can be joined by comma", + "policy_expression.participant_id_title": "Consumer's Participant ID", + "policy_expression.time_restriction_description": "Time at which the policy is evaluated. This can be used to restrict the data offer to certain time periods", + "policy_expression.time_restriction_title": "Time Restriction", + "policy_expression.timespan_restriction_title": "Timespan Restriction", + "policy_expression.timespan_restriction_tooltip": "Timespan at which the policy is evaluated. This can be used to restrict the data offer to certain time periods", + "policy_expression.xone_tooltip": "XONE operation. Evaluates to true if and only if exactly one child expression is true", + "services.curator_org": "Curator Organization Name", + "services.curator_url": "Curator URL", + "services.env_version": "Environment Version", + "services.failed_loading": "Failed loading connector information", + "services.main_url": "Maintainer URL", + "services.maintainer": "Maintainer Organization Name", + "tooltip.clipboard": "Copy to clipboard", + "tooltip.details": "Click for details", + "tooltip.failed_details": "Click for failed catalog details", + "tooltip.negotiate": "Cannot negotiate contracts with your own connector.", + "transfer_history_page.counter_endpoint": "Counterparty Connector Endpoint", + "transfer_history_page.counter_id": "Counterparty Participant ID", + "transfer_history_page.no_trans": "No transfer history found.", + "transfer_history_page.page": "Transfer History Page", + "transfer_history_page.subtitle": "Transfer History Details", + "transfer_history_page.title": "Transfer History" +} diff --git a/connector-ui/src/assets/images/flags/de.svg b/connector-ui/src/assets/images/flags/de.svg new file mode 100644 index 000000000..442047028 --- /dev/null +++ b/connector-ui/src/assets/images/flags/de.svg @@ -0,0 +1,9 @@ + + + + Flag of Germany + + + + diff --git a/connector-ui/src/assets/images/flags/en.svg b/connector-ui/src/assets/images/flags/en.svg new file mode 100644 index 000000000..c52d43125 --- /dev/null +++ b/connector-ui/src/assets/images/flags/en.svg @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/connector-ui/src/assets/images/mds_favicon.ico b/connector-ui/src/assets/images/mds_favicon.ico new file mode 100644 index 000000000..4642ce589 Binary files /dev/null and b/connector-ui/src/assets/images/mds_favicon.ico differ diff --git a/connector-ui/src/assets/images/mds_logo.svg b/connector-ui/src/assets/images/mds_logo.svg new file mode 100644 index 000000000..79ea4ad7f --- /dev/null +++ b/connector-ui/src/assets/images/mds_logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/connector-ui/src/assets/images/sovity_favicon-192x192.png b/connector-ui/src/assets/images/sovity_favicon-192x192.png new file mode 100644 index 000000000..923895c94 Binary files /dev/null and b/connector-ui/src/assets/images/sovity_favicon-192x192.png differ diff --git a/connector-ui/src/assets/images/sovity_logo.svg b/connector-ui/src/assets/images/sovity_logo.svg new file mode 100644 index 000000000..0141eddf5 --- /dev/null +++ b/connector-ui/src/assets/images/sovity_logo.svg @@ -0,0 +1,140 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/connector-ui/src/environments/edc-ui-environment.ts b/connector-ui/src/environments/edc-ui-environment.ts new file mode 100644 index 000000000..09d75a4ba --- /dev/null +++ b/connector-ui/src/environments/edc-ui-environment.ts @@ -0,0 +1,6 @@ +/** + * We have multiple environment files that will replace environment.ts depending on active angular configuration. + */ +export interface EdcUiEnvironment { + production: boolean; +} diff --git a/connector-ui/src/environments/environment.prod.ts b/connector-ui/src/environments/environment.prod.ts new file mode 100644 index 000000000..c99109fea --- /dev/null +++ b/connector-ui/src/environments/environment.prod.ts @@ -0,0 +1,5 @@ +import {EdcUiEnvironment} from './edc-ui-environment'; + +export const environment: EdcUiEnvironment = { + production: true, +}; diff --git a/connector-ui/src/environments/environment.ts b/connector-ui/src/environments/environment.ts new file mode 100644 index 000000000..bb42b3c31 --- /dev/null +++ b/connector-ui/src/environments/environment.ts @@ -0,0 +1,19 @@ +// This file can be replaced during build by using the `fileReplacements` array. +// `ng build` replaces `environment.ts` with `environment.prod.ts`. +// The list of file replacements can be found in `angular.json`. + +/* + * For easier debugging in development mode, you can import the following file + * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. + * + * This import should be commented out in production mode because it will have a negative impact + * on performance if an error is thrown. + */ +import 'zone.js/plugins/zone-error'; +import {EdcUiEnvironment} from './edc-ui-environment'; + +export const environment: EdcUiEnvironment = { + production: false, +}; + +// Included with Angular CLI. diff --git a/connector-ui/src/index.html b/connector-ui/src/index.html new file mode 100644 index 000000000..f07329fee --- /dev/null +++ b/connector-ui/src/index.html @@ -0,0 +1,12 @@ + + + + + EDC Demo + + + + + + + diff --git a/connector-ui/src/main.ts b/connector-ui/src/main.ts new file mode 100644 index 000000000..61a73957a --- /dev/null +++ b/connector-ui/src/main.ts @@ -0,0 +1,16 @@ +import {enableProdMode} from '@angular/core'; +import {platformBrowserDynamic} from '@angular/platform-browser-dynamic'; +import {AppModule} from './app/app.module'; +import {loadAppConfig} from './app/core/config/app-config-initializer'; +import {environment} from './environments/environment'; + +if (environment.production) { + enableProdMode(); +} + +// We fetch the config here, because we need the config before APP_INITIALIZER, +// because we want to decide our routes based on our config, and ROUTES needs +// to be provided before APP_INITIALIZER. +loadAppConfig() + .then(() => platformBrowserDynamic().bootstrapModule(AppModule)) + .catch((err) => console.error(err)); diff --git a/connector-ui/src/polyfills.ts b/connector-ui/src/polyfills.ts new file mode 100644 index 000000000..cbe685175 --- /dev/null +++ b/connector-ui/src/polyfills.ts @@ -0,0 +1,66 @@ +/** + * This file includes polyfills needed by Angular and is loaded before the app. + * You can add your own extra polyfills to this file. + * + * This file is divided into 2 sections: + * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. + * 2. Application imports. Files imported after ZoneJS that should be loaded before your main + * file. + * + * The current setup is for so-called "evergreen" browsers; the last versions of browsers that + * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), + * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. + * + * Learn more in https://angular.io/guide/browser-support + */ + +/*************************************************************************************************** + * BROWSER POLYFILLS + */ + +/** + * IE11 requires the following for NgClass support on SVG elements + */ +// import 'classlist.js'; // Run `npm install --save classlist.js`. + +/** + * Web Animations `@angular/platform-browser/animations` + * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. + * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). + */ +// import 'web-animations-js'; // Run `npm install --save web-animations-js`. + +/** + * By default, zone.js will patch all possible macroTask and DomEvents + * user can disable parts of macroTask/DomEvents patch by setting following flags + * because those flags need to be set before `zone.js` being loaded, and webpack + * will put import in the top of bundle, so user need to create a separate file + * in this directory (for example: zone-flags.ts), and put the following flags + * into that file, and then add the following code before importing zone.js. + * import './zone-flags'; + * + * The flags allowed in zone-flags.ts are listed here. + * + * The following flags will work for all browsers. + * + * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame + * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick + * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames + * + * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js + * with the following flag, it will bypass `zone.js` patch for IE/Edge + * + * (window as any).__Zone_enable_cross_context_check = true; + * + */ + +/*************************************************************************************************** + * Zone JS is required by default for Angular itself. + */ +import 'zone.js'; + +// Included with Angular CLI. + +/*************************************************************************************************** + * APPLICATION IMPORTS + */ diff --git a/connector-ui/src/styles.scss b/connector-ui/src/styles.scss new file mode 100644 index 000000000..464185a7e --- /dev/null +++ b/connector-ui/src/styles.scss @@ -0,0 +1,320 @@ +@forward 'theme'; +@forward 'tailwind'; + +html, +body { + height: 100%; +} + +body { + margin: 0; +} + +.snackbar-info-style { + background-color: orange; + color: black; +} + +.snackbar-error-style { + background-color: crimson; + color: black; +} + +.code { + font-family: monospace; + font-size: larger; +} + +.text-ellipsis { + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; +} + +.mat-hint-text-ellipsis { + mat-hint { + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + } +} + +.asset-card { + width: 300px; +} + +.list-style-material-icons-check { + list-style-type: none; + padding-inline-start: 8px; + + li { + margin-top: 8px; + + &:before { + margin-right: 8px; + content: 'check'; + font-family: 'Material Icons'; + } + } +} + +.form-section-title { + margin: 16px 3px; + text-transform: uppercase; + color: #373737; + font-size: 14px; + letter-spacing: 0.04em; +} + +.property-grid-group-title { + margin: 30px 3px 15px 3px; + text-transform: uppercase; + color: #373737; + font-size: 14px; + letter-spacing: 0.05em; +} + +.mat-form-field-any-size-hint .mat-form-field-subscript-wrapper { + position: unset !important; +} + +// hrefs +.link { + color: var(--link-color); + text-decoration: none; + + &:hover, + &:focus { + text-decoration: underline; + } +} + +.flex-even-sized { + flex: 1 1 0; +} + +.text-slate { + color: rgba(0, 0, 0, 0.54); +} + +.text-warn { + color: var(--warn-color); +} + +.bg-muted { + background: rgba(0, 0, 0, 0.12); +} + +.text-sm { + font-size: 0.8em; +} + +.mat-icon-\[80px\] { + font-size: 80px !important; + width: 80px !important; + height: 80px !important; + line-height: 80px !important; +} + +.mat-icon-\[40px\] { + font-size: 40px !important; + width: 40px !important; + height: 40px !important; + line-height: 40px !important; +} + +.mat-icon-\[10px\] { + font-size: 10px !important; + width: 10px !important; + height: 10px !important; + line-height: 10px !important; +} + +.mat-icon-\[14px\] { + margin-right: 5px; + font-size: 14px !important; + width: 14px !important; + height: 14px !important; + line-height: 14px !important; +} + +.mat-icon-\[16px\] { + font-size: 16px !important; + width: 16px !important; + height: 16px !important; + line-height: 16px !important; +} + +.mat-icon-\[12px\] { + font-size: 12px !important; + width: 12px !important; + height: 12px !important; + line-height: 12px !important; +} + +.mat-icon-\[18px\] { + margin-top: 5px; + font-size: 18px !important; + width: 18px !important; + height: 18px !important; + line-height: 18px !important; +} + +.mat-icon-\[22px\] { + font-size: 22px !important; + width: 22px !important; + height: 22px !important; + line-height: 22px !important; +} + +.mat-icon-\[24px\] { + font-size: 24px !important; + width: 24px !important; + height: 24px !important; + line-height: 24px !important; +} + +.mat-icon-\[28px\] { + margin-top: 8px; + font-size: 28px !important; + width: 28px !important; + height: 28px !important; + line-height: 28px !important; +} + +.mat-icon-\[17px\] { + font-size: 17px !important; + line-height: 17px !important; + width: 17px !important; + height: 17px !important; +} + +.mat-card-header-text { + overflow-wrap: break-word; + overflow: hidden; +} + +.mat-expansion-panel-flex-col .mat-expansion-panel-body { + display: flex; + flex-direction: column; +} + +.mat-form-field-without-margin-bottom { + .mat-form-field-wrapper { + margin-bottom: 0 !important; + margin-top: 0 !important; + padding-bottom: 0 !important; + } + .mat-form-field-subscript-wrapper { + top: unset !important; + } +} + +.max-two-lines-list-item .mat-list-text { + display: -webkit-box !important; + -webkit-line-clamp: 2; + line-clamp: 2; + -webkit-box-orient: vertical; + text-overflow: ellipsis; + overflow: hidden !important; +} + +.mat-drawer-inner-container { + display: flex; + flex-direction: column; + overflow-x: hidden !important; +} + +.markdown-description { + @apply prose whitespace-normal max-w-none; + @apply prose-img:rounded-lg prose-img:my-3 prose-img:object-contain prose-img:max-w-full prose-img:max-h-[250px]; + @apply prose-a:text-[--link-color]; + @apply prose-blockquote:mx-0; + @apply prose-code:rounded-lg; + @apply prose-blockquote:border-solid prose-blockquote:border-t-0 prose-blockquote:border-r-0 prose-blockquote:border-b-0; + @apply prose-table:border-collapse prose-table:text-[13px]; + @apply prose-th:px-[13px] prose-th:py-[6px] prose-th:border prose-th:border-[--tw-prose-td-borders] prose-th:align-middle prose-th:border-solid; + @apply prose-td:px-[13px] prose-td:py-[6px] prose-td:border prose-td:border-[--tw-prose-td-borders] prose-td:align-middle prose-td:border-solid; + + font: 400 14px / 20px Sans; + letter-spacing: normal; + + h1, + h2, + h3, + h4, + h5 { + font-weight: 500; + } + h1 { + font-size: 19px; + } + + h2 { + font-size: 17px; + } + + h3 { + font-size: 15px; + } + + h4 { + font-size: 14px; + } + + h5 { + font-size: 12px; + } + + pre code { + @apply p-0; + border: initial; + line-height: 1.5; + } + + a:has(> img) { + @apply w-fit; + } + + p:has(> a > img) { + @apply flex flex-col; + } + + :last-child { + margin: 0px; + } +} + +.mat-menu-content { + padding: 0 !important; +} + +.mat-menu-panel { + min-height: 0 !important; +} + +.mat-icon-button { + &:hover { + background: #ededed !important; + } + + mat-icon { + margin-top: -1px; + } +} + +#edit-asset-form .mat-form-field-infix { + border-top: 4.8px solid transparent !important; +} + +.normal-form-field .mat-form-field-infix { + border-top: 13.6px solid transparent !important; +} + +#edit-asset-form .mat-form-field-subscript-wrapper { + font-size: 100%; +} + +#edit-asset-form .mat-hint { + font-size: 0.75rem; +} diff --git a/connector-ui/src/tailwind.scss b/connector-ui/src/tailwind.scss new file mode 100644 index 000000000..7eed5d6e7 --- /dev/null +++ b/connector-ui/src/tailwind.scss @@ -0,0 +1,26 @@ +/** + * This injects Tailwind's base styles and any base styles registered by + * plugins. + */ +@tailwind base; + +/** + * This injects Tailwind's component classes and any component classes + * registered by plugins. + */ +@tailwind components; + +/** + * This injects Tailwind's utility classes and any utility classes registered + * by plugins. + */ +@tailwind utilities; + +/** + * Use this directive to control where Tailwind injects the hover, focus, + * responsive, dark mode, and other variants of each class. + * + * If omitted, Tailwind will append these classes to the very end of + * your stylesheet by default. + */ +// @tailwind variants; diff --git a/connector-ui/src/test.ts b/connector-ui/src/test.ts new file mode 100644 index 000000000..22f342169 --- /dev/null +++ b/connector-ui/src/test.ts @@ -0,0 +1,29 @@ +// This file is required by karma.conf.js and loads recursively all the .spec and framework files +import 'zone.js/testing'; +import {getTestBed} from '@angular/core/testing'; +import { + BrowserDynamicTestingModule, + platformBrowserDynamicTesting, +} from '@angular/platform-browser-dynamic/testing'; + +declare const require: { + context( + path: string, + deep?: boolean, + filter?: RegExp, + ): { + (id: string): T; + keys(): string[]; + }; +}; + +// First, initialize the Angular testing environment. +getTestBed().initTestEnvironment( + BrowserDynamicTestingModule, + platformBrowserDynamicTesting(), +); + +// Then we find all the tests. +const context = require.context('./', true, /\.spec\.ts$/); +// And load the modules. +context.keys().forEach(context); diff --git a/connector-ui/src/theme-colors.mds.scss b/connector-ui/src/theme-colors.mds.scss new file mode 100644 index 000000000..50c938a39 --- /dev/null +++ b/connector-ui/src/theme-colors.mds.scss @@ -0,0 +1,88 @@ +@use '@angular/material' as mat; +@use 'sass:map'; + +/* Theming (MDS EDC UI) */ + +// theme set as per http://mcg.mbitson.com/#!?mcgpalette0=%23ffff00&themename=mds +$theme-colors-primary: ( + 50: #ffffe0, + 100: #ffffb3, + 200: #ffff80, + 300: #ffff4d, + 400: #ffff26, + 500: #ffff00, + 600: #ffff00, + 700: #ffff00, + 800: #ffff00, + 900: #ffff00, + A100: #ffffff, + A200: #fffff2, + A400: #ffffbf, + A700: #ffffa6, + contrast: ( + 50: #000000, + 100: #000000, + 200: #000000, + 300: #000000, + 400: #000000, + 500: #000000, + 600: #000000, + 700: #000000, + 800: #000000, + 900: #000000, + A100: #000000, + A200: #000000, + A400: #000000, + A700: #000000, + ), +); + +$link-color: darken (map.get($theme-colors-primary, 500), 15%); + +$theme-colors-accent: ( + 50: #ffffff, + 100: #000000, + 200: #000000, + 300: #000000, + 400: #000000, + 500: #000000, + 600: #000000, + 700: #000000, + 800: #000000, + 900: #000000, + A100: #000000, + A200: #000000, + A400: #000000, + A700: #000000, + contrast: ( + 50: #000000, + 100: #000000, + 200: #000000, + 300: #ffffff, + 400: #ffffff, + 500: #ffffff, + 600: #ffffff, + 700: #ffffff, + 800: #ffffff, + 900: #ffffff, + A100: #000000, + A200: #ffffff, + A400: #ffffff, + A700: #ffffff, + ), +); + +$theme-palette-primary: mat.define-palette($theme-colors-primary); +$theme-palette-accent: mat.define-palette( + $theme-colors-accent, + A200, + A100, + A400 +); +$theme-palette-warn: mat.define-palette(mat.$red-palette); + +$theme: mat.define-light-theme( + $theme-palette-primary, + $theme-palette-accent, + $theme-palette-warn +); diff --git a/connector-ui/src/theme-colors.scss b/connector-ui/src/theme-colors.scss new file mode 100644 index 000000000..186fd888f --- /dev/null +++ b/connector-ui/src/theme-colors.scss @@ -0,0 +1,82 @@ +@use '@angular/material' as mat; +@use 'sass:map'; + +/* Theming (Sovity Stock EDC UI) */ + +$theme-colors-primary: ( + 50: #e0e5ec, + 100: #b3bed0, + 200: #8092b1, + 300: #4d6691, + 400: #26467a, + 500: #002562, + 600: #00215a, + 700: #001b50, + 800: #001646, + 900: #000d34, + A100: #6c80ff, + A200: #3954ff, + A400: #0628ff, + A700: #0020eb, + contrast: ( + 50: #000000, + 100: #000000, + 200: #000000, + 300: #ffffff, + 400: #ffffff, + 500: #ffffff, + 600: #ffffff, + 700: #ffffff, + 800: #ffffff, + 900: #ffffff, + A100: #000000, + A200: #ffffff, + A400: #ffffff, + A700: #ffffff, + ), +); + +$link-color: lighten(map.get($theme-colors-primary, 500), 5%); + +$theme-colors-accent: ( + 50: #ffffff, + 100: #000000, + 200: #000000, + 300: #000000, + 400: #000000, + 500: #000000, + 600: #000000, + 700: #000000, + 800: #000000, + 900: #000000, + A100: #000000, + A200: #000000, + A400: #000000, + A700: #000000, + contrast: ( + 50: #000000, + 100: #000000, + 200: #000000, + 300: #ffffff, + 400: #ffffff, + 500: #ffffff, + 600: #ffffff, + 700: #ffffff, + 800: #ffffff, + 900: #ffffff, + A100: #000000, + A200: #ffffff, + A400: #ffffff, + A700: #ffffff, + ), +); + +$theme-palette-primary: mat.define-palette($theme-colors-primary); +$theme-palette-accent: mat.define-palette($theme-colors-accent); +$theme-palette-warn: mat.define-palette(mat.$red-palette); + +$theme: mat.define-light-theme( + $theme-palette-primary, + $theme-palette-accent, + $theme-palette-warn +); diff --git a/connector-ui/src/theme-generated-variables.scss b/connector-ui/src/theme-generated-variables.scss new file mode 100644 index 000000000..ddec267ac --- /dev/null +++ b/connector-ui/src/theme-generated-variables.scss @@ -0,0 +1,25 @@ +@use 'sass:map'; +@use '@angular/material' as mat; + +@mixin theme-vars($theme, $link-color) { + --link-color: #{$link-color}; + --warn-color: #{mat.get-color-from-palette(map-get($theme, warn))}; + + --ngx-json-string: black; // color of string values + --ngx-json-number: black; // color of number values + --ngx-json-boolean: black; // color of boolean values + --ngx-json-date: black; // color of date values + // --ngx-json-array: ; // color of array values + // --ngx-json-object: ; // color of object values + // --ngx-json-function: ; // color of function values + --ngx-json-null: black; // color of null values + --ngx-json-null-bg: transparent; // background color of null values + // --ngx-json-undefined: ; // color of undefined values + // --ngx-json-toggler: ; // color of toggler + --ngx-json-key: #002562; // color of keys + // --ngx-json-separator: ; // color of separators + // --ngx-json-value: ; // color of values + // --ngx-json-undefined-key: ; // color for key of undefined values + // --ngx-json-font-family: ; // font-family + // --ngx-json-font-size: ; // font-size +} diff --git a/connector-ui/src/theme.scss b/connector-ui/src/theme.scss new file mode 100644 index 000000000..f89e157c5 --- /dev/null +++ b/connector-ui/src/theme.scss @@ -0,0 +1,124 @@ +@use '@angular/material' as mat; +@use 'theme-colors' as sovityColors; +@use 'theme-colors.mds' as mdsColors; +@use 'theme-generated-variables' as themeGeneratedVars; + +// Import styles that aren't theme dependant (including typography) + +@font-face { + font-display: swap; + font-family: 'Sans'; + font-style: normal; + font-weight: 300; + src: url('assets/fonts/Inter-Light.woff2') format('woff2'); +} + +@font-face { + font-display: swap; + font-family: 'Sans'; + font-style: normal; + font-weight: 400; + src: url('assets/fonts/Inter-Regular.woff2') format('woff2'); +} + +@font-face { + font-display: swap; + font-family: 'Sans'; + font-style: normal; + font-weight: 500; + src: url('assets/fonts/Inter-Medium.woff2') format('woff2'); +} + +@font-face { + font-display: swap; + font-family: 'Sans'; + font-style: normal; + font-weight: 600; + src: url('assets/fonts/Inter-SemiBold.woff2') format('woff2'); +} + +@font-face { + font-display: swap; + font-family: 'Sans'; + font-style: normal; + font-weight: 700; + src: url('assets/fonts/Inter-Bold.woff2') format('woff2'); +} + +/* material-icons-regular - latin */ +@font-face { + font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */ + font-family: 'Material Icons'; + font-style: normal; + font-weight: 400; + src: url('assets/fonts/material-icons-v140-latin-regular.woff2') + format('woff2'); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */ +} + +.material-icons { + font-family: 'Material Icons'; + font-weight: normal; + font-style: normal; + font-size: 24px; + line-height: 1; + letter-spacing: normal; + text-transform: none; + display: inline-block; + white-space: nowrap; + word-wrap: normal; + direction: ltr; + -webkit-font-feature-settings: 'liga'; + -webkit-font-smoothing: antialiased; +} + +$custom-typography: mat.define-typography-config( + $font-family: 'Sans', +); +@include mat.core($custom-typography); + +// Import theme and theme-dependant styles +.theme-sovity { + @include mat.all-component-themes(sovityColors.$theme); + @include themeGeneratedVars.theme-vars( + sovityColors.$theme, + sovityColors.$link-color + ); +} + +.theme-mds { + @include mat.all-component-themes(mdsColors.$theme); + @include themeGeneratedVars.theme-vars( + mdsColors.$theme, + mdsColors.$link-color + ); +} + +// fix paginator background, it was white before, but our background is gray +mat-paginator { + background: transparent !important; +} + +// Fix Icon sizes used in card avatars +mat-icon[mat-card-avatar], +.mat-card-avatar-icon { + font-size: 40px !important; + width: 40px !important; + height: 40px !important; + line-height: 40px !important; + margin-top: 1px; + margin-right: -3px; + margin-left: 2px; +} + +// Fix mat-chips exploding out and/or having multiline exploding strings +mat-chip { + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +// Fix Date range second-date-text being "higher" than first-date-text +.mat-date-range-input-container { + align-items: unset !important; +} diff --git a/connector-ui/src/types.d.ts b/connector-ui/src/types.d.ts new file mode 100644 index 000000000..d334b3156 --- /dev/null +++ b/connector-ui/src/types.d.ts @@ -0,0 +1,2 @@ +// Required since this dependency does not have a types.d.ts file +declare module 'json-stable-stringify'; diff --git a/connector-ui/tailwind.config.js b/connector-ui/tailwind.config.js new file mode 100644 index 000000000..0312164eb --- /dev/null +++ b/connector-ui/tailwind.config.js @@ -0,0 +1,22 @@ +module.exports = { + prefix: '', + purge: { + content: ['./src/**/*.{html,ts}'], + }, + darkMode: 'class', // or 'media' or 'class' + theme: { + container: { + center: true, + padding: '0.5rem', + screens: { + '2xl': '87.5rem', + '3xl': '100rem', + }, + }, + extend: {}, + }, + variants: { + extend: {}, + }, + plugins: [require('@tailwindcss/typography')], +}; diff --git a/connector-ui/tsconfig.app.json b/connector-ui/tsconfig.app.json new file mode 100644 index 000000000..ff396d4ce --- /dev/null +++ b/connector-ui/tsconfig.app.json @@ -0,0 +1,10 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/app", + "types": [] + }, + "files": ["src/main.ts", "src/polyfills.ts"], + "include": ["src/**/*.d.ts"] +} diff --git a/connector-ui/tsconfig.json b/connector-ui/tsconfig.json new file mode 100644 index 000000000..a00f06927 --- /dev/null +++ b/connector-ui/tsconfig.json @@ -0,0 +1,30 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "compileOnSave": false, + "compilerOptions": { + "baseUrl": "./", + "outDir": "./dist/out-tsc", + "forceConsistentCasingInFileNames": true, + "strict": true, + "noUnusedLocals": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "sourceMap": true, + "declaration": false, + "downlevelIteration": true, + "experimentalDecorators": true, + "moduleResolution": "node", + "importHelpers": true, + "target": "es2020", + "module": "es2020", + "lib": ["es2021", "dom"], + "skipLibCheck": true + }, + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true, + "fullTemplateTypeCheck": true + } +} diff --git a/connector-ui/tsconfig.spec.json b/connector-ui/tsconfig.spec.json new file mode 100644 index 000000000..669344f8d --- /dev/null +++ b/connector-ui/tsconfig.spec.json @@ -0,0 +1,10 @@ +/* To learn more about this file see: https://angular.io/config/tsconfig. */ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/spec", + "types": ["jasmine"] + }, + "files": ["src/test.ts", "src/polyfills.ts"], + "include": ["src/**/*.spec.ts", "src/**/*.d.ts"] +} diff --git a/connector-ui/webpack.config.js b/connector-ui/webpack.config.js new file mode 100644 index 000000000..3c2e58f02 --- /dev/null +++ b/connector-ui/webpack.config.js @@ -0,0 +1,22 @@ +module.exports = { + module: { + rules: [ + // Add PostCSS Loader fir Tailwind Utilities + { + test: /\.scss$/, + loader: 'postcss-loader', + options: { + postcssOptions: { + ident: 'postcss', + syntax: 'postcss-scss', + plugins: [ + require('postcss-import'), + require('tailwindcss'), + require('autoprefixer'), + ], + }, + }, + }, + ], + }, +};