diff --git a/.devcontainer/updateContentCommand.sh b/.devcontainer/updateContentCommand.sh index 4be12810ac..c08b35545b 100755 --- a/.devcontainer/updateContentCommand.sh +++ b/.devcontainer/updateContentCommand.sh @@ -3,10 +3,10 @@ export WEB_CONCURRENCY=2 invoke demo.build demo.start sleep 120 -docker logs infrahub-infrahub-server-1 +docker logs infrahub-server-1 invoke demo.load-infra-schema -docker logs infrahub-infrahub-server-1 +docker logs infrahub-server-1 sleep 90 -docker logs infrahub-infrahub-server-1 +docker logs infrahub-server-1 invoke demo.load-infra-data invoke demo.stop diff --git a/.github/workflows/ci-docker-image.yml b/.github/workflows/ci-docker-image.yml index 39cd234552..c266d88e62 100644 --- a/.github/workflows/ci-docker-image.yml +++ b/.github/workflows/ci-docker-image.yml @@ -81,5 +81,3 @@ jobs: platforms: ${{ inputs.platforms }} tags: ${{ inputs.tags }} labels: ${{ inputs.labels }} - cache-from: type=gha - cache-to: type=gha,mode=max diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5db74683b4..a058fda216 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,15 +15,15 @@ concurrency: env: INFRAHUB_DB_USERNAME: neo4j INFRAHUB_DB_PASSWORD: admin - INFRAHUB_DB_ADDRESS: localhost - INFRAHUB_DB_PORT: 7687 INFRAHUB_DB_PROTOCOL: bolt - INFRAHUB_BROKER_ADDRESS: message-queue INFRAHUB_LOG_LEVEL: CRITICAL INFRAHUB_IMAGE_NAME: "opsmill/infrahub" INFRAHUB_IMAGE_VER: "local" + INFRAHUB_SERVER_PORT: 0 + INFRAHUB_DB_BACKUP_PORT: 0 + VMAGENT_PORT: 0 PYTEST_XDIST_WORKER_COUNT: 4 - INFRAHUB_TEST_IN_DOCKER: 1 + INFRAHUB_USE_TEST_CONTAINERS: 1 VALE_VERSION: "3.7.1" GITHUB_PR_NUMBER: ${{ github.event.pull_request.number }} METRICS_ENDPOINT: ${{ secrets.METRICS_ENDPOINT }} @@ -90,8 +90,6 @@ jobs: run: "yamllint -s ." javascript-lint: - if: needs.files-changed.outputs.javascript == 'true' - needs: ["files-changed"] runs-on: ubuntu-latest timeout-minutes: 5 steps: @@ -99,18 +97,13 @@ jobs: uses: "actions/checkout@v4" with: submodules: true - - name: Install NodeJS - uses: actions/setup-node@v4 + - name: Setup Biome + uses: biomejs/setup-biome@v2 with: - node-version: 20 - cache: 'npm' - cache-dependency-path: frontend/app/package-lock.json - - name: Install frontend dependencies - working-directory: ./frontend/app - run: npm install - - name: Run ESLint + version: 1.9.3 + - name: Run Biome working-directory: ./frontend/app - run: npm run eslint + run: biome ci . python-lint: if: needs.files-changed.outputs.python == 'true' @@ -123,11 +116,11 @@ jobs: with: submodules: true - name: "Setup environment" - run: "pip install ruff==0.5.0" + run: "pip install ruff==0.6.6" - name: "Linting: ruff check" - run: "ruff check ." + run: "ruff check . --exclude python_sdk" - name: "Linting: ruff format" - run: "ruff format --check --diff ." + run: "ruff format --check --diff --exclude python_sdk ." markdown-lint: if: needs.files-changed.outputs.documentation == 'true' @@ -140,7 +133,7 @@ jobs: with: submodules: true - name: "Linting: markdownlint" - uses: DavidAnson/markdownlint-cli2-action@v16 + uses: DavidAnson/markdownlint-cli2-action@v17 with: config: .markdownlint.yaml globs: | @@ -241,24 +234,28 @@ jobs: group: huge-runners timeout-minutes: 45 env: - INFRAHUB_DB_TYPE: memgraph + INFRAHUB_DB_TYPE: neo4j steps: - name: "Check out repository code" uses: "actions/checkout@v4" with: submodules: true + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.12' + - name: "Setup git credentials" + run: "git config --global user.name 'Infrahub' && \ + git config --global user.email 'infrahub@opsmill.com' && \ + git config --global --add safe.directory '*' && \ + git config --global credential.usehttppath true && \ + git config --global credential.helper /usr/local/bin/infrahub-git-credential" - name: "Setup Python environment" - run: "pip install toml invoke" - - name: "Set environment variables" - run: echo INFRAHUB_BUILD_NAME=infrahub-${{ runner.name }} >> $GITHUB_ENV - - name: "Set environment variables" - run: echo INFRAHUB_IMAGE_VER=local-${{ runner.name }}-${{ github.sha }} >> $GITHUB_ENV - - name: "Clear docker environment" - run: docker compose -p $INFRAHUB_BUILD_NAME down -v --remove-orphans --rmi local - - name: "Build Test Image" - run: "invoke dev.build" - - name: "Pull External Docker Images" - run: "invoke dev.pull" + run: | + poetry config virtualenvs.create false + pip install toml invoke + - name: "Install dependencies" + run: "poetry install --no-interaction --no-ansi" - name: "Unit Tests" run: "invoke backend.test-unit" - name: "Coveralls : Unit Tests" @@ -270,7 +267,7 @@ jobs: flag-name: backend-unit parallel: true - name: Generate tracing spans - if: always() && github.event.pull_request.head.repo.fork == false + if: always() && github.event.pull_request.head.repo.fork == false && github.actor != 'dependabot[bot]' uses: inception-health/otel-upload-test-artifact-action@v1 with: jobName: "backend-tests-unit" @@ -296,22 +293,26 @@ jobs: uses: "actions/checkout@v4" with: submodules: true + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.12' + - name: "Setup git credentials" + run: "git config --global user.name 'Infrahub' && \ + git config --global user.email 'infrahub@opsmill.com' && \ + git config --global --add safe.directory '*' && \ + git config --global credential.usehttppath true && \ + git config --global credential.helper /usr/local/bin/infrahub-git-credential" - name: "Setup Python environment" - run: "pip install toml invoke" - - name: "Set environment variables" - run: echo INFRAHUB_BUILD_NAME=infrahub-${{ runner.name }} >> $GITHUB_ENV - - name: "Set environment variables" - run: echo INFRAHUB_IMAGE_VER=local-${{ runner.name }}-${{ github.sha }} >> $GITHUB_ENV - - name: "Clear docker environment" - run: docker compose -p $INFRAHUB_BUILD_NAME down -v --remove-orphans --rmi local - - name: "Build Test Image" - run: "invoke dev.build" - - name: "Pull External Docker Images" - run: "invoke dev.pull" + run: | + poetry config virtualenvs.create false + pip install toml invoke + - name: "Install dependencies" + run: "poetry install --no-interaction --no-ansi" - name: "Mypy Tests" - run: "invoke backend.mypy --docker" + run: "invoke backend.mypy" - name: "Pylint Tests" - run: "invoke backend.pylint --docker" + run: "invoke backend.pylint" - name: "Integration Tests" run: "invoke backend.test-integration" - name: "Coveralls : Integration Tests" @@ -323,7 +324,7 @@ jobs: flag-name: backend-integration parallel: true - backend-tests-neo4j: + backend-tests-memgraph: if: | always() && !cancelled() && !contains(needs.*.result, 'failure') && @@ -337,18 +338,15 @@ jobs: fail-fast: false matrix: include: - - name: backend-tests-neo4j - env: - INFRAHUB_DB_TYPE: neo4j - - name: backend-tests-nats + - name: backend-tests-unit-memgraph env: INFRAHUB_DB_TYPE: memgraph - INFRAHUB_USE_NATS: 1 - INFRAHUB_BROKER_DRIVER: nats - INFRAHUB_BROKER_PORT: 4222 - INFRAHUB_CACHE_DRIVER: nats - INFRAHUB_CACHE_ADDRESS: message-queue - INFRAHUB_CACHE_PORT: 4222 + # - name: backend-tests-unit-nats + # env: + # INFRAHUB_DB_TYPE: memgraph + # INFRAHUB_USE_NATS: 1 + # INFRAHUB_BROKER_DRIVER: nats + # INFRAHUB_CACHE_DRIVER: nats name: ${{ matrix.name }} env: ${{ matrix.env }} steps: @@ -356,18 +354,22 @@ jobs: uses: "actions/checkout@v4" with: submodules: true + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.12' + - name: "Setup git credentials" + run: "git config --global user.name 'Infrahub' && \ + git config --global user.email 'infrahub@opsmill.com' && \ + git config --global --add safe.directory '*' && \ + git config --global credential.usehttppath true && \ + git config --global credential.helper /usr/local/bin/infrahub-git-credential" - name: "Setup Python environment" - run: "pip install toml invoke" - - name: "Set environment variables" - run: echo INFRAHUB_BUILD_NAME=infrahub-${{ runner.name }} >> $GITHUB_ENV - - name: "Set environment variables" - run: echo INFRAHUB_IMAGE_VER=local-${{ runner.name }}-${{ github.sha }} >> $GITHUB_ENV - - name: "Clear docker environment" - run: docker compose -p $INFRAHUB_BUILD_NAME down -v --remove-orphans --rmi local - - name: "Build Test Image" - run: "invoke dev.build" - - name: "Pull External Docker Images" - run: "invoke dev.pull" + run: | + poetry config virtualenvs.create false + pip install toml invoke + - name: "Install dependencies" + run: "poetry install --no-interaction --no-ansi" - name: "Unit Tests" run: "invoke backend.test-unit" @@ -554,15 +556,15 @@ jobs: - name: E2E-testing-playwright env: INFRAHUB_DB_TYPE: neo4j - - name: E2E-testing-playwright-nats - env: - INFRAHUB_DB_TYPE: neo4j - INFRAHUB_USE_NATS: 1 - INFRAHUB_BROKER_DRIVER: nats - INFRAHUB_BROKER_PORT: 4222 - INFRAHUB_CACHE_DRIVER: nats - INFRAHUB_CACHE_ADDRESS: message-queue - INFRAHUB_CACHE_PORT: 4222 + # - name: E2E-testing-playwright-nats + # env: + # INFRAHUB_DB_TYPE: neo4j + # INFRAHUB_USE_NATS: 1 + # INFRAHUB_BROKER_DRIVER: nats + # INFRAHUB_BROKER_PORT: 4222 + # INFRAHUB_CACHE_DRIVER: nats + # INFRAHUB_CACHE_ADDRESS: message-queue + # INFRAHUB_CACHE_PORT: 4222 name: ${{ matrix.name }} env: ${{ matrix.env }} steps: @@ -580,17 +582,11 @@ jobs: - name: Install Invoke run: pip install toml invoke - - name: Select infrahub port - run: echo "INFRAHUB_SERVER_PORT=$(shuf -n 1 -i 10000-30000)" >> $GITHUB_ENV - - name: Select infrahub db port - run: echo "INFRAHUB_DB_BACKUP_PORT=$(shuf -n 1 -i 10000-30000)" >> $GITHUB_ENV - - name: Select vmagent port - run: echo "VMAGENT_PORT=$(shuf -n 1 -i 10000-30000)" >> $GITHUB_ENV - name: Set job name run: echo JOB_NAME="$GITHUB_JOB" >> $GITHUB_ENV - name: Enable tracing - if: github.event.pull_request.head.repo.fork == false + if: github.event.pull_request.head.repo.fork == false && github.actor != 'dependabot[bot]' run: echo "INFRAHUB_TRACE_ENABLE=true" >> $GITHUB_ENV - name: Set tracing configuration run: echo "INFRAHUB_TRACE_INSECURE=false" >> $GITHUB_ENV @@ -631,7 +627,9 @@ jobs: run: invoke dev.infra-git-import dev.infra-git-create - name: Set infrahub address - run: echo "INFRAHUB_ADDRESS=http://localhost:${INFRAHUB_SERVER_PORT}" >> $GITHUB_ENV + run: | + PORT=$(docker compose -p $INFRAHUB_BUILD_NAME port server 8000 | cut -d: -f2) + echo "INFRAHUB_ADDRESS=http://localhost:${PORT}" >> $GITHUB_ENV - name: Install frontend dependencies run: npm install @@ -657,11 +655,17 @@ jobs: env: INFRAHUB_MISC_RESPONSE_DELAY: 2 + - name: Set infrahub address + if: needs.files-changed.outputs.e2e_tests == 'true' + run: | + PORT=$(docker compose -p $INFRAHUB_BUILD_NAME port server 8000 | cut -d: -f2) + echo "INFRAHUB_ADDRESS=http://localhost:${PORT}" >> $GITHUB_ENV + - name: Run Playwright tests run: npm run ci:test:e2e - name: Generate tracing spans - if: always() && github.event.pull_request.head.repo.fork == false + if: always() && github.event.pull_request.head.repo.fork == false && github.actor != 'dependabot[bot]' uses: inception-health/otel-upload-test-artifact-action@v1 with: jobName: "E2E-testing-playwright" @@ -683,15 +687,19 @@ jobs: - name: Display server logs if: always() - run: docker logs "${INFRAHUB_BUILD_NAME}-infrahub-server-1" + run: docker logs "${INFRAHUB_BUILD_NAME}-server-1" - - name: Display git 1 logs + - name: Display task worker 1 logs if: always() - run: docker logs "${INFRAHUB_BUILD_NAME}-infrahub-git-1" + run: docker logs "${INFRAHUB_BUILD_NAME}-task-worker-1" - - name: Display git 2 logs + - name: Display task worker 2 logs if: always() - run: docker logs "${INFRAHUB_BUILD_NAME}-infrahub-git-2" + run: docker logs "${INFRAHUB_BUILD_NAME}-task-worker-2" + + - name: Display task manager logs + if: always() + run: docker logs "${INFRAHUB_BUILD_NAME}-task-manager-1" - name: Display database logs if: always() @@ -731,7 +739,7 @@ jobs: runs-on: group: huge-runners env: - INFRAHUB_DB_TYPE: memgraph + INFRAHUB_DB_TYPE: neo4j steps: - name: Check out repository code uses: actions/checkout@v4 @@ -740,33 +748,19 @@ jobs: - name: Set up Python uses: actions/setup-python@v5 with: - python-version: 3.12 - - name: "Setup environment" + python-version: '3.12' + - name: "Setup git credentials" + run: "git config --global user.name 'Infrahub' && \ + git config --global user.email 'infrahub@opsmill.com' && \ + git config --global --add safe.directory '*' && \ + git config --global credential.usehttppath true && \ + git config --global credential.helper /usr/local/bin/infrahub-git-credential" + - name: "Setup Python environment" run: | - pipx install poetry - poetry config virtualenvs.prefer-active-python true - pip install invoke toml - - name: Set Version of Pydantic - run: poetry add pydantic@^2 - - name: "Install Package" - run: "poetry install" - - - name: Select infrahub db port - run: echo "INFRAHUB_DB_PORT=$(shuf -n 1 -i 10000-30000)" >> $GITHUB_ENV - - - name: "Set environment variables" - run: echo INFRAHUB_BUILD_NAME=infrahub-${{ runner.name }} >> $GITHUB_ENV - - name: "Set environment variables" - run: echo INFRAHUB_IMAGE_VER=local-${{ runner.name }}-${{ github.sha }} >> $GITHUB_ENV - - name: "Clear docker environment" - run: docker compose -p $INFRAHUB_BUILD_NAME down -v --remove-orphans --rmi local - - - name: Create docker compose override - run: mv development/docker-compose.dev-override-benchmark.yml development/docker-compose.dev-override.yml - - - name: Start dependencies - run: invoke dev.deps - + poetry config virtualenvs.create false + pip install toml invoke + - name: "Install dependencies" + run: "poetry install --no-interaction --no-ansi" - name: Update PATH run: "echo ~/.cargo/bin >> $GITHUB_PATH" - name: Run benchmarks @@ -774,8 +768,6 @@ jobs: with: token: ${{ secrets.CODSPEED_TOKEN }} run: "poetry run pytest -v backend/tests/benchmark/ --codspeed" - env: - INFRAHUB_TEST_IN_DOCKER: "false" # ------------------------------------------ Coverall Report ------------------------------------------ coverall-report: needs: diff --git a/.github/workflows/publish-dev-docker-image.yml b/.github/workflows/publish-dev-docker-image.yml index 4929f771c9..b84b533f8e 100644 --- a/.github/workflows/publish-dev-docker-image.yml +++ b/.github/workflows/publish-dev-docker-image.yml @@ -62,8 +62,8 @@ jobs: latest=false - publish-docker-image: - if: github.event_name == 'workflow_dispatch' || contains(github.event.pull_request.labels.*.name, 'cd/preview') + publish-docker-image-dispatch: + if: github.event_name == 'workflow_dispatch' uses: ./.github/workflows/ci-docker-image.yml needs: meta_data secrets: inherit @@ -74,3 +74,16 @@ jobs: tags: ${{needs.meta_data.outputs.tags}} labels: ${{needs.meta_data.outputs.labels}} platforms: ${{ inputs.platforms }} + + publish-docker-image-pr: + if: github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'cd/preview') + uses: ./.github/workflows/ci-docker-image.yml + needs: meta_data + secrets: inherit + with: + publish: true + version: dev-${{ needs.meta_data.outputs.short_ref }} + ref: ${{ needs.meta_data.outputs.ref }} + tags: ${{ needs.meta_data.outputs.tags }} + labels: ${{ needs.meta_data.outputs.labels }} + platforms: "linux/amd64" diff --git a/.github/workflows/scale-tests.yml b/.github/workflows/scale-tests.yml index d2eb603291..7f876f096d 100644 --- a/.github/workflows/scale-tests.yml +++ b/.github/workflows/scale-tests.yml @@ -26,6 +26,9 @@ env: INFRAHUB_LOG_LEVEL: CRITICAL INFRAHUB_IMAGE_NAME: "opsmill/infrahub" INFRAHUB_IMAGE_VER: "local" + INFRAHUB_SERVER_PORT: 0 + INFRAHUB_DB_BACKUP_PORT: 0 + VMAGENT_PORT: 0 jobs: scale-tests: @@ -160,16 +163,10 @@ jobs: - name: "Clear docker environment" run: docker compose -p $INFRAHUB_BUILD_NAME down -v --remove-orphans --rmi local - - name: Select infrahub port - run: echo "INFRAHUB_SERVER_PORT=$(shuf -n 1 -i 10000-30000)" >> $GITHUB_ENV - name: Set INFRAHUB_URL - run: echo "INFRAHUB_URL=http://localhost:${INFRAHUB_SERVER_PORT}" >> $GITHUB_ENV - - name: Select infrahub db port - run: echo "INFRAHUB_DB_PORT=$(shuf -n 1 -i 10000-30000)" >> $GITHUB_ENV - - name: Select infrahub db port - run: echo "INFRAHUB_DB_BACKUP_PORT=$(shuf -n 1 -i 10000-30000)" >> $GITHUB_ENV - - name: Select vmagent port - run: echo "VMAGENT_PORT=$(shuf -n 1 -i 10000-30000)" >> $GITHUB_ENV + run: | + PORT=$(docker compose -p $INFRAHUB_BUILD_NAME port infrahub-server 8000 | cut -d: -f2) + echo "INFRAHUB_URL=http://localhost:${PORT}" >> $GITHUB_ENV - name: Set job name run: echo "JOB_NAME=${{ matrix.name }}" >> $GITHUB_ENV @@ -190,7 +187,7 @@ jobs: run: 'echo "https://grafana-prod.tailc018d.ts.net/d/ebf7ec72-db79-4fb7-9b46-4621ca9c407a/scale-tests?orgId=1&var-run_id=$GITHUB_RUN_ID&var-job=${JOB_NAME// /%20}&var-stage=test&var-node_amount=${{ matrix.node-amount }}&var-attrs_amount=${{ matrix.attrs-amount }}&var-rels_amount=${{ matrix.rels-amount }}&var-runner=$INFRAHUB_BUILD_NAME&from=$TEST_START_TIME&to=$(date +%s)000"' - name: Display server logs if: always() - run: docker logs "${INFRAHUB_BUILD_NAME}-infrahub-server-1" + run: docker logs "${INFRAHUB_BUILD_NAME}-server-1" - name: "Destroy scale environment" if: always() run: "invoke backend.test-scale-env-destroy" diff --git a/.github/workflows/version-upgrade.yml b/.github/workflows/version-upgrade.yml index 8bbb19b194..9268895b25 100644 --- a/.github/workflows/version-upgrade.yml +++ b/.github/workflows/version-upgrade.yml @@ -26,6 +26,7 @@ env: INFRAHUB_LOG_LEVEL: CRITICAL INFRAHUB_IMAGE_NAME: "opsmill/infrahub" INFRAHUB_IMAGE_VER: "local" + INFRAHUB_SERVER_PORT: 0 jobs: migration-tests: @@ -73,11 +74,10 @@ jobs: run: echo INFRAHUB_IMAGE_VER=${{ matrix.source_version }} >> $GITHUB_ENV - name: "Clear docker environment" run: docker compose -p $INFRAHUB_BUILD_NAME down -v --remove-orphans --rmi local - - - name: Select infrahub port - run: echo "INFRAHUB_SERVER_PORT=$(shuf -n 1 -i 10000-30000)" >> $GITHUB_ENV - name: Set INFRAHUB_URL - run: echo "INFRAHUB_URL=http://localhost:${INFRAHUB_SERVER_PORT}" >> $GITHUB_ENV + run: | + PORT=$(docker compose -p $INFRAHUB_BUILD_NAME port infrahub-server 8000 | cut -d: -f2) + echo "INFRAHUB_URL=http://localhost:${PORT}" >> $GITHUB_ENV - name: "Store start time" run: echo TEST_START_TIME=$(date +%s)000 >> $GITHUB_ENV @@ -126,15 +126,15 @@ jobs: - name: Display server logs if: always() - run: docker logs "${INFRAHUB_BUILD_NAME}-infrahub-server-1" + run: docker logs "${INFRAHUB_BUILD_NAME}-server-1" - name: Display git 1 logs if: always() - run: docker logs "${INFRAHUB_BUILD_NAME}-infrahub-git-1" + run: docker logs "${INFRAHUB_BUILD_NAME}-task-worker-1" - name: Display git 2 logs if: always() - run: docker logs "${INFRAHUB_BUILD_NAME}-infrahub-git-2" + run: docker logs "${INFRAHUB_BUILD_NAME}-task-worker-2" - name: Display database logs if: always() diff --git a/.gitignore b/.gitignore index 7f2630f698..0fc6779979 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ coverage.xml *.env script.py **/*.local.* +local/* .vscode/settings.json node_modules/* development/docker-compose.override.yml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 96359c6623..bb94c87dec 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -13,7 +13,7 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.5.0 + rev: v0.6.6 hooks: # Run the linter. - id: ruff diff --git a/.vale/styles/Infrahub/sentence-case.yml b/.vale/styles/Infrahub/sentence-case.yml index 30f2e81512..85f5923388 100644 --- a/.vale/styles/Infrahub/sentence-case.yml +++ b/.vale/styles/Infrahub/sentence-case.yml @@ -52,6 +52,9 @@ exceptions: - Namespace - NATS - Node + - OAuth2 + - OIDC + - Open ID Connect - OpsMill - Pydantic - Python @@ -59,6 +62,7 @@ exceptions: - REST - RFile - SDK + - Single sign-on - TLS - Tony Stark - TransformPython diff --git a/.vale/styles/spelling-exceptions.txt b/.vale/styles/spelling-exceptions.txt index c0fb18b4a6..7034fa9926 100644 --- a/.vale/styles/spelling-exceptions.txt +++ b/.vale/styles/spelling-exceptions.txt @@ -64,6 +64,7 @@ Jotai json JSONSchema kbps +Keycloak Loopbacks markdownlint max_count @@ -100,6 +101,7 @@ toml Towncrier uncheck uniqueness_constraints +userinfo validator upsert Upserting diff --git a/.yamllint.yml b/.yamllint.yml index 50d26e1acb..0e9210947b 100644 --- a/.yamllint.yml +++ b/.yamllint.yml @@ -2,6 +2,7 @@ extends: default ignore: | + /.git /.venv /examples /repositories diff --git a/backend/infrahub/api/__init__.py b/backend/infrahub/api/__init__.py index 84ed4b1a8d..ab1e0e104d 100644 --- a/backend/infrahub/api/__init__.py +++ b/backend/infrahub/api/__init__.py @@ -14,6 +14,8 @@ file, internal, menu, + oauth2, + oidc, query, schema, storage, @@ -29,6 +31,8 @@ router.include_router(file.router) router.include_router(internal.router) router.include_router(menu.router) +router.include_router(oauth2.router) +router.include_router(oidc.router) router.include_router(query.router) router.include_router(schema.router) router.include_router(storage.router) diff --git a/backend/infrahub/api/dependencies.py b/backend/infrahub/api/dependencies.py index ad756e64aa..cb58029c39 100644 --- a/backend/infrahub/api/dependencies.py +++ b/backend/infrahub/api/dependencies.py @@ -85,9 +85,8 @@ async def get_branch_params( at: Optional[str] = Query(None, description="Time to use for the query, in absolute or relative format"), ) -> BranchParams: branch = await registry.get_branch(db=db, branch=branch_name) - at = Timestamp(at) - return BranchParams(branch=branch, at=at) + return BranchParams(branch=branch, at=Timestamp(at)) async def get_branch_dep( diff --git a/backend/infrahub/api/diff/diff.py b/backend/infrahub/api/diff/diff.py index 3deaedb91d..e5ed7b4fed 100644 --- a/backend/infrahub/api/diff/diff.py +++ b/backend/infrahub/api/diff/diff.py @@ -24,7 +24,7 @@ DiffPayloadBuilder, get_display_labels_per_kind, ) -from infrahub.core.schema_manager import INTERNAL_SCHEMA_NODE_KINDS +from infrahub.core.schema.constants import INTERNAL_SCHEMA_NODE_KINDS from infrahub.database import InfrahubDatabase # noqa: TCH001 from .validation_models import DiffQueryValidated @@ -195,7 +195,7 @@ async def get_diff_artifacts( if not target: continue definition_id = node.elements["definition"].peer.new.id - artifacts_in_main[(target.id, definition_id)] = node + artifacts_in_main[target.id, definition_id] = node for node in payload[branch.name]: if "storage_id" not in node.elements or "checksum" not in node.elements: @@ -231,7 +231,7 @@ async def get_diff_artifacts( and (target.id, node.elements["definition"].peer.new.id) in artifacts_in_main ): diff_artifact.action = DiffAction.UPDATED - node_in_main = artifacts_in_main[(target.id, node.elements["definition"].peer.new.id)] + node_in_main = artifacts_in_main[target.id, node.elements["definition"].peer.new.id] diff_artifact.item_previous = BranchDiffArtifactStorage( storage_id=node_in_main.elements["storage_id"].value.value.new, checksum=node_in_main.elements["checksum"].value.value.new, diff --git a/backend/infrahub/api/internal.py b/backend/infrahub/api/internal.py index e6f72a1f13..2618ee4782 100644 --- a/backend/infrahub/api/internal.py +++ b/backend/infrahub/api/internal.py @@ -20,6 +20,7 @@ class ConfigAPI(BaseModel): logging: LoggingSettings analytics: AnalyticsSettings experimental_features: ExperimentalFeaturesSettings + sso: config.SSOInfo class InfoAPI(BaseModel): @@ -34,6 +35,7 @@ async def get_config() -> ConfigAPI: logging=config.SETTINGS.logging, analytics=config.SETTINGS.analytics, experimental_features=config.SETTINGS.experimental_features, + sso=config.SETTINGS.security.public_sso_config, ) diff --git a/backend/infrahub/api/menu.py b/backend/infrahub/api/menu.py index 4f16a06424..e622c61114 100644 --- a/backend/infrahub/api/menu.py +++ b/backend/infrahub/api/menu.py @@ -5,15 +5,21 @@ from fastapi import APIRouter, Depends from pydantic import BaseModel, Field -from infrahub.api.dependencies import get_branch_dep +from infrahub.api.dependencies import get_branch_dep, get_current_user, get_db from infrahub.core import registry from infrahub.core.branch import Branch # noqa: TCH001 from infrahub.core.constants import InfrahubKind +from infrahub.core.protocols import CoreMenuItem from infrahub.core.schema import NodeSchema from infrahub.log import get_logger +from infrahub.menu.generator import generate_menu +from infrahub.menu.models import Menu # noqa: TCH001 if TYPE_CHECKING: + from infrahub.auth import AccountSession from infrahub.core.schema import MainSchemaTypes + from infrahub.database import InfrahubDatabase + log = get_logger() router = APIRouter(prefix="/menu") @@ -198,9 +204,9 @@ async def get_menu(branch: Branch = Depends(get_branch_dep)) -> list[InterfaceMe title="Admin", children=[ InterfaceMenu( - title="Accounts", - path=f"/objects/{InfrahubKind.GENERICACCOUNT}", - icon=_extract_node_icon(full_schema[InfrahubKind.GENERICACCOUNT]), + title="Role Management", + path="/role-management", + icon=_extract_node_icon(full_schema[InfrahubKind.BASEPERMISSION]), ), InterfaceMenu( title="Credentials", @@ -231,3 +237,17 @@ async def get_menu(branch: Branch = Depends(get_branch_dep)) -> list[InterfaceMe menu_items.extend([groups, unified_storage, change_control, deployment, admin]) return menu_items + + +@router.get("/new") +async def get_new_menu( + db: InfrahubDatabase = Depends(get_db), + branch: Branch = Depends(get_branch_dep), + account_session: AccountSession = Depends(get_current_user), +) -> Menu: + log.info("new_menu_request", branch=branch.name) + + menu_items = await registry.manager.query(db=db, schema=CoreMenuItem, branch=branch, prefetch_relationships=True) + menu = await generate_menu(db=db, branch=branch, account=account_session, menu_items=menu_items) + + return menu.to_rest() diff --git a/backend/infrahub/api/oauth2.py b/backend/infrahub/api/oauth2.py new file mode 100644 index 0000000000..a10cf58841 --- /dev/null +++ b/backend/infrahub/api/oauth2.py @@ -0,0 +1,125 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING +from urllib.parse import urljoin + +from authlib.integrations.httpx_client import AsyncOAuth2Client +from fastapi import APIRouter, Depends, Request, Response +from fastapi.responses import JSONResponse, RedirectResponse + +from infrahub import config, models +from infrahub.api.dependencies import get_db +from infrahub.auth import signin_sso_account +from infrahub.exceptions import GatewayError, ProcessingError +from infrahub.log import get_logger +from infrahub.message_bus.types import KVTTL + +if TYPE_CHECKING: + import httpx + + from infrahub.database import InfrahubDatabase + from infrahub.services import InfrahubServices + +log = get_logger() +router = APIRouter(prefix="/oauth2") + + +def _get_redirect_url(request: Request, provider_name: str) -> str: + """This function is mostly to support local development when the frontend runs on different ports compared to the API.""" + base_url = config.SETTINGS.dev.frontend_url or str(request.base_url) + return urljoin(base_url, f"auth/oauth2/{provider_name}/callback") + + +@router.get("/{provider_name:str}/authorize") +async def authorize(request: Request, provider_name: str, final_url: str | None = None) -> Response: + provider = config.SETTINGS.security.get_oauth2_provider(provider=provider_name) + client = AsyncOAuth2Client( + client_id=provider.client_id, + client_secret=provider.client_secret, + scope=provider.scopes, + ) + + redirect_uri = _get_redirect_url(request=request, provider_name=provider_name) + final_url = final_url or config.SETTINGS.dev.frontend_url or str(request.base_url) + + authorization_uri, state = client.create_authorization_url( + url=provider.authorization_url, redirect_uri=redirect_uri, scope=provider.scopes, final_url=final_url + ) + + service: InfrahubServices = request.app.state.service + + await service.cache.set( + key=f"security:oauth2:provider:{provider_name}:state:{state}", value=final_url, expires=KVTTL.TWO_HOURS + ) + + if config.SETTINGS.dev.frontend_redirect_sso: + return JSONResponse(content={"url": authorization_uri}) + + return RedirectResponse(url=authorization_uri) + + +@router.get("/{provider_name:str}/token") +async def token( + request: Request, + response: Response, + provider_name: str, + state: str, + code: str, + db: InfrahubDatabase = Depends(get_db), +) -> models.UserTokenWithUrl: + provider = config.SETTINGS.security.get_oauth2_provider(provider=provider_name) + + service: InfrahubServices = request.app.state.service + + cache_key = f"security:oauth2:provider:{provider_name}:state:{state}" + stored_final_url = await service.cache.get(key=cache_key) + await service.cache.delete(key=cache_key) + + if not stored_final_url: + raise ProcessingError(message="Invalid 'state' parameter") + + token_data = { + "code": code, + "client_id": provider.client_id, + "client_secret": provider.client_secret, + "redirect_uri": _get_redirect_url(request=request, provider_name=provider_name), + "grant_type": "authorization_code", + } + + token_response = await service.http.post(provider.token_url, data=token_data) + _validate_response(response=token_response) + payload = token_response.json() + + headers = {"Authorization": f"{payload.get('token_type')} {payload.get('access_token')}"} + userinfo_response = await service.http.post(provider.userinfo_url, headers=headers) + _validate_response(response=userinfo_response) + user_info = userinfo_response.json() + sso_groups = user_info.get("groups", []) + user_token = await signin_sso_account(db=db, account_name=user_info["name"], sso_groups=sso_groups) + + response.set_cookie( + "access_token", user_token.access_token, httponly=True, max_age=config.SETTINGS.security.access_token_lifetime + ) + response.set_cookie( + "refresh_token", + user_token.refresh_token, + httponly=True, + max_age=config.SETTINGS.security.refresh_token_lifetime, + ) + + return models.UserTokenWithUrl( + access_token=user_token.access_token, refresh_token=user_token.refresh_token, final_url=stored_final_url + ) + + +def _validate_response(response: httpx.Response) -> None: + if 200 <= response.status_code <= 299: + return + + log.error( + "Invalid response from the OAuth provider", + url=response.url, + status_code=response.status_code, + body=response.json(), + ) + raise GatewayError(message="Invalid response from Authentication provider") diff --git a/backend/infrahub/api/oidc.py b/backend/infrahub/api/oidc.py new file mode 100644 index 0000000000..ffc150e7e1 --- /dev/null +++ b/backend/infrahub/api/oidc.py @@ -0,0 +1,164 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING +from urllib.parse import urljoin + +from authlib.integrations.httpx_client import AsyncOAuth2Client +from fastapi import APIRouter, Depends, Request, Response +from fastapi.responses import JSONResponse, RedirectResponse +from pydantic import BaseModel, HttpUrl + +from infrahub import config, models +from infrahub.api.dependencies import get_db +from infrahub.auth import signin_sso_account +from infrahub.exceptions import GatewayError, ProcessingError +from infrahub.log import get_logger +from infrahub.message_bus.types import KVTTL + +if TYPE_CHECKING: + import httpx + + from infrahub.database import InfrahubDatabase + from infrahub.services import InfrahubServices + +log = get_logger() +router = APIRouter(prefix="/oidc") + + +class OIDCDiscoveryConfig(BaseModel): + issuer: HttpUrl + authorization_endpoint: HttpUrl + token_endpoint: HttpUrl + userinfo_endpoint: HttpUrl + jwks_uri: HttpUrl + revocation_endpoint: HttpUrl | None = None + registration_endpoint: HttpUrl | None = None + introspection_endpoint: HttpUrl | None = None + end_session_endpoint: HttpUrl | None = None + frontchannel_logout_supported: bool | None = None + frontchannel_logout_session_supported: bool | None = None + grant_types_supported: list[str] | None = None + response_types_supported: list[str] + subject_types_supported: list[str] + id_token_signing_alg_values_supported: list[str] + scopes_supported: list[str] | None = None + token_endpoint_auth_methods_supported: list[str] | None = None + claims_supported: list[str] | None = None + acr_values_supported: list[str] | None = None + request_parameter_supported: bool | None = None + request_uri_parameter_supported: bool | None = None + require_request_uri_registration: bool | None = None + code_challenge_methods_supported: list[str] | None = None + tls_client_certificate_bound_access_tokens: bool | None = None + mtls_endpoint_aliases: dict[str, HttpUrl] | None = None + + +def _get_redirect_url(request: Request, provider_name: str) -> str: + """This function is mostly to support local development when the frontend runs on different ports compared to the API.""" + base_url = config.SETTINGS.dev.frontend_url or str(request.base_url) + return urljoin(base_url, f"auth/oidc/{provider_name}/callback") + + +@router.get("/{provider_name:str}/authorize") +async def authorize(request: Request, provider_name: str, final_url: str | None = None) -> Response: + provider = config.SETTINGS.security.get_oidc_provider(provider=provider_name) + service: InfrahubServices = request.app.state.service + + response = await service.http.get(url=provider.discovery_url) + _validate_response(response=response) + oidc_config = OIDCDiscoveryConfig(**response.json()) + + client = AsyncOAuth2Client( + client_id=provider.client_id, + client_secret=provider.client_secret, + scope=provider.scopes, + ) + + redirect_uri = _get_redirect_url(request=request, provider_name=provider_name) + final_url = final_url or config.SETTINGS.dev.frontend_url or str(request.base_url) + + authorization_uri, state = client.create_authorization_url( + url=str(oidc_config.authorization_endpoint), redirect_uri=redirect_uri, scope=provider.scopes + ) + + await service.cache.set( + key=f"security:oidc:provider:{provider_name}:state:{state}", value=final_url, expires=KVTTL.TWO_HOURS + ) + + if config.SETTINGS.dev.frontend_redirect_sso: + return JSONResponse(content={"url": authorization_uri}) + + return RedirectResponse(url=authorization_uri) + + +@router.get("/{provider_name:str}/token") +async def token( + request: Request, + response: Response, + provider_name: str, + state: str, + code: str, + db: InfrahubDatabase = Depends(get_db), +) -> models.UserTokenWithUrl: + provider = config.SETTINGS.security.get_oidc_provider(provider=provider_name) + + service: InfrahubServices = request.app.state.service + + cache_key = f"security:oidc:provider:{provider_name}:state:{state}" + stored_final_url = await service.cache.get(key=cache_key) + await service.cache.delete(key=cache_key) + + if not stored_final_url: + raise ProcessingError(message="Invalid 'state' parameter") + + token_data = { + "code": code, + "client_id": provider.client_id, + "client_secret": provider.client_secret, + "redirect_uri": _get_redirect_url(request=request, provider_name=provider_name), + "grant_type": "authorization_code", + } + + discovery_response = await service.http.get(url=provider.discovery_url) + _validate_response(response=discovery_response) + + oidc_config = OIDCDiscoveryConfig(**discovery_response.json()) + + token_response = await service.http.post(str(oidc_config.token_endpoint), data=token_data) + _validate_response(response=token_response) + payload = token_response.json() + + headers = {"Authorization": f"{payload.get('token_type')} {payload.get('access_token')}"} + userinfo_response = await service.http.post(str(oidc_config.userinfo_endpoint), headers=headers) + _validate_response(response=userinfo_response) + user_info = userinfo_response.json() + + sso_groups = user_info.get("groups", []) + user_token = await signin_sso_account(db=db, account_name=user_info["name"], sso_groups=sso_groups) + + response.set_cookie( + "access_token", user_token.access_token, httponly=True, max_age=config.SETTINGS.security.access_token_lifetime + ) + response.set_cookie( + "refresh_token", + user_token.refresh_token, + httponly=True, + max_age=config.SETTINGS.security.refresh_token_lifetime, + ) + + return models.UserTokenWithUrl( + access_token=user_token.access_token, refresh_token=user_token.refresh_token, final_url=stored_final_url + ) + + +def _validate_response(response: httpx.Response) -> None: + if 200 <= response.status_code <= 299: + return + + log.error( + "Invalid response from the OIDC provider", + url=response.url, + status_code=response.status_code, + body=response.json(), + ) + raise GatewayError(message="Invalid response from Authentication provider") diff --git a/backend/infrahub/api/query.py b/backend/infrahub/api/query.py index f96810b758..c7780c9f8a 100644 --- a/backend/infrahub/api/query.py +++ b/backend/infrahub/api/query.py @@ -10,8 +10,9 @@ from infrahub.core import registry from infrahub.core.constants import InfrahubKind from infrahub.database import InfrahubDatabase # noqa: TCH001 -from infrahub.graphql import prepare_graphql_params from infrahub.graphql.analyzer import InfrahubGraphQLQueryAnalyzer +from infrahub.graphql.api.dependencies import build_graphql_query_permission_checker +from infrahub.graphql.initialization import prepare_graphql_params from infrahub.graphql.metrics import ( GRAPHQL_DURATION_METRICS, GRAPHQL_QUERY_DEPTH_METRICS, @@ -26,8 +27,11 @@ from infrahub.message_bus import messages if TYPE_CHECKING: + from infrahub.auth import AccountSession + from infrahub.graphql.auth.query_permission_checker.checker import GraphQLQueryPermissionChecker from infrahub.services import InfrahubServices + log = get_logger() router = APIRouter(prefix="/query") @@ -44,17 +48,28 @@ async def execute_query( params: dict[str, str], update_group: bool, subscribers: list[str], + permission_checker: GraphQLQueryPermissionChecker, + account_session: AccountSession, ) -> dict[str, Any]: gql_query = await registry.manager.get_one_by_id_or_default_filter( db=db, id=query_id, kind=InfrahubKind.GRAPHQLQUERY, branch=branch_params.branch, at=branch_params.at ) - gql_params = prepare_graphql_params(db=db, branch=branch_params.branch, at=branch_params.at) + gql_params = prepare_graphql_params( + db=db, branch=branch_params.branch, at=branch_params.at, account_session=account_session + ) analyzed_query = InfrahubGraphQLQueryAnalyzer( query=gql_query.query.value, # type: ignore[attr-defined] schema=gql_params.schema, branch=branch_params.branch, ) + await permission_checker.check( + db=db, + account_session=account_session, + analyzed_query=analyzed_query, + query_parameters=gql_params, + branch=branch_params.branch, + ) labels = { "type": "mutation" if analyzed_query.contains_mutation else "query", @@ -120,7 +135,8 @@ async def graphql_query_post( ), db: InfrahubDatabase = Depends(get_db), branch_params: BranchParams = Depends(get_branch_params), - _: str = Depends(get_current_user), + account_session: AccountSession = Depends(get_current_user), + permission_checker: GraphQLQueryPermissionChecker = Depends(build_graphql_query_permission_checker), ) -> dict: return await execute_query( db=db, @@ -130,6 +146,8 @@ async def graphql_query_post( params=payload.variables, update_group=update_group, subscribers=subscribers, + permission_checker=permission_checker, + account_session=account_session, ) @@ -146,7 +164,8 @@ async def graphql_query_get( ), db: InfrahubDatabase = Depends(get_db), branch_params: BranchParams = Depends(get_branch_params), - _: str = Depends(get_current_user), + account_session: AccountSession = Depends(get_current_user), + permission_checker: GraphQLQueryPermissionChecker = Depends(build_graphql_query_permission_checker), ) -> dict: params = { key: value @@ -162,4 +181,6 @@ async def graphql_query_get( params=params, update_group=update_group, subscribers=subscribers, + permission_checker=permission_checker, + account_session=account_session, ) diff --git a/backend/infrahub/api/schema.py b/backend/infrahub/api/schema.py index 5c8eafb7b3..e852eb0acd 100644 --- a/backend/infrahub/api/schema.py +++ b/backend/infrahub/api/schema.py @@ -17,11 +17,15 @@ from infrahub.api.exceptions import SchemaNotValidError from infrahub.core import registry from infrahub.core.branch import Branch # noqa: TCH001 -from infrahub.core.migrations.schema.runner import schema_migrations_runner -from infrahub.core.models import SchemaBranchHash, SchemaDiff # noqa: TCH001 +from infrahub.core.migrations.schema.models import SchemaApplyMigrationData +from infrahub.core.models import ( # noqa: TCH001 + SchemaBranchHash, + SchemaDiff, + SchemaUpdateValidationResult, +) from infrahub.core.schema import GenericSchema, MainSchemaTypes, NodeSchema, ProfileSchema, SchemaRoot -from infrahub.core.schema_manager import SchemaBranch, SchemaNamespace, SchemaUpdateValidationResult # noqa: TCH001 -from infrahub.core.validators.checker import schema_validators_checker +from infrahub.core.schema.constants import SchemaNamespace # noqa: TCH001 +from infrahub.core.validators.models.validate_migration import SchemaValidateMigrationData from infrahub.database import InfrahubDatabase # noqa: TCH001 from infrahub.exceptions import MigrationError from infrahub.log import get_logger @@ -29,10 +33,12 @@ from infrahub.services import services from infrahub.types import ATTRIBUTE_PYTHON_TYPES from infrahub.worker import WORKER_IDENTITY +from infrahub.workflows.catalogue import SCHEMA_APPLY_MIGRATION, SCHEMA_VALIDATE_MIGRATION if TYPE_CHECKING: from typing_extensions import Self + from infrahub.core.schema.schema_branch import SchemaBranch from infrahub.services import InfrahubServices @@ -258,11 +264,16 @@ async def load_schema( # ---------------------------------------------------------- # Validate if the new schema is valid with the content of the database # ---------------------------------------------------------- - error_messages, _ = await schema_validators_checker( - branch=branch, schema=candidate_schema, constraints=result.constraints, service=service + validate_migration_data = SchemaValidateMigrationData( + branch=branch, + schema_branch=candidate_schema, + constraints=result.constraints, + ) + error_messages = await service.workflow.execute( # type: ignore[var-annotated] + workflow=SCHEMA_VALIDATE_MIGRATION, message=validate_migration_data ) - if error_messages: - raise SchemaNotValidError(message=",\n".join(error_messages)) + if error_messages: # type: ignore[has-type] + raise SchemaNotValidError(message=",\n".join(error_messages)) # type: ignore[has-type] # ---------------------------------------------------------- # Update the schema @@ -293,15 +304,18 @@ async def load_schema( # ---------------------------------------------------------- # Run the migrations # ---------------------------------------------------------- - error_messages = await schema_migrations_runner( + apply_migration_data = SchemaApplyMigrationData( branch=branch, new_schema=candidate_schema, previous_schema=origin_schema, migrations=result.migrations, - service=service, ) - if error_messages: - raise MigrationError(message=",\n".join(error_messages)) + migration_error_msgs = await service.workflow.execute( # type: ignore[var-annotated] + workflow=SCHEMA_APPLY_MIGRATION, message=apply_migration_data + ) + + if migration_error_msgs: + raise MigrationError(message=",\n".join(migration_error_msgs)) if config.SETTINGS.broker.enable: message = messages.EventSchemaUpdate( @@ -339,8 +353,13 @@ async def check_schema( # ---------------------------------------------------------- # Validate if the new schema is valid with the content of the database # ---------------------------------------------------------- - error_messages, _ = await schema_validators_checker( - branch=branch, schema=candidate_schema, constraints=result.constraints, service=service + validate_migration_data = SchemaValidateMigrationData( + branch=branch, + schema_branch=candidate_schema, + constraints=result.constraints, + ) + error_messages = await service.workflow.execute( # type: ignore[var-annotated] + workflow=SCHEMA_VALIDATE_MIGRATION, message=validate_migration_data ) if error_messages: raise SchemaNotValidError(message=",\n".join(error_messages)) diff --git a/backend/infrahub/api/transformation.py b/backend/infrahub/api/transformation.py index 153386df32..d4b620b420 100644 --- a/backend/infrahub/api/transformation.py +++ b/backend/infrahub/api/transformation.py @@ -21,14 +21,14 @@ ) from infrahub.database import InfrahubDatabase # noqa: TCH001 from infrahub.exceptions import TransformError -from infrahub.graphql import prepare_graphql_params +from infrahub.graphql.initialization import prepare_graphql_params from infrahub.graphql.utils import extract_data from infrahub.message_bus.messages import ( - TransformJinjaTemplate, - TransformJinjaTemplateResponse, TransformPythonData, TransformPythonDataResponse, ) +from infrahub.message_bus.messages.transform_jinja_template import TransformJinjaTemplateData +from infrahub.workflows.catalogue import TRANSFORM_JINJA2_RENDER if TYPE_CHECKING: from infrahub.services import InfrahubServices @@ -134,9 +134,7 @@ async def transform_jinja2( data = extract_data(query_name=query.name.value, result=result) - service: InfrahubServices = request.app.state.service - - message = TransformJinjaTemplate( + message = TransformJinjaTemplateData( repository_id=repository.id, repository_name=repository.name.value, repository_kind=repository.get_kind(), @@ -146,5 +144,8 @@ async def transform_jinja2( data=data, ) - response = await service.message_bus.rpc(message=message, response_class=TransformJinjaTemplateResponse) - return PlainTextResponse(content=response.data.rendered_template) + service: InfrahubServices = request.app.state.service + + response: str = await service.workflow.execute(workflow=TRANSFORM_JINJA2_RENDER, message=message) # type: ignore[arg-type] + + return PlainTextResponse(content=response) diff --git a/backend/infrahub/auth.py b/backend/infrahub/auth.py index 861f98767d..cda103eb07 100644 --- a/backend/infrahub/auth.py +++ b/backend/infrahub/auth.py @@ -14,6 +14,7 @@ from infrahub.core.constants import AccountStatus, InfrahubKind from infrahub.core.manager import NodeManager from infrahub.core.node import Node +from infrahub.core.protocols import CoreAccount, CoreAccountGroup from infrahub.core.registry import registry from infrahub.exceptions import AuthorizationError, NodeNotFoundError @@ -123,6 +124,35 @@ async def create_fresh_access_token( return models.AccessTokenResponse(access_token=access_token) +async def signin_sso_account(db: InfrahubDatabase, account_name: str, sso_groups: list[str]) -> models.UserToken: + account = await NodeManager.get_one_by_default_filter(db=db, id=account_name, kind=InfrahubKind.ACCOUNT) + + if not account: + account = await Node.init(db=db, schema=InfrahubKind.ACCOUNT) + await account.new(db=db, name=account_name, account_type="User", role="admin", password=str(uuid.uuid4())) + await account.save(db=db) + + if sso_groups: + infrahub_groups = await NodeManager.query( + db=db, + schema=CoreAccountGroup, + filters={"name__values": sso_groups}, + prefetch_relationships=True, + ) + for group in infrahub_groups: + members = await group.members.get_peers(db=db, branch_agnostic=True, peer_type=CoreAccount) + if account.id not in members: + await group.members.add(db=db, data=account) + await group.members.save(db=db) + + now = datetime.now(tz=timezone.utc) + refresh_expires = now + timedelta(seconds=config.SETTINGS.security.refresh_token_lifetime) + session_id = await create_db_refresh_token(db=db, account_id=account.id, expiration=refresh_expires) + access_token = generate_access_token(account_id=account.id, role=account.role.value.value, session_id=session_id) + refresh_token = generate_refresh_token(account_id=account.id, session_id=session_id, expiration=refresh_expires) + return models.UserToken(access_token=access_token, refresh_token=refresh_token) + + def generate_access_token(account_id: str, role: str, session_id: uuid.UUID) -> str: now = datetime.now(tz=timezone.utc) diff --git a/backend/infrahub/cli/__init__.py b/backend/infrahub/cli/__init__.py index f6d042be32..5320e89803 100644 --- a/backend/infrahub/cli/__init__.py +++ b/backend/infrahub/cli/__init__.py @@ -8,6 +8,7 @@ from infrahub.cli.events import app as events_app from infrahub.cli.git_agent import app as git_app from infrahub.cli.server import app as server_app +from infrahub.cli.tasks import app as tasks_app from infrahub.core.initialization import initialization from infrahub.database import InfrahubDatabase, get_db @@ -26,6 +27,7 @@ def common(ctx: typer.Context) -> None: app.add_typer(git_app, name="git-agent") app.add_typer(db_app, name="db") app.add_typer(events_app, name="events", help="Interact with the events system.") +app.add_typer(tasks_app, name="tasks", hidden=True) async def _init_shell(config_file: str) -> None: diff --git a/backend/infrahub/cli/db.py b/backend/infrahub/cli/db.py index 1fbaebc2a1..80271a4e07 100644 --- a/backend/infrahub/cli/db.py +++ b/backend/infrahub/cli/db.py @@ -1,10 +1,12 @@ import importlib import logging +import os from enum import Enum from typing import TYPE_CHECKING, Optional import typer from infrahub_sdk.async_typer import AsyncTyper +from prefect.testing.utilities import prefect_test_harness from rich import print as rprint from rich.console import Console from rich.logging import RichHandler @@ -18,16 +20,18 @@ from infrahub.core.graph.schema import GRAPH_SCHEMA from infrahub.core.initialization import first_time_initialization, get_root_node, initialization, initialize_registry from infrahub.core.migrations.graph import get_graph_migrations -from infrahub.core.migrations.schema.runner import schema_migrations_runner +from infrahub.core.migrations.schema.models import SchemaApplyMigrationData from infrahub.core.schema import SchemaRoot, core_models, internal_schema from infrahub.core.schema.definitions.deprecated import deprecated_models -from infrahub.core.schema_manager import SchemaManager +from infrahub.core.schema.manager import SchemaManager from infrahub.core.utils import delete_all_nodes -from infrahub.core.validators.checker import schema_validators_checker +from infrahub.core.validators.models.validate_migration import SchemaValidateMigrationData from infrahub.database import DatabaseType from infrahub.log import get_logger from infrahub.services import InfrahubServices from infrahub.services.adapters.message_bus.local import BusSimulator +from infrahub.services.adapters.workflow.local import WorkflowLocalExecution +from infrahub.workflows.catalogue import SCHEMA_APPLY_MIGRATION, SCHEMA_VALIDATE_MIGRATION if TYPE_CHECKING: from infrahub.cli.context import CliContext @@ -177,6 +181,9 @@ async def update_core_schema( # pylint: disable=too-many-statements """Check the current format of the internal graph and apply the necessary migrations""" logging.getLogger("infrahub").setLevel(logging.WARNING) logging.getLogger("neo4j").setLevel(logging.ERROR) + logging.getLogger("prefect").setLevel(logging.ERROR) + os.environ["PREFECT_SERVER_ANALYTICS_ENABLED"] = "false" + config.load_and_exit(config_file_name=config_file) context: CliContext = ctx.obj @@ -185,98 +192,109 @@ async def update_core_schema( # pylint: disable=too-many-statements error_badge = "[bold red]ERROR[/bold red]" async with dbdriver.start_session() as db: - # ---------------------------------------------------------- - # Initialize Schema and Registry - # ---------------------------------------------------------- - service = InfrahubServices(database=db, message_bus=BusSimulator(database=db)) - await initialize_registry(db=db) - - default_branch = registry.get_branch_from_registry(branch=registry.default_branch) - - registry.schema = SchemaManager() - schema = SchemaRoot(**internal_schema) - registry.schema.register_schema(schema=schema) - - # ---------------------------------------------------------- - # Load Current Schema from the database - # ---------------------------------------------------------- - schema_default_branch = await registry.schema.load_schema_from_db(db=db, branch=default_branch) - registry.schema.set_schema_branch(name=default_branch.name, schema=schema_default_branch) - branch_schema = registry.schema.get_schema_branch(name=registry.default_branch) - - candidate_schema = branch_schema.duplicate() - candidate_schema.load_schema(schema=SchemaRoot(**internal_schema)) - candidate_schema.load_schema(schema=SchemaRoot(**core_models)) - candidate_schema.load_schema(schema=SchemaRoot(**deprecated_models)) - candidate_schema.process() - - result = branch_schema.validate_update(other=candidate_schema, enforce_update_support=False) - if result.errors: - rprint(f"{error_badge} | Unable to update the schema, due to failed validations") - for error in result.errors: - rprint(error.to_string()) - raise typer.Exit(1) - - if not result.diff.all: - rprint("Core Schema Up to date, nothing to update") - raise typer.Exit(0) - - rprint("Core Schema has diff, will need to be updated") - if debug: - result.diff.print() - - # ---------------------------------------------------------- - # Validate if the new schema is valid with the content of the database - # ---------------------------------------------------------- - error_messages, _ = await schema_validators_checker( - branch=default_branch, schema=candidate_schema, constraints=result.constraints, service=service - ) - if error_messages: - rprint(f"{error_badge} | Unable to update the schema, due to failed validations") - for message in error_messages: - rprint(message) - raise typer.Exit(1) - - # ---------------------------------------------------------- - # Update the schema - # ---------------------------------------------------------- - origin_schema = branch_schema.duplicate() - - # Update the internal schema - schema_default_branch.load_schema(schema=SchemaRoot(**internal_schema)) - schema_default_branch.process() - registry.schema.set_schema_branch(name=default_branch.name, schema=schema_default_branch) - - async with db.start_transaction() as dbt: - await registry.schema.update_schema_branch( - schema=candidate_schema, - db=dbt, - branch=default_branch.name, - diff=result.diff, - limit=result.diff.all, - update_db=True, + with prefect_test_harness(): + # ---------------------------------------------------------- + # Initialize Schema and Registry + # ---------------------------------------------------------- + service = InfrahubServices( + database=db, message_bus=BusSimulator(database=db), workflow=WorkflowLocalExecution() ) - default_branch.update_schema_hash() - rprint("The Core Schema has been updated") + await initialize_registry(db=db) + + default_branch = registry.get_branch_from_registry(branch=registry.default_branch) + + registry.schema = SchemaManager() + schema = SchemaRoot(**internal_schema) + registry.schema.register_schema(schema=schema) + + # ---------------------------------------------------------- + # Load Current Schema from the database + # ---------------------------------------------------------- + schema_default_branch = await registry.schema.load_schema_from_db(db=db, branch=default_branch) + registry.schema.set_schema_branch(name=default_branch.name, schema=schema_default_branch) + branch_schema = registry.schema.get_schema_branch(name=registry.default_branch) + + candidate_schema = branch_schema.duplicate() + candidate_schema.load_schema(schema=SchemaRoot(**internal_schema)) + candidate_schema.load_schema(schema=SchemaRoot(**core_models)) + candidate_schema.load_schema(schema=SchemaRoot(**deprecated_models)) + candidate_schema.process() + + result = branch_schema.validate_update(other=candidate_schema, enforce_update_support=False) + if result.errors: + rprint(f"{error_badge} | Unable to update the schema, due to failed validations") + for error in result.errors: + rprint(error.to_string()) + raise typer.Exit(1) + + if not result.diff.all: + rprint("Core Schema Up to date, nothing to update") + raise typer.Exit(0) + + rprint("Core Schema has diff, will need to be updated") if debug: - rprint(f"New schema hash: {default_branch.active_schema_hash.main}") - await default_branch.save(db=dbt) - - # ---------------------------------------------------------- - # Run the migrations - # ---------------------------------------------------------- - error_messages = await schema_migrations_runner( - branch=default_branch, - new_schema=candidate_schema, - previous_schema=origin_schema, - migrations=result.migrations, - service=service, - ) - if error_messages: - rprint(f"{error_badge} | Some error(s) happened while running the schema migrations") - for message in error_messages: - rprint(message) - raise typer.Exit(1) + result.diff.print() + + # ---------------------------------------------------------- + # Validate if the new schema is valid with the content of the database + # ---------------------------------------------------------- + validate_migration_data = SchemaValidateMigrationData( + branch=default_branch, + schema_branch=candidate_schema, + constraints=result.constraints, + ) + error_messages = await service.workflow.execute( # type: ignore[var-annotated] + workflow=SCHEMA_VALIDATE_MIGRATION, message=validate_migration_data + ) + if error_messages: + rprint(f"{error_badge} | Unable to update the schema, due to failed validations") + for message in error_messages: + rprint(message) + raise typer.Exit(1) + + # ---------------------------------------------------------- + # Update the schema + # ---------------------------------------------------------- + origin_schema = branch_schema.duplicate() + + # Update the internal schema + schema_default_branch.load_schema(schema=SchemaRoot(**internal_schema)) + schema_default_branch.process() + registry.schema.set_schema_branch(name=default_branch.name, schema=schema_default_branch) + + async with db.start_transaction() as dbt: + await registry.schema.update_schema_branch( + schema=candidate_schema, + db=dbt, + branch=default_branch.name, + diff=result.diff, + limit=result.diff.all, + update_db=True, + ) + default_branch.update_schema_hash() + rprint("The Core Schema has been updated") + if debug: + rprint(f"New schema hash: {default_branch.active_schema_hash.main}") + await default_branch.save(db=dbt) + + # ---------------------------------------------------------- + # Run the migrations + # ---------------------------------------------------------- + apply_migration_data = SchemaApplyMigrationData( + branch=default_branch, + new_schema=candidate_schema, + previous_schema=origin_schema, + migrations=result.migrations, + ) + migration_error_msgs = await service.workflow.execute( # type: ignore[var-annotated] + workflow=SCHEMA_APPLY_MIGRATION, message=apply_migration_data + ) + + if migration_error_msgs: + rprint(f"{error_badge} | Some error(s) happened while running the schema migrations") + for message in migration_error_msgs: + rprint(message) + raise typer.Exit(1) @app.command() diff --git a/backend/infrahub/cli/git_agent.py b/backend/infrahub/cli/git_agent.py index ef163db558..3f0e20d319 100644 --- a/backend/infrahub/cli/git_agent.py +++ b/backend/infrahub/cli/git_agent.py @@ -23,6 +23,8 @@ from infrahub.services.adapters.cache.redis import RedisCache from infrahub.services.adapters.message_bus.nats import NATSMessageBus from infrahub.services.adapters.message_bus.rabbitmq import RabbitMQMessageBus +from infrahub.services.adapters.workflow.local import WorkflowLocalExecution +from infrahub.services.adapters.workflow.worker import WorkflowWorkerExecution from infrahub.trace import configure_trace if TYPE_CHECKING: @@ -69,6 +71,7 @@ async def start( logging.getLogger("aio_pika").setLevel(logging.ERROR) logging.getLogger("aiormq").setLevel(logging.ERROR) logging.getLogger("git").setLevel(logging.ERROR) + logging.getLogger("aiosqlite").setLevel(logging.ERROR) log.debug(f"Config file : {config_file}") # Prevent git from interactively prompting the user for passwords if the credentials provided @@ -110,6 +113,12 @@ async def start( database = await context.get_db(retry=1) + workflow = config.OVERRIDE.workflow or ( + WorkflowWorkerExecution() + if config.SETTINGS.workflow.driver == config.WorkflowDriver.WORKER + else WorkflowLocalExecution() + ) + message_bus = config.OVERRIDE.message_bus or ( NATSMessageBus() if config.SETTINGS.broker.driver == config.BrokerDriver.NATS else RabbitMQMessageBus() ) @@ -121,6 +130,7 @@ async def start( cache=cache, client=client, database=database, + workflow=workflow, message_bus=message_bus, component_type=ComponentType.GIT_AGENT, ) diff --git a/backend/infrahub/cli/tasks.py b/backend/infrahub/cli/tasks.py new file mode 100644 index 0000000000..ffcff0bae4 --- /dev/null +++ b/backend/infrahub/cli/tasks.py @@ -0,0 +1,50 @@ +import logging + +import typer +from infrahub_sdk.async_typer import AsyncTyper +from prefect.client.orchestration import get_client + +from infrahub import config +from infrahub.services.adapters.workflow.worker import WorkflowWorkerExecution +from infrahub.tasks.dummy import DUMMY_FLOW, DummyInput +from infrahub.workflows.initialization import setup_task_manager +from infrahub.workflows.models import WorkerPoolDefinition + +app = AsyncTyper() + +# pylint: disable=unused-argument + + +@app.command() +async def init( + ctx: typer.Context, + debug: bool = typer.Option(False, help="Enable advanced logging and troubleshooting"), + config_file: str = typer.Argument("infrahub.toml", envvar="INFRAHUB_CONFIG"), +) -> None: + """Initialize the task manager""" + logging.getLogger("prefect").setLevel(logging.ERROR) + + config.load_and_exit(config_file_name=config_file) + + await setup_task_manager() + + +@app.command() +async def execute( + ctx: typer.Context, + debug: bool = typer.Option(False, help="Enable advanced logging and troubleshooting"), + config_file: str = typer.Argument("infrahub.toml", envvar="INFRAHUB_CONFIG"), +) -> None: + """Check the current format of the internal graph and apply the necessary migrations""" + logging.getLogger("infrahub").setLevel(logging.WARNING) + logging.getLogger("neo4j").setLevel(logging.ERROR) + logging.getLogger("prefect").setLevel(logging.ERROR) + + config.load_and_exit(config_file_name=config_file) + + async with get_client(sync_client=False) as client: + worker = WorkflowWorkerExecution() + await DUMMY_FLOW.save(client=client, work_pool=WorkerPoolDefinition(name="testing", worker_type="process")) + + result = await worker.execute(workflow=DUMMY_FLOW, data=DummyInput(firstname="John", lastname="Doe")) # type: ignore[var-annotated] + print(result) diff --git a/backend/infrahub/config.py b/backend/infrahub/config.py index e5d21bb3f1..64b67802ef 100644 --- a/backend/infrahub/config.py +++ b/backend/infrahub/config.py @@ -2,6 +2,7 @@ import os import os.path +import ssl import sys from dataclasses import dataclass from enum import Enum @@ -10,16 +11,17 @@ import toml from infrahub_sdk import generate_uuid -from pydantic import AliasChoices, Field, ValidationError, model_validator +from pydantic import AliasChoices, BaseModel, Field, PrivateAttr, ValidationError, computed_field, model_validator from pydantic_settings import BaseSettings, SettingsConfigDict from typing_extensions import Self from infrahub.database.constants import DatabaseType -from infrahub.exceptions import InitializationError +from infrahub.exceptions import InitializationError, ProcessingError if TYPE_CHECKING: from infrahub.services.adapters.cache import InfrahubCache from infrahub.services.adapters.message_bus import InfrahubMessageBus + from infrahub.services.adapters.workflow import InfrahubWorkflow VALID_DATABASE_NAME_REGEX = r"^[a-z][a-z0-9\.]+$" @@ -34,6 +36,46 @@ def default_cors_allow_headers() -> list[str]: return ["accept", "authorization", "content-type", "user-agent", "x-csrftoken", "x-requested-with"] +class SSOProtocol(str, Enum): + OAUTH2 = "oauth2" + OIDC = "oidc" + + +class Oauth2Provider(str, Enum): + GOOGLE = "google" + PROVIDER1 = "provider1" + PROVIDER2 = "provider2" + + +class OIDCProvider(str, Enum): + GOOGLE = "google" + PROVIDER1 = "provider1" + PROVIDER2 = "provider2" + + +class SSOInfo(BaseModel): + providers: list[SSOProviderInfo] = Field(default_factory=list) + + @computed_field + def enabled(self) -> bool: + return bool(self.providers) + + +class SSOProviderInfo(BaseModel): + name: str + display_label: str + icon: str + protocol: SSOProtocol + + @computed_field + def authorize_path(self) -> str: + return f"/api/{self.protocol.value}/{self.name}/authorize" + + @computed_field + def token_path(self) -> str: + return f"/api/{self.protocol.value}/{self.name}/token" + + class StorageDriver(str, Enum): FileSystemStorage = "local" InfrahubS3ObjectStorage = "s3" @@ -62,6 +104,11 @@ class CacheDriver(str, Enum): NATS = "nats" +class WorkflowDriver(str, Enum): + LOCAL = "local" + WORKER = "worker" + + class MainSettings(BaseSettings): model_config = SettingsConfigDict(env_prefix="INFRAHUB_") docs_index_path: str = Field( @@ -77,6 +124,10 @@ class MainSettings(BaseSettings): telemetry_interval: int = Field( default=3600 * 24, ge=60, description="Time (in seconds) between telemetry usage push" ) + permission_backends: list[str] = Field( + default=["infrahub.permissions.LocalPermissionBackend"], + description="List of modules to handle permissions, they will be run in the given order", + ) class FileSystemStorageSettings(BaseSettings): @@ -165,6 +216,21 @@ def database_name(self) -> str: return self.database or self.db_type.value +class DevelopmentSettings(BaseSettings): + """The development settings are only relevant for local development""" + + model_config = SettingsConfigDict(env_prefix="INFRAHUB_DEV_") + + frontend_url: Optional[str] = Field( + default=None, + description="Define the URL of the frontend, useful for OAuth2 development when the frontend and backend use different ports.", + ) + frontend_redirect_sso: bool = Field( + default=False, + description="Indicates of the frontend should be responsible for the SSO redirection", + ) + + class BrokerSettings(BaseSettings): model_config = SettingsConfigDict(env_prefix="INFRAHUB_BROKER_") enable: bool = True @@ -175,6 +241,7 @@ class BrokerSettings(BaseSettings): password: str = "infrahub" address: str = "localhost" port: Optional[int] = Field(default=None, ge=1, le=65535, description="Specified if running on a non default port.") + rabbitmq_http_port: Optional[int] = Field(default=None, ge=1, le=65535) namespace: str = "infrahub" maximum_message_retries: int = Field( default=10, description="The maximum number of retries that are attempted for failed messages" @@ -202,8 +269,8 @@ class CacheSettings(BaseSettings): ) database: int = Field(default=0, ge=0, le=15, description="Id of the database to use") driver: CacheDriver = CacheDriver.Redis - username: str = "infrahub" - password: str = "infrahub" + username: str = "" + password: str = "" tls_enabled: bool = Field(default=False, description="Indicates if TLS is enabled for the connection") tls_insecure: bool = Field(default=False, description="Indicates if TLS certificates are verified") tls_ca_file: Optional[str] = Field(default=None, description="File path to CA cert or bundle in PEM format") @@ -216,6 +283,27 @@ def service_port(self) -> int: return self.port or default_ports +class WorkflowSettings(BaseSettings): + model_config = SettingsConfigDict(env_prefix="INFRAHUB_WORKFLOW_") + enable: bool = True + address: str = "localhost" + port: Optional[int] = Field(default=None, ge=1, le=65535, description="Specified if running on a non default port.") + tls_enabled: bool = Field(default=False, description="Indicates if TLS is enabled for the connection") + driver: WorkflowDriver = WorkflowDriver.WORKER + worker_polling_interval: int = Field( + default=2, ge=1, le=30, description="Specify how often the worker should poll the server for tasks (sec)" + ) + + @property + def api_endpoint(self) -> str: + url = "https://" if self.tls_enabled else "http://" + url += self.address + if self.port: + url += f":{self.port}" + url += "/api" + return url + + class ApiSettings(BaseSettings): model_config = SettingsConfigDict(env_prefix="INFRAHUB_API_") cors_allow_origins: list[str] = Field( @@ -242,6 +330,58 @@ class GitSettings(BaseSettings): ) +class HTTPSettings(BaseSettings): + """The HTTP settings control how Infrahub interacts with external HTTP servers + + This can be things like webhooks and OAuth2 providers""" + + model_config = SettingsConfigDict(env_prefix="INFRAHUB_HTTP_") + timeout: int = Field(default=10, description="Default connection timeout in seconds") + tls_insecure: bool = Field( + default=False, + description="Indicates if Infrahub will validate server certificates or if the validation is ignored.", + ) + tls_ca_bundle: str | None = Field( + default=None, + description="Custom CA bundle in PEM format. The value should either be the CA bundle as a string, alternatively as a file path.", + ) + + @model_validator(mode="after") + def set_tls_context(self) -> Self: + try: + # Validate that the context can be created, we want to raise this error during application start + # instead of running into issues later when we first try to use the tls context. + self.get_tls_context() + except ssl.SSLError as exc: + raise ValueError(f"Unable load CA bundle from {self.tls_ca_bundle}: {exc}") from exc + + return self + + def get_tls_context(self) -> ssl.SSLContext: + if self.tls_insecure: + return ssl._create_unverified_context() + + if not self.tls_ca_bundle: + return ssl.create_default_context() + + tls_ca_path = Path(self.tls_ca_bundle) + + try: + possibly_file = tls_ca_path.exists() + except OSError: + # Raised if the filename is too long which can indicate + # that the value is a PEM certificate in string form. + possibly_file = False + + if possibly_file and tls_ca_path.is_file(): + context = ssl.create_default_context(cafile=str(tls_ca_path)) + else: + context = ssl.create_default_context() + context.load_verify_locations(cadata=self.tls_ca_bundle) + + return context + + class InitialSettings(BaseSettings): model_config = SettingsConfigDict(env_prefix="INFRAHUB_INITIAL_") default_branch: str = Field( @@ -268,6 +408,85 @@ def check_tokens_match(self) -> Self: return self +def _default_scopes() -> list[str]: + return ["openid", "profile", "email"] + + +class SecurityOIDCBaseSettings(BaseSettings): + """Baseclass for typing""" + + icon: str = Field(default="mdi:account-key") + display_label: str = Field(default="Single Sign on") + + +class SecurityOIDCSettings(SecurityOIDCBaseSettings): + client_id: str = Field(..., description="Client ID of the application created in the auth provider") + client_secret: str = Field(..., description="Client secret as defined in auth provider") + discovery_url: str = Field(..., description="The OIDC discovery URL xyz/.well-known/openid-configuration") + scopes: list[str] = Field(default_factory=_default_scopes) + + +class SecurityOIDCGoogle(SecurityOIDCSettings): + """Settings for the custom OIDC provider""" + + model_config = SettingsConfigDict(env_prefix="INFRAHUB_OIDC_GOOGLE_") + + discovery_url: str = Field(default="https://accounts.google.com/.well-known/openid-configuration") + icon: str = Field(default="mdi:google") + display_label: str = Field(default="Google") + + +class SecurityOIDCProvider1(SecurityOIDCSettings): + """Settings for the custom OIDC provider""" + + model_config = SettingsConfigDict(env_prefix="INFRAHUB_OIDC_PROVIDER1_") + + +class SecurityOIDCProvider2(SecurityOIDCSettings): + """Settings for the custom OIDC provider""" + + model_config = SettingsConfigDict(env_prefix="INFRAHUB_OIDC_PROVIDER2_") + + +class SecurityOAuth2BaseSettings(BaseSettings): + """Baseclass for typing""" + + icon: str = Field(default="mdi:account-key") + + +class SecurityOAuth2Settings(SecurityOAuth2BaseSettings): + """Common base for Oauth2 providers""" + + client_id: str = Field(..., description="Client ID of the application created in the auth provider") + client_secret: str = Field(..., description="Client secret as defined in auth provider") + authorization_url: str = Field(...) + token_url: str = Field(...) + userinfo_url: str = Field(...) + scopes: list[str] = Field(default_factory=_default_scopes) + display_label: str = Field(default="Single Sign on") + + +class SecurityOAuth2Provider1(SecurityOAuth2Settings): + """Common base for Oauth2 providers""" + + model_config = SettingsConfigDict(env_prefix="INFRAHUB_OAUTH2_PROVIDER1_") + + +class SecurityOAuth2Provider2(SecurityOAuth2Settings): + """Common base for Oauth2 providers""" + + model_config = SettingsConfigDict(env_prefix="INFRAHUB_OAUTH2_PROVIDER2_") + + +class SecurityOAuth2Google(SecurityOAuth2Settings): + model_config = SettingsConfigDict(env_prefix="INFRAHUB_OAUTH2_GOOGLE_") + authorization_url: str = Field(default="https://accounts.google.com/o/oauth2/auth") + token_url: str = Field(default="https://oauth2.googleapis.com/token") + userinfo_url: str = Field(default="https://www.googleapis.com/oauth2/v3/userinfo") + icon: str = Field(default="mdi:google") + display_label: str = Field(default="Google") + + class MiscellaneousSettings(BaseSettings): model_config = SettingsConfigDict(env_prefix="INFRAHUB_MISC_") print_query_details: bool = False @@ -312,6 +531,72 @@ class SecuritySettings(BaseSettings): secret_key: str = Field( default_factory=generate_uuid, description="The secret key used to validate authentication tokens" ) + oauth2_providers: list[Oauth2Provider] = Field(default_factory=list, description="The selected OAuth2 providers") + oidc_providers: list[OIDCProvider] = Field(default_factory=list, description="The selected OIDC providers") + _oauth2_settings: dict[str, SecurityOAuth2Settings] = PrivateAttr(default_factory=dict) + _oidc_settings: dict[str, SecurityOIDCSettings] = PrivateAttr(default_factory=dict) + + @model_validator(mode="after") + def check_oauth2_provider_settings(self) -> Self: + mapped_providers: dict[Oauth2Provider, type[SecurityOAuth2BaseSettings]] = { + Oauth2Provider.PROVIDER1: SecurityOAuth2Provider1, + Oauth2Provider.PROVIDER2: SecurityOAuth2Provider2, + Oauth2Provider.GOOGLE: SecurityOAuth2Google, + } + for oauth2_provider in self.oauth2_providers: + provider = mapped_providers[oauth2_provider]() + if isinstance(provider, SecurityOAuth2Settings): + self._oauth2_settings[oauth2_provider.value] = provider + + return self + + @model_validator(mode="after") + def check_oidc_provider_settings(self) -> Self: + mapped_providers: dict[OIDCProvider, type[SecurityOIDCBaseSettings]] = { + OIDCProvider.GOOGLE: SecurityOIDCGoogle, + OIDCProvider.PROVIDER1: SecurityOIDCProvider1, + OIDCProvider.PROVIDER2: SecurityOIDCProvider2, + } + for oidc_provider in self.oidc_providers: + provider = mapped_providers[oidc_provider]() + if isinstance(provider, SecurityOIDCSettings): + self._oidc_settings[oidc_provider.value] = provider + + return self + + def get_oauth2_provider(self, provider: str) -> SecurityOAuth2Settings: + if provider in self._oauth2_settings: + return self._oauth2_settings[provider] + + raise ProcessingError(message=f"The provider {provider} has not been initialized") + + def get_oidc_provider(self, provider: str) -> SecurityOIDCSettings: + if provider in self._oidc_settings: + return self._oidc_settings[provider] + + raise ProcessingError(message=f"The provider {provider} has not been initialized") + + @property + def public_sso_config(self) -> SSOInfo: + oauth2_providers = [ + SSOProviderInfo( + name=provider, + display_label=self._oauth2_settings[provider].display_label, + icon=self._oauth2_settings[provider].icon, + protocol=SSOProtocol.OAUTH2, + ) + for provider in self._oauth2_settings + ] + oidc_providers = [ + SSOProviderInfo( + name=provider, + display_label=self._oidc_settings[provider].display_label, + icon=self._oidc_settings[provider].icon, + protocol=SSOProtocol.OIDC, + ) + for provider in self._oidc_settings + ] + return SSOInfo(providers=oauth2_providers + oidc_providers) class TraceSettings(BaseSettings): @@ -333,10 +618,11 @@ class TraceSettings(BaseSettings): class Override: message_bus: Optional[InfrahubMessageBus] = None cache: Optional[InfrahubCache] = None + workflow: Optional[InfrahubWorkflow] = None @dataclass -class ConfiguredSettings: +class ConfiguredSettings: # pylint: disable=too-many-public-methods settings: Optional[Settings] = None def initialize(self, config_file: Optional[str] = None) -> None: @@ -379,6 +665,10 @@ def api(self) -> ApiSettings: def git(self) -> GitSettings: return self.active_settings.git + @property + def http(self) -> HTTPSettings: + return self.active_settings.http + @property def database(self) -> DatabaseSettings: return self.active_settings.database @@ -391,6 +681,14 @@ def broker(self) -> BrokerSettings: def cache(self) -> CacheSettings: return self.active_settings.cache + @property + def dev(self) -> DevelopmentSettings: + return self.active_settings.dev + + @property + def workflow(self) -> WorkflowSettings: + return self.active_settings.workflow + @property def miscellaneous(self) -> MiscellaneousSettings: return self.active_settings.miscellaneous @@ -430,9 +728,12 @@ class Settings(BaseSettings): main: MainSettings = MainSettings() api: ApiSettings = ApiSettings() git: GitSettings = GitSettings() + dev: DevelopmentSettings = DevelopmentSettings() + http: HTTPSettings = HTTPSettings() database: DatabaseSettings = DatabaseSettings() broker: BrokerSettings = BrokerSettings() cache: CacheSettings = CacheSettings() + workflow: WorkflowSettings = WorkflowSettings() miscellaneous: MiscellaneousSettings = MiscellaneousSettings() logging: LoggingSettings = LoggingSettings() analytics: AnalyticsSettings = AnalyticsSettings() diff --git a/backend/infrahub/core/account.py b/backend/infrahub/core/account.py index 2402c4be83..1e629fe8b8 100644 --- a/backend/infrahub/core/account.py +++ b/backend/infrahub/core/account.py @@ -1,17 +1,216 @@ from __future__ import annotations +from dataclasses import dataclass from typing import TYPE_CHECKING, Any, Optional, Union +from infrahub.core.constants import InfrahubKind from infrahub.core.query import Query from infrahub.core.registry import registry if TYPE_CHECKING: from infrahub.core.branch import Branch from infrahub.database import InfrahubDatabase + from infrahub.permissions.constants import AssignedPermissions + # pylint: disable=redefined-builtin +@dataclass +class Permission: + id: str + name: str + action: str + decision: str + + +@dataclass +class GlobalPermission(Permission): + def __str__(self) -> str: + return f"global:{self.action}:{self.decision}" + + +@dataclass +class ObjectPermission(Permission): + branch: str + namespace: str + + def __str__(self) -> str: + return f"object:{self.branch}:{self.namespace}:{self.name}:{self.action}:{self.decision}" + + +class AccountGlobalPermissionQuery(Query): + name: str = "account_global_permissions" + + def __init__(self, account_id: str, **kwargs: Any): + self.account_id = account_id + super().__init__(**kwargs) + + async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: + self.params["account_id"] = self.account_id + + branch_filter, branch_params = self.branch.get_query_filter_path( + at=self.at.to_string(), branch_agnostic=self.branch_agnostic + ) + self.params.update(branch_params) + + # ruff: noqa: E501 + query = """ + MATCH (account:%(generic_account_node)s) + WHERE account.uuid = $account_id + CALL { + WITH account + MATCH (account)-[r:IS_PART_OF]-(root:Root) + WHERE %(branch_filter)s + RETURN account as account1, r as r1 + ORDER BY r.branch_level DESC, r.from DESC + LIMIT 1 + } + WITH account, r1 as r + WHERE r.status = "active" + WITH account + MATCH group_path = (account)-[]->(:Relationship {name: "group_member"}) + <-[]-(:%(group_node)s) + -[]->(:Relationship {name: "role__accountgroups"}) + <-[]-(:%(account_role_node)s) + -[]->(:Relationship {name: "role__permissions"}) + <-[]-(global_permission:%(global_permission_node)s) + -[:HAS_ATTRIBUTE]->(:Attribute {name: "name"}) + -[:HAS_VALUE]->(global_permission_name:AttributeValue) + WITH global_permission, global_permission_name + WHERE all(r IN relationships(group_path) WHERE (%(branch_filter)s) AND r.status = "active") + MATCH action_path = (global_permission)-[:HAS_ATTRIBUTE]->(:Attribute {name: "action"})-[:HAS_VALUE]->(global_permission_action:AttributeValue) + WHERE all(r IN relationships(action_path) WHERE (%(branch_filter)s) AND r.status = "active") + MATCH decision_path = (global_permission)-[:HAS_ATTRIBUTE]->(:Attribute {name: "decision"})-[:HAS_VALUE]->(global_permission_decision:AttributeValue) + WHERE all(r IN relationships(decision_path) WHERE (%(branch_filter)s) AND r.status = "active") + """ % { + "branch_filter": branch_filter, + "generic_account_node": InfrahubKind.GENERICACCOUNT, + "account_role_node": InfrahubKind.ACCOUNTROLE, + "group_node": InfrahubKind.ACCOUNTGROUP, + "global_permission_node": InfrahubKind.GLOBALPERMISSION, + } + + self.add_to_query(query) + + self.return_labels = [ + "global_permission", + "global_permission_name", + "global_permission_action", + "global_permission_decision", + ] + + def get_permissions(self) -> list[GlobalPermission]: + permissions: list[GlobalPermission] = [] + + for result in self.get_results(): + permissions.append( + GlobalPermission( + id=result.get("global_permission").get("uuid"), + name=result.get("global_permission_name").get("value"), + action=result.get("global_permission_action").get("value"), + decision=result.get("global_permission_decision").get("value"), + ) + ) + + return permissions + + +class AccountObjectPermissionQuery(Query): + name: str = "account_object_permissions" + + def __init__(self, account_id: str, **kwargs: Any): + self.account_id = account_id + super().__init__(**kwargs) + + async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: + self.params["account_id"] = self.account_id + + branch_filter, branch_params = self.branch.get_query_filter_path( + at=self.at.to_string(), branch_agnostic=self.branch_agnostic + ) + self.params.update(branch_params) + + query = """ + MATCH (account:%(generic_account_node)s) + WHERE account.uuid = $account_id + CALL { + WITH account + MATCH (account)-[r:IS_PART_OF]-(root:Root) + WHERE %(branch_filter)s + RETURN account as account1, r as r1 + ORDER BY r.branch_level DESC, r.from DESC + LIMIT 1 + } + WITH account, r1 as r + WHERE r.status = "active" + WITH account + MATCH group_path = (account)-[]->(:Relationship {name: "group_member"}) + <-[]-(:%(account_group_node)s) + -[]->(:Relationship {name: "role__accountgroups"}) + <-[]-(:%(account_role_node)s) + -[]->(:Relationship {name: "role__permissions"}) + <-[]-(object_permission:%(object_permission_node)s) + -[:HAS_ATTRIBUTE]->(:Attribute {name: "branch"}) + -[:HAS_VALUE]->(object_permission_branch:AttributeValue) + WITH object_permission, object_permission_branch + WHERE all(r IN relationships(group_path) WHERE (%(branch_filter)s) AND r.status = "active") + MATCH namespace_path = (object_permission)-[:HAS_ATTRIBUTE]->(:Attribute {name: "namespace"})-[:HAS_VALUE]->(object_permission_namespace:AttributeValue) + WHERE all(r IN relationships(namespace_path) WHERE (%(branch_filter)s) AND r.status = "active") + MATCH name_path = (object_permission)-[:HAS_ATTRIBUTE]->(:Attribute {name: "name"})-[:HAS_VALUE]->(object_permission_name:AttributeValue) + WHERE all(r IN relationships(name_path) WHERE (%(branch_filter)s) AND r.status = "active") + MATCH action_path = (object_permission)-[:HAS_ATTRIBUTE]->(:Attribute {name: "action"})-[:HAS_VALUE]->(object_permission_action:AttributeValue) + WHERE all(r IN relationships(action_path) WHERE (%(branch_filter)s) AND r.status = "active") + MATCH decision_path = (object_permission)-[:HAS_ATTRIBUTE]->(:Attribute {name: "decision"})-[:HAS_VALUE]->(object_permission_decision:AttributeValue) + WHERE all(r IN relationships(decision_path) WHERE (%(branch_filter)s) AND r.status = "active") + """ % { + "branch_filter": branch_filter, + "account_group_node": InfrahubKind.ACCOUNTGROUP, + "account_role_node": InfrahubKind.ACCOUNTROLE, + "generic_account_node": InfrahubKind.GENERICACCOUNT, + "object_permission_node": InfrahubKind.OBJECTPERMISSION, + } + + self.add_to_query(query) + + self.return_labels = [ + "object_permission", + "object_permission_branch", + "object_permission_namespace", + "object_permission_name", + "object_permission_action", + "object_permission_decision", + ] + + def get_permissions(self) -> list[ObjectPermission]: + permissions: list[ObjectPermission] = [] + for result in self.get_results(): + permissions.append( + ObjectPermission( + id=result.get("object_permission").get("uuid"), + branch=result.get("object_permission_branch").get("value"), + namespace=result.get("object_permission_namespace").get("value"), + name=result.get("object_permission_name").get("value"), + action=result.get("object_permission_action").get("value"), + decision=result.get("object_permission_decision").get("value"), + ) + ) + + return permissions + + +async def fetch_permissions(account_id: str, db: InfrahubDatabase, branch: Branch) -> AssignedPermissions: + query1 = await AccountGlobalPermissionQuery.init(db=db, branch=branch, account_id=account_id, branch_agnostic=True) + await query1.execute(db=db) + global_permissions = query1.get_permissions() + + query2 = await AccountObjectPermissionQuery.init(db=db, branch=branch, account_id=account_id) + await query2.execute(db=db) + object_permissions = query2.get_permissions() + + return {"global_permissions": global_permissions, "object_permissions": object_permissions} + + class AccountTokenValidateQuery(Query): name: str = "account_token_validate" diff --git a/backend/infrahub/core/attribute.py b/backend/infrahub/core/attribute.py index a661c7d5b1..5074e10ea2 100644 --- a/backend/infrahub/core/attribute.py +++ b/backend/infrahub/core/attribute.py @@ -151,7 +151,7 @@ def get_branch_based_on_support_type(self) -> Branch: return self.branch @classmethod - def __init_subclass__(cls, **kwargs): + def __init_subclass__(cls, **kwargs) -> None: super().__init_subclass__(**kwargs) registry.attribute[cls.__name__] = cls @@ -549,7 +549,7 @@ async def from_graphql(self, data: dict, db: InfrahubDatabase) -> bool: return changed - def get_db_node_type(self): + def get_db_node_type(self) -> AttributeDBNodeType: return AttributeDBNodeType.DEFAULT def get_create_data(self) -> AttributeCreateData: @@ -840,7 +840,7 @@ def serialize_value(self) -> str: return ipaddress.ip_network(str(self.value)).with_prefixlen - def get_db_node_type(self): + def get_db_node_type(self) -> AttributeDBNodeType: if self.value is not None: return AttributeDBNodeType.IPNETWORK return AttributeDBNodeType.DEFAULT @@ -966,7 +966,7 @@ def serialize_value(self) -> str: return ipaddress.ip_interface(str(self.value)).with_prefixlen - def get_db_node_type(self): + def get_db_node_type(self) -> AttributeDBNodeType: if self.value is not None: return AttributeDBNodeType.IPHOST return AttributeDBNodeType.DEFAULT diff --git a/backend/infrahub/core/constants/__init__.py b/backend/infrahub/core/constants/__init__.py index 6bb1f4195c..d3a5e57e87 100644 --- a/backend/infrahub/core/constants/__init__.py +++ b/backend/infrahub/core/constants/__init__.py @@ -9,12 +9,12 @@ from .schema import FlagProperty, NodeProperty, SchemaElementPathType, UpdateSupport, UpdateValidationErrorType __all__ = [ - "InfrahubKind", "FlagProperty", + "InfrahubKind", "NodeProperty", + "SchemaElementPathType", "UpdateSupport", "UpdateValidationErrorType", - "SchemaElementPathType", ] @@ -50,6 +50,29 @@ class PermissionLevel(enum.Flag): DEFAULT = 0 +class GlobalPermissions(InfrahubStringEnum): + EDIT_DEFAULT_BRANCH = "edit_default_branch" + SUPER_ADMIN = "super_admin" + MERGE_BRANCH = "merge_branch" + MERGE_PROPOSED_CHANGE = "merge_proposed_change" + MANAGE_ACCOUNTS = "manage_accounts" + MANAGE_PERMISSIONS = "manage_permissions" + MANAGE_REPOSITORIES = "manage_repositories" + + +class PermissionAction(InfrahubStringEnum): + ANY = "any" + ADD = "create" + CHANGE = "update" + DELETE = "delete" + VIEW = "view" + + +class PermissionDecision(InfrahubStringEnum): + ALLOW = "allow" + DENY = "deny" + + class AccountRole(InfrahubStringEnum): ADMIN = "admin" READ_ONLY = "read-only" @@ -175,16 +198,6 @@ def from_relationship(cls, relationship: RelationshipCardinality) -> PathType: return cls("relationship_many") -class FilterSchemaKind(InfrahubStringEnum): - TEXT = "Text" - LIST = "Text" - NUMBER = "Number" - BOOLEAN = "Boolean" - OBJECT = "Object" - MULTIOBJECT = "MultiObject" - ENUM = "Enum" - - class ProposedChangeState(InfrahubStringEnum): OPEN = "open" MERGED = "merged" diff --git a/backend/infrahub/core/constants/infrahubkind.py b/backend/infrahub/core/constants/infrahubkind.py index 904371b510..bc2f8229f6 100644 --- a/backend/infrahub/core/constants/infrahubkind.py +++ b/backend/infrahub/core/constants/infrahubkind.py @@ -1,4 +1,6 @@ ACCOUNT = "CoreAccount" +ACCOUNTGROUP = "CoreAccountGroup" +ACCOUNTROLE = "CoreAccountRole" ACCOUNTTOKEN = "InternalAccountToken" ARTIFACT = "CoreArtifact" ARTIFACTCHECK = "CoreArtifactCheck" @@ -6,6 +8,7 @@ ARTIFACTTARGET = "CoreArtifactTarget" ARTIFACTTHREAD = "CoreArtifactThread" ARTIFACTVALIDATOR = "CoreArtifactValidator" +BASEPERMISSION = "CoreBasePermission" CHANGECOMMENT = "CoreChangeComment" CHANGETHREAD = "CoreChangeThread" CHECKDEFINITION = "CoreCheckDefinition" @@ -23,6 +26,7 @@ GENERATORVALIDATOR = "CoreGeneratorValidator" GENERATORGROUP = "CoreGeneratorGroup" GENERICGROUP = "CoreGroup" +GLOBALPERMISSION = "CoreGlobalPermission" GRAPHQLQUERY = "CoreGraphQLQuery" GRAPHQLQUERYGROUP = "CoreGraphQLQueryGroup" IPNAMESPACE = "BuiltinIPNamespace" @@ -30,11 +34,13 @@ IPADDRESSPOOL = "CoreIPAddressPool" IPPREFIX = "BuiltinIPPrefix" IPPREFIXPOOL = "CoreIPPrefixPool" +MENUITEM = "CoreMenuItem" NAMESPACE = "IpamNamespace" NODE = "CoreNode" NUMBERPOOL = "CoreNumberPool" LINEAGEOWNER = "LineageOwner" LINEAGESOURCE = "LineageSource" +OBJECTPERMISSION = "CoreObjectPermission" OBJECTTHREAD = "CoreObjectThread" PASSWORDCREDENTIAL = "CorePasswordCredential" PROFILE = "CoreProfile" diff --git a/backend/infrahub/core/diff/branch_differ.py b/backend/infrahub/core/diff/branch_differ.py index 30bfb06cc9..240823b261 100644 --- a/backend/infrahub/core/diff/branch_differ.py +++ b/backend/infrahub/core/diff/branch_differ.py @@ -278,7 +278,7 @@ async def get_conflicts_graph(self) -> list[DataConflict]: property_name=conflict.property_name, ) for branch, change in changes.items(): - if response.path in change and change[response.path]: + if change.get(response.path): response.changes.append( BranchChanges( branch=branch, diff --git a/backend/infrahub/core/diff/combiner.py b/backend/infrahub/core/diff/combiner.py index 67ecc7bb14..584c70265b 100644 --- a/backend/infrahub/core/diff/combiner.py +++ b/backend/infrahub/core/diff/combiner.py @@ -108,7 +108,7 @@ def _combine_actions(self, earlier: DiffAction, later: DiffAction) -> DiffAction (DiffAction.REMOVED, DiffAction.ADDED): DiffAction.UPDATED, (DiffAction.REMOVED, DiffAction.UPDATED): DiffAction.UPDATED, } - return actions_map[(earlier, later)] + return actions_map[earlier, later] @staticmethod def combine_conflicts( @@ -155,6 +155,15 @@ def _combine_properties( conflict=combined_conflict, ) ) + combined_properties.add( + replace( + later_property, + previous_label=earlier_property.previous_label, + previous_value=earlier_property.previous_value, + action=self._combine_actions(earlier=earlier_property.action, later=later_property.action), + conflict=combined_conflict, + ) + ) combined_properties |= { deepcopy(prop) for prop in later_properties if prop.property_type not in common_property_types } diff --git a/backend/infrahub/core/diff/coordinator.py b/backend/infrahub/core/diff/coordinator.py index cc334cb857..c725222fd9 100644 --- a/backend/infrahub/core/diff/coordinator.py +++ b/backend/infrahub/core/diff/coordinator.py @@ -343,11 +343,12 @@ def _get_missing_time_ranges( return missing_time_ranges def _get_node_field_specifiers(self, enriched_diff: EnrichedDiffRoot) -> set[NodeFieldSpecifier]: - specifiers = set() + specifiers: set[NodeFieldSpecifier] = set() schema_branch = registry.schema.get_schema_branch(name=enriched_diff.diff_branch_name) for node in enriched_diff.nodes: - for attribute in node.attributes: - specifiers.add(NodeFieldSpecifier(node_uuid=node.uuid, field_name=attribute.name)) + specifiers.update( + NodeFieldSpecifier(node_uuid=node.uuid, field_name=attribute.name) for attribute in node.attributes + ) if not node.relationships: continue node_schema = schema_branch.get_node(name=node.kind, duplicate=False) diff --git a/sync/examples/infrahub_to_peering-manager/infrahub/__init__.py b/backend/infrahub/core/diff/merger/__init__.py similarity index 100% rename from sync/examples/infrahub_to_peering-manager/infrahub/__init__.py rename to backend/infrahub/core/diff/merger/__init__.py diff --git a/backend/infrahub/core/diff/merger/merger.py b/backend/infrahub/core/diff/merger/merger.py new file mode 100644 index 0000000000..0792f2549f --- /dev/null +++ b/backend/infrahub/core/diff/merger/merger.py @@ -0,0 +1,47 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from infrahub.core.diff.model.path import BranchTrackingId +from infrahub.core.diff.query.merge import DiffMergeQuery + +if TYPE_CHECKING: + from infrahub.core.branch import Branch + from infrahub.core.diff.repository.repository import DiffRepository + from infrahub.core.timestamp import Timestamp + from infrahub.database import InfrahubDatabase + + from .serializer import DiffMergeSerializer + + +class DiffMerger: + def __init__( + self, + db: InfrahubDatabase, + source_branch: Branch, + destination_branch: Branch, + diff_repository: DiffRepository, + serializer: DiffMergeSerializer, + ): + self.source_branch = source_branch + self.destination_branch = destination_branch + self.db = db + self.diff_repository = diff_repository + self.serializer = serializer + + async def merge_graph(self, at: Timestamp) -> None: + enriched_diff = await self.diff_repository.get_one( + diff_branch_name=self.source_branch.name, tracking_id=BranchTrackingId(name=self.source_branch.name) + ) + node_diff_dicts = await self.serializer.serialize(diff=enriched_diff) + merge_query = await DiffMergeQuery.init( + db=self.db, + branch=self.source_branch, + at=at, + target_branch=self.destination_branch, + node_diff_dicts=node_diff_dicts, + ) + await merge_query.execute(db=self.db) + + self.source_branch.branched_from = at.to_string() + await self.source_branch.save(db=self.db) diff --git a/backend/infrahub/core/diff/merger/serializer.py b/backend/infrahub/core/diff/merger/serializer.py new file mode 100644 index 0000000000..52aad8ad28 --- /dev/null +++ b/backend/infrahub/core/diff/merger/serializer.py @@ -0,0 +1,28 @@ +from typing import Any + +from infrahub.core.constants import DiffAction + +from ..model.path import ConflictSelection, EnrichedDiffConflict, EnrichedDiffRoot + + +class DiffMergeSerializer: + def _get_action(self, action: DiffAction, conflict: EnrichedDiffConflict | None) -> DiffAction: + if not conflict: + return action + if conflict.selected_branch is ConflictSelection.BASE_BRANCH: + return conflict.base_branch_action + if conflict.selected_branch is ConflictSelection.DIFF_BRANCH: + return conflict.diff_branch_action + raise ValueError(f"conflict {conflict.uuid} does not have a branch selection") + + async def serialize(self, diff: EnrichedDiffRoot) -> list[dict[str, Any]]: + serialized_node_diffs = [] + for node in diff.nodes: + node_action = self._get_action(action=node.action, conflict=node.conflict) + serialized_node_diffs.append( + { + "action": str(node_action.value).upper(), + "uuid": node.uuid, + } + ) + return serialized_node_diffs diff --git a/backend/infrahub/core/diff/model/path.py b/backend/infrahub/core/diff/model/path.py index 5fb902d0c4..617de15777 100644 --- a/backend/infrahub/core/diff/model/path.py +++ b/backend/infrahub/core/diff/model/path.py @@ -15,7 +15,7 @@ from neo4j.graph import Path as Neo4jPath from neo4j.graph import Relationship as Neo4jRelationship - from infrahub.graphql import GraphqlContext + from infrahub.graphql.initialization import GraphqlContext @dataclass diff --git a/backend/infrahub/core/diff/query/merge.py b/backend/infrahub/core/diff/query/merge.py new file mode 100644 index 0000000000..b9084431db --- /dev/null +++ b/backend/infrahub/core/diff/query/merge.py @@ -0,0 +1,95 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any + +from infrahub.core.query import Query, QueryType + +if TYPE_CHECKING: + from infrahub.core.branch import Branch + from infrahub.core.timestamp import Timestamp + from infrahub.database import InfrahubDatabase + + +class DiffMergeQuery(Query): + name = "diff_merge" + type = QueryType.WRITE + insert_return = False + + def __init__( + self, + node_diff_dicts: dict[str, Any], + at: Timestamp, + target_branch: Branch, + **kwargs: Any, + ) -> None: + super().__init__(**kwargs) + self.node_diff_dicts = node_diff_dicts + self.at = at + self.target_branch = target_branch + self.source_branch_name = self.branch.name + + async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: + self.params = { + "node_diff_dicts": self.node_diff_dicts, + "at": self.at.to_string(), + "branch_level": self.target_branch.hierarchy_level, + "target_branch": self.target_branch.name, + "source_branch": self.source_branch_name, + } + query = """ +UNWIND $node_diff_dicts AS node_diff_map +CALL { + WITH node_diff_map + WITH node_diff_map, CASE + WHEN node_diff_map.action = "ADDED" THEN "active" + WHEN node_diff_map.action = "REMOVED" THEN "deleted" + ELSE NULL + END AS node_rel_status + CALL { + // ------------------------------ + // only make IS_PART_OF updates if node is ADDED or REMOVED + // ------------------------------ + WITH node_diff_map, node_rel_status + WITH node_diff_map, node_rel_status + WHERE node_rel_status IS NOT NULL + MATCH (root:Root) + MATCH (n:Node {uuid: node_diff_map.uuid}) + // ------------------------------ + // check if IS_PART_OF relationship with node_rel_status already exists on the target branch + // ------------------------------ + CALL { + WITH root, n, node_rel_status + OPTIONAL MATCH (root)<-[r_root:IS_PART_OF {branch: $target_branch}]-(n) + WHERE r_root.status = node_rel_status + AND r_root.from <= $at + AND (r_root.to >= $at OR r_root.to IS NULL) + RETURN r_root + } + // ------------------------------ + // set IS_PART_OF.to on source branch and, optionally, target branch + // ------------------------------ + WITH root, r_root, n, node_rel_status + CALL { + WITH root, n, node_rel_status + OPTIONAL MATCH (root)<-[source_r_root:IS_PART_OF {branch: $source_branch, status: node_rel_status}]-(n) + WHERE source_r_root.from <= $at AND source_r_root.to IS NULL + SET source_r_root.to = $at + } + WITH root, r_root, n, node_rel_status + CALL { + WITH root, n, node_rel_status + OPTIONAL MATCH (root)<-[target_r_root:IS_PART_OF {branch: $target_branch, status: "active"}]-(n) + WHERE node_rel_status = "deleted" + AND target_r_root.from <= $at AND target_r_root.to IS NULL + SET target_r_root.to = $at + } + // ------------------------------ + // create new IS_PART_OF relationship on target_branch + // ------------------------------ + WITH root, r_root, n, node_rel_status + WHERE r_root IS NULL + CREATE (root)<-[:IS_PART_OF { branch: $target_branch, branch_level: $branch_level, from: $at, status: node_rel_status }]-(n) + } +} + """ + self.add_to_query(query=query) diff --git a/backend/infrahub/core/diff/query_parser.py b/backend/infrahub/core/diff/query_parser.py index 367ccd0a5b..f3606f34e7 100644 --- a/backend/infrahub/core/diff/query_parser.py +++ b/backend/infrahub/core/diff/query_parser.py @@ -22,8 +22,8 @@ if TYPE_CHECKING: from infrahub.core.branch import Branch from infrahub.core.query import QueryResult + from infrahub.core.schema.manager import SchemaManager from infrahub.core.schema.relationship_schema import RelationshipSchema - from infrahub.core.schema_manager import SchemaManager class DiffNoChildPathError(Exception): ... @@ -447,15 +447,17 @@ def get_diff_root_for_branch(self, branch: str) -> DiffRoot: def get_node_field_specifiers_for_branch(self, branch_name: str) -> set[NodeFieldSpecifier]: if branch_name not in self._diff_root_by_branch: return set() - node_field_specifiers = set() + node_field_specifiers: set[NodeFieldSpecifier] = set() diff_root = self._diff_root_by_branch[branch_name] for node in diff_root.nodes_by_id.values(): - for attribute_name in node.attributes_by_name: - node_field_specifiers.add(NodeFieldSpecifier(node_uuid=node.uuid, field_name=attribute_name)) - for relationship_diff in node.relationships_by_name.values(): - node_field_specifiers.add( - NodeFieldSpecifier(node_uuid=node.uuid, field_name=relationship_diff.identifier) - ) + node_field_specifiers.update( + NodeFieldSpecifier(node_uuid=node.uuid, field_name=attribute_name) + for attribute_name in node.attributes_by_name + ) + node_field_specifiers.update( + NodeFieldSpecifier(node_uuid=node.uuid, field_name=relationship_diff.identifier) + for relationship_diff in node.relationships_by_name.values() + ) return node_field_specifiers def read_result(self, query_result: QueryResult) -> None: diff --git a/backend/infrahub/core/initialization.py b/backend/infrahub/core/initialization.py index 1c3d5b3b8a..eef11ef412 100644 --- a/backend/infrahub/core/initialization.py +++ b/backend/infrahub/core/initialization.py @@ -1,23 +1,37 @@ +import importlib from typing import Optional from uuid import uuid4 from infrahub import config, lock from infrahub.core import registry from infrahub.core.branch import Branch -from infrahub.core.constants import DEFAULT_IP_NAMESPACE, GLOBAL_BRANCH_NAME, AccountRole, InfrahubKind +from infrahub.core.constants import ( + DEFAULT_IP_NAMESPACE, + GLOBAL_BRANCH_NAME, + AccountRole, + GlobalPermissions, + InfrahubKind, + PermissionDecision, +) from infrahub.core.graph import GRAPH_VERSION from infrahub.core.node import Node from infrahub.core.node.ipam import BuiltinIPPrefix +from infrahub.core.node.permissions import CoreGlobalPermission, CoreObjectPermission from infrahub.core.node.resource_manager.ip_address_pool import CoreIPAddressPool from infrahub.core.node.resource_manager.ip_prefix_pool import CoreIPPrefixPool from infrahub.core.node.resource_manager.number_pool import CoreNumberPool +from infrahub.core.protocols import CoreAccount, CoreMenuItem from infrahub.core.root import Root from infrahub.core.schema import SchemaRoot, core_models, internal_schema -from infrahub.core.schema_manager import SchemaManager +from infrahub.core.schema.manager import SchemaManager from infrahub.database import InfrahubDatabase from infrahub.exceptions import DatabaseError from infrahub.log import get_logger +from infrahub.menu.menu import default_menu +from infrahub.menu.models import MenuItemDefinition +from infrahub.permissions import PermissionBackend from infrahub.storage import InfrahubObjectStorage +from infrahub.utils import format_label log = get_logger() @@ -53,6 +67,18 @@ async def get_default_ipnamespace(db: InfrahubDatabase) -> Optional[Node]: return nodes[0] +def initialize_permission_backends() -> list[PermissionBackend]: + permission_backends: list[PermissionBackend] = [] + for backend_module_path in config.SETTINGS.main.permission_backends: + log.info("Loading permission backend", backend=backend_module_path) + + module, class_name = backend_module_path.rsplit(".", maxsplit=1) + Backend = getattr(importlib.import_module(module), class_name) + permission_backends.append(Backend()) + + return permission_backends + + async def initialize_registry(db: InfrahubDatabase, initialize: bool = False) -> None: # --------------------------------------------------- # Initialize the database and Load the Root node @@ -81,6 +107,13 @@ async def initialize_registry(db: InfrahubDatabase, initialize: bool = False) -> registry.node[InfrahubKind.IPADDRESSPOOL] = CoreIPAddressPool registry.node[InfrahubKind.IPPREFIXPOOL] = CoreIPPrefixPool registry.node[InfrahubKind.NUMBERPOOL] = CoreNumberPool + registry.node[InfrahubKind.GLOBALPERMISSION] = CoreGlobalPermission + registry.node[InfrahubKind.OBJECTPERMISSION] = CoreObjectPermission + + # --------------------------------------------------- + # Instantiate permission backends + # --------------------------------------------------- + registry.permission_backends = initialize_permission_backends() async def initialization(db: InfrahubDatabase) -> None: @@ -233,9 +266,9 @@ async def create_account( role: str = "admin", password: Optional[str] = None, token_value: Optional[str] = None, -) -> Node: +) -> CoreAccount: token_schema = db.schema.get_node_schema(name=InfrahubKind.ACCOUNTTOKEN) - obj = await Node.init(db=db, schema=InfrahubKind.ACCOUNT) + obj = await Node.init(db=db, schema=CoreAccount) await obj.new(db=db, name=name, account_type="User", role=role, password=password) await obj.save(db=db) log.info(f"Created Account: {name}", account_name=name) @@ -261,6 +294,72 @@ async def create_ipam_namespace( return obj +async def create_initial_permission(db: InfrahubDatabase) -> Node: + permission = await Node.init(db=db, schema=InfrahubKind.GLOBALPERMISSION) + await permission.new( + db=db, + name=format_label(GlobalPermissions.SUPER_ADMIN.value), + action=GlobalPermissions.SUPER_ADMIN.value, + decision=PermissionDecision.ALLOW.value, + ) + await permission.save(db=db) + log.info(f"Created global permission: {GlobalPermissions.SUPER_ADMIN}") + return permission + + +async def create_menu_children(db: InfrahubDatabase, parent: CoreMenuItem, children: list[MenuItemDefinition]) -> None: + for child in children: + obj = await child.to_node(db=db, parent=parent) + await obj.save(db=db) + if child.children: + await create_menu_children(db=db, parent=obj, children=child.children) + + +async def create_default_menu(db: InfrahubDatabase) -> None: + for item in default_menu: + obj = await item.to_node(db=db) + await obj.save(db=db) + if item.children: + await create_menu_children(db=db, parent=obj, children=item.children) + + +async def create_super_administrator_role(db: InfrahubDatabase) -> Node: + permission = await Node.init(db=db, schema=InfrahubKind.GLOBALPERMISSION) + await permission.new( + db=db, + name=format_label(GlobalPermissions.SUPER_ADMIN.value), + action=GlobalPermissions.SUPER_ADMIN.value, + decision=PermissionDecision.ALLOW.value, + ) + await permission.save(db=db) + log.info(f"Created global permission: {GlobalPermissions.SUPER_ADMIN}") + + role_name = "Super Administrator" + obj = await Node.init(db=db, schema=InfrahubKind.ACCOUNTROLE) + await obj.new(db=db, name=role_name, permissions=[permission]) + await obj.save(db=db) + log.info(f"Created account role: {role_name}") + + return obj + + +async def create_super_administrators_group( + db: InfrahubDatabase, role: Node, admin_accounts: list[CoreAccount] +) -> Node: + group_name = "Super Administrators" + group = await Node.init(db=db, schema=InfrahubKind.ACCOUNTGROUP) + await group.new(db=db, name=group_name, roles=[role]) + await group.save(db=db) + log.info(f"Created account group: {group_name}") + + for admin_account in admin_accounts: + await group.members.add(db=db, data=admin_account) # type: ignore[attr-defined] + await group.members.save(db=db) # type: ignore[attr-defined] + log.info(f"Assigned account group: {group_name} to {admin_account.name.value}") + + return group + + async def first_time_initialization(db: InfrahubDatabase) -> None: # -------------------------------------------------- # Create the default Branch @@ -283,27 +382,43 @@ async def first_time_initialization(db: InfrahubDatabase) -> None: await default_branch.save(db=db) log.info("Created the Schema in the database", hash=default_branch.active_schema_hash.main) + # -------------------------------------------------- + # Create Default Menu + # -------------------------------------------------- + await create_default_menu(db=db) + # -------------------------------------------------- # Create Default Users and Groups # -------------------------------------------------- - await create_account( - db=db, - name="admin", - password=config.SETTINGS.initial.admin_password, - token_value=config.SETTINGS.initial.admin_token, + admin_accounts: list[CoreAccount] = [] + admin_accounts.append( + await create_account( + db=db, + name="admin", + password=config.SETTINGS.initial.admin_password, + token_value=config.SETTINGS.initial.admin_token, + ) ) if config.SETTINGS.initial.create_agent_user: password = config.SETTINGS.initial.agent_password or str(uuid4()) - await create_account( - db=db, - name="agent", - password=password, - role=AccountRole.READ_WRITE.value, - token_value=config.SETTINGS.initial.agent_token, + admin_accounts.append( + await create_account( + db=db, + name="agent", + password=password, + role=AccountRole.READ_WRITE.value, + token_value=config.SETTINGS.initial.agent_token, + ) ) + # -------------------------------------------------- + # Create Global Permissions and assign them + # -------------------------------------------------- + administrator_role = await create_super_administrator_role(db=db) + await create_super_administrators_group(db=db, role=administrator_role, admin_accounts=admin_accounts) + # -------------------------------------------------- # Create Default IPAM Namespace # -------------------------------------------------- diff --git a/backend/infrahub/core/ipam/utilization.py b/backend/infrahub/core/ipam/utilization.py index 7b48ce9cf4..da85b79293 100644 --- a/backend/infrahub/core/ipam/utilization.py +++ b/backend/infrahub/core/ipam/utilization.py @@ -75,7 +75,7 @@ async def get_children( return prefix_child_details_list for prefix_id in prefix_ids: child_details_by_branch = self._results_by_prefix_id[prefix_id] - branch_names_to_check = branch_names if branch_names else list(child_details_by_branch.keys()) + branch_names_to_check = branch_names or list(child_details_by_branch.keys()) for branch_name in branch_names_to_check: for child_details in child_details_by_branch.get(branch_name, []): if prefix_member_type and child_details.child_type != prefix_member_type: diff --git a/backend/infrahub/core/manager.py b/backend/infrahub/core/manager.py index a40d0cbc3e..495b2994d6 100644 --- a/backend/infrahub/core/manager.py +++ b/backend/infrahub/core/manager.py @@ -71,7 +71,7 @@ def __init__( self, profile_attributes_id_map: dict[str, NodeAttributesFromDB], profile_ids_by_node_id: dict[str, list[str]], - ): + ) -> None: self._profile_attributes_id_map = profile_attributes_id_map self._profile_ids_by_node_id = profile_ids_by_node_id @@ -1043,21 +1043,28 @@ async def get_one( node = result[id] node_schema = node.get_schema() + kind_validation = None + if kind: + node_schema_validation = get_schema(db=db, branch=branch, node_schema=kind) + kind_validation = node_schema_validation.kind + # Temporary list of exception to the validation of the kind kind_validation_exceptions = [ ("CoreChangeThread", "CoreObjectThread"), # issue/3318 ] - if kind and (node_schema.kind != kind and kind not in node_schema.inherit_from): + if kind_validation and ( + node_schema.kind != kind_validation and kind_validation not in node_schema.inherit_from + ): for item in kind_validation_exceptions: - if item[0] == kind and item[1] == node.get_kind(): + if item[0] == kind_validation and item[1] == node.get_kind(): return node raise NodeNotFoundError( branch_name=branch.name, - node_type=kind, + node_type=kind_validation, identifier=id, - message=f"Node with id {id} exists, but it is a {node.get_kind()}, not {kind}", + message=f"Node with id {id} exists, but it is a {node.get_kind()}, not {kind_validation}", ) return node diff --git a/backend/infrahub/core/merge.py b/backend/infrahub/core/merge.py index 49ec1c5645..818ea38de2 100644 --- a/backend/infrahub/core/merge.py +++ b/backend/infrahub/core/merge.py @@ -4,7 +4,7 @@ from infrahub.core.constants import DiffAction, RelationshipStatus, RepositoryInternalStatus from infrahub.core.manager import NodeManager -from infrahub.core.models import SchemaBranchDiff +from infrahub.core.models import SchemaBranchDiff, SchemaUpdateValidationResult from infrahub.core.protocols import CoreRepository from infrahub.core.query.branch import ( AddNodeToBranch, @@ -12,7 +12,6 @@ from infrahub.core.query.node import NodeDeleteQuery, NodeListGetInfoQuery from infrahub.core.registry import registry from infrahub.core.schema import GenericSchema, NodeSchema -from infrahub.core.schema_manager import SchemaUpdateValidationResult from infrahub.core.timestamp import Timestamp from infrahub.core.utils import add_relationship, update_relationships_to from infrahub.exceptions import ValidationError @@ -23,7 +22,8 @@ if TYPE_CHECKING: from infrahub.core.branch import Branch from infrahub.core.models import SchemaUpdateConstraintInfo, SchemaUpdateMigrationInfo - from infrahub.core.schema_manager import SchemaBranch, SchemaDiff + from infrahub.core.schema.manager import SchemaDiff + from infrahub.core.schema.schema_branch import SchemaBranch from infrahub.database import InfrahubDatabase from infrahub.services import InfrahubServices diff --git a/backend/infrahub/core/migrations/graph/m012_convert_account_generic.py b/backend/infrahub/core/migrations/graph/m012_convert_account_generic.py index c26f42d95b..37c49b1556 100644 --- a/backend/infrahub/core/migrations/graph/m012_convert_account_generic.py +++ b/backend/infrahub/core/migrations/graph/m012_convert_account_generic.py @@ -5,7 +5,7 @@ from infrahub.core.branch import Branch from infrahub.core.constants import GLOBAL_BRANCH_NAME, BranchSupportType, InfrahubKind from infrahub.core.migrations.shared import MigrationResult -from infrahub.core.query import Query, QueryType # noqa: TCH001 +from infrahub.core.query import Query, QueryType from ..query.attribute_rename import AttributeInfo, AttributeRenameQuery from ..query.delete_element_in_schema import DeleteElementInSchemaQuery diff --git a/backend/infrahub/core/migrations/query/schema_attribute_update.py b/backend/infrahub/core/migrations/query/schema_attribute_update.py index f3e488c8d7..1a52cdbfbf 100644 --- a/backend/infrahub/core/migrations/query/schema_attribute_update.py +++ b/backend/infrahub/core/migrations/query/schema_attribute_update.py @@ -4,7 +4,7 @@ import ujson -from infrahub.core.query import Query, QueryType # noqa: TCH001 +from infrahub.core.query import Query, QueryType from infrahub.core.timestamp import Timestamp if TYPE_CHECKING: diff --git a/backend/infrahub/core/migrations/schema/models.py b/backend/infrahub/core/migrations/schema/models.py new file mode 100644 index 0000000000..16e0a7fad5 --- /dev/null +++ b/backend/infrahub/core/migrations/schema/models.py @@ -0,0 +1,15 @@ +from pydantic import BaseModel, ConfigDict + +from infrahub.core.branch import Branch +from infrahub.core.models import SchemaUpdateMigrationInfo +from infrahub.core.schema.schema_branch import SchemaBranch + + +class SchemaApplyMigrationData(BaseModel): + model_config = ConfigDict( + arbitrary_types_allowed=True, json_encoders={SchemaBranch: SchemaBranch.to_dict_schema_object} + ) + branch: Branch + new_schema: SchemaBranch + previous_schema: SchemaBranch + migrations: list[SchemaUpdateMigrationInfo] diff --git a/backend/infrahub/core/migrations/schema/runner.py b/backend/infrahub/core/migrations/schema/runner.py index 49a83ea9b5..8ebd66fb19 100644 --- a/backend/infrahub/core/migrations/schema/runner.py +++ b/backend/infrahub/core/migrations/schema/runner.py @@ -3,13 +3,16 @@ import asyncio from typing import TYPE_CHECKING, Optional -from infrahub.message_bus.messages.schema_migration_path import SchemaMigrationPath, SchemaMigrationPathResponse +from infrahub.message_bus.messages.schema_migration_path import ( + SchemaMigrationPath, + SchemaMigrationPathResponse, +) if TYPE_CHECKING: from infrahub.core.branch import Branch from infrahub.core.models import SchemaUpdateMigrationInfo from infrahub.core.schema import MainSchemaTypes - from infrahub.core.schema_manager import SchemaBranch + from infrahub.core.schema.schema_branch import SchemaBranch from infrahub.services import InfrahubServices diff --git a/backend/infrahub/core/migrations/schema/tasks.py b/backend/infrahub/core/migrations/schema/tasks.py new file mode 100644 index 0000000000..d67e4ba2aa --- /dev/null +++ b/backend/infrahub/core/migrations/schema/tasks.py @@ -0,0 +1,65 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Optional + +from infrahub_sdk.batch import InfrahubBatch +from prefect import flow + +from infrahub.message_bus.messages.schema_migration_path import ( + SchemaMigrationPathData, +) +from infrahub.message_bus.operations.schema.migration import schema_path_migrate +from infrahub.services import services + +from .models import SchemaApplyMigrationData # noqa: TCH001 + +if TYPE_CHECKING: + from infrahub.core.schema import MainSchemaTypes + + +@flow +async def schema_apply_migrations(message: SchemaApplyMigrationData) -> list[str]: + service = services.service + + batch = InfrahubBatch() + error_messages: list[str] = [] + + if not message.migrations: + return error_messages + + for migration in message.migrations: + service.log.info( + f"Preparing migration for {migration.migration_name!r} ({migration.routing_key})", + branch=message.branch.name, + ) + + new_node_schema: Optional[MainSchemaTypes] = None + previous_node_schema: Optional[MainSchemaTypes] = None + + if message.new_schema.has(name=migration.path.schema_kind): + new_node_schema = message.new_schema.get(name=migration.path.schema_kind) + + if new_node_schema and new_node_schema.id: + previous_node_schema = message.previous_schema.get_by_id(id=new_node_schema.id) + else: + previous_node_schema = message.previous_schema.get(name=migration.path.schema_kind) + + if not previous_node_schema: + raise ValueError( + f"Unable to find the previous version of the schema for {migration.path.schema_kind}, in order to run the migration." + ) + + msg = SchemaMigrationPathData( + branch=message.branch, + migration_name=migration.migration_name, + new_node_schema=new_node_schema, + previous_node_schema=previous_node_schema, + schema_path=migration.path, + ) + + batch.add(task=schema_path_migrate, message=msg) + + async for _, result in batch.execute(): + error_messages.extend(result.errors) + + return error_messages diff --git a/backend/infrahub/core/migrations/shared.py b/backend/infrahub/core/migrations/shared.py index d0c721937e..20cf7b3f46 100644 --- a/backend/infrahub/core/migrations/shared.py +++ b/backend/infrahub/core/migrations/shared.py @@ -21,7 +21,7 @@ if TYPE_CHECKING: from infrahub.core.branch import Branch - from infrahub.core.schema_manager import SchemaBranch + from infrahub.core.schema.schema_branch import SchemaBranch from infrahub.core.timestamp import Timestamp from infrahub.database import InfrahubDatabase @@ -141,7 +141,7 @@ class InternalSchemaMigration(BaseModel): @staticmethod def get_internal_schema() -> SchemaBranch: - from infrahub.core.schema_manager import SchemaBranch # pylint: disable=import-outside-toplevel + from infrahub.core.schema.schema_branch import SchemaBranch # pylint: disable=import-outside-toplevel # load the internal schema from schema = SchemaRoot(**internal_schema) diff --git a/backend/infrahub/core/models.py b/backend/infrahub/core/models.py index 81628afd72..0339a40b72 100644 --- a/backend/infrahub/core/models.py +++ b/backend/infrahub/core/models.py @@ -17,7 +17,7 @@ if TYPE_CHECKING: from infrahub.core.schema import MainSchemaTypes - from infrahub.core.schema_manager import SchemaBranch + from infrahub.core.schema.schema_branch import SchemaBranch class NodeKind(BaseModel): @@ -409,7 +409,7 @@ def duplicate(self) -> Self: @staticmethod def is_list_composed_of_hashable_model(items: list[Any]) -> bool: - return all((isinstance(item, HashableModel) for item in items)) + return all(isinstance(item, HashableModel) for item in items) @staticmethod def _organize_sub_items(items: list[HashableModel], shared_ids: set[str]) -> dict[tuple[Any], HashableModel]: @@ -417,7 +417,7 @@ def _organize_sub_items(items: list[HashableModel], shared_ids: set[str]) -> dic sub_items = {} for item in items: if item.id and item.id in shared_ids: - sub_items[(item.id,)] = item + sub_items[item.id,] = item continue sub_items[item._sorting_id] = item diff --git a/backend/infrahub/core/node/__init__.py b/backend/infrahub/core/node/__init__.py index fe2f423285..85db92eda9 100644 --- a/backend/infrahub/core/node/__init__.py +++ b/backend/infrahub/core/node/__init__.py @@ -1,7 +1,7 @@ from __future__ import annotations from enum import Enum -from typing import TYPE_CHECKING, Any, Optional, Union +from typing import TYPE_CHECKING, Any, Optional, TypeVar, Union, overload from infrahub_sdk import UUIDT from infrahub_sdk.utils import is_valid_uuid @@ -27,6 +27,8 @@ from ..attribute import BaseAttribute +SchemaProtocol = TypeVar("SchemaProtocol") + # --------------------------------------------------------------------------------------- # Type of Nodes # - Core node, wo/ branch : Branch, MergeRequest, Comment @@ -43,7 +45,7 @@ class Node(BaseNode, metaclass=BaseNodeMeta): @classmethod def __init_subclass_with_meta__( # pylint: disable=arguments-differ cls, _meta=None, default_filter=None, **options - ): + ) -> None: if not _meta: _meta = BaseNodeOptions(cls) @@ -64,6 +66,9 @@ def get_id(self) -> str: raise InitializationError("The node has not been saved yet and doesn't have an id") + def get_updated_at(self) -> Timestamp | None: + return self._updated_at + async def get_hfid(self, db: InfrahubDatabase, include_kind: bool = False) -> Optional[list[str]]: """Return the Human friendly id of the node.""" if not self._schema.human_friendly_id: @@ -162,14 +167,34 @@ def __init__(self, schema: Union[NodeSchema, ProfileSchema], branch: Branch, at: self._attributes: list[str] = [] self._relationships: list[str] = [] + @overload @classmethod async def init( cls, schema: Union[NodeSchema, ProfileSchema, str], db: InfrahubDatabase, + branch: Optional[Union[Branch, str]] = ..., + at: Optional[Union[Timestamp, str]] = ..., + ) -> Self: ... + + @overload + @classmethod + async def init( + cls, + schema: type[SchemaProtocol], + db: InfrahubDatabase, + branch: Optional[Union[Branch, str]] = ..., + at: Optional[Union[Timestamp, str]] = ..., + ) -> SchemaProtocol: ... + + @classmethod + async def init( + cls, + schema: Union[NodeSchema, ProfileSchema, str, type[SchemaProtocol]], + db: InfrahubDatabase, branch: Optional[Union[Branch, str]] = None, at: Optional[Union[Timestamp, str]] = None, - ) -> Self: + ) -> Self | SchemaProtocol: attrs: dict[str, Any] = {} branch = await registry.get_branch(branch=branch, db=db) @@ -179,6 +204,8 @@ async def init( elif isinstance(schema, str): # TODO need to raise a proper exception for this, right now it will raise a generic ValueError attrs["schema"] = db.schema.get(name=schema, branch=branch) + elif hasattr(schema, "_is_runtime_protocol") and getattr(schema, "_is_runtime_protocol"): + attrs["schema"] = db.schema.get(name=schema.__name__, branch=branch) else: raise ValueError(f"Invalid schema provided {type(schema)}, expected NodeSchema or ProfileSchema") diff --git a/backend/infrahub/core/node/base.py b/backend/infrahub/core/node/base.py index 4f8ca06512..16e443a4fd 100644 --- a/backend/infrahub/core/node/base.py +++ b/backend/infrahub/core/node/base.py @@ -14,7 +14,7 @@ class BaseOptions: def __init__(self, class_type): self.class_type = class_type - def freeze(self): + def freeze(self) -> None: self._frozen = True def __setattr__(self, name, value): @@ -36,7 +36,7 @@ class BaseNode(SubclassWithMeta): # return type(class_name, (cls,), {"Meta": options}) @classmethod - def __init_subclass_with_meta__(cls, name=None, description=None, _meta=None, **_kwargs): + def __init_subclass_with_meta__(cls, name=None, description=None, _meta=None, **_kwargs) -> None: assert "_meta" not in cls.__dict__, "Can't assign meta directly" if not _meta: return diff --git a/backend/infrahub/core/node/constraints/attribute_uniqueness.py b/backend/infrahub/core/node/constraints/attribute_uniqueness.py index 0f373a4100..661a2234ea 100644 --- a/backend/infrahub/core/node/constraints/attribute_uniqueness.py +++ b/backend/infrahub/core/node/constraints/attribute_uniqueness.py @@ -45,7 +45,7 @@ async def check(self, node: Node, at: Optional[Timestamp] = None, filters: Optio at=at, ) - if any((n for n in nodes if n.get_id() != node.id)): + if any(n for n in nodes if n.get_id() != node.id): raise ValidationError( {unique_attr.name: f"An object already exist with this value: {unique_attr.name}: {attr.value}"} ) diff --git a/backend/infrahub/core/node/constraints/grouped_uniqueness.py b/backend/infrahub/core/node/constraints/grouped_uniqueness.py index 6e53b7638e..76f6d64c98 100644 --- a/backend/infrahub/core/node/constraints/grouped_uniqueness.py +++ b/backend/infrahub/core/node/constraints/grouped_uniqueness.py @@ -105,7 +105,7 @@ def _check_one_constraint_group( self, schema_attribute_path_values: list[SchemaAttributePathValue], results_index: UniquenessQueryResultsIndex ) -> None: # constraint cannot be violated if this node is missing any values - if any((sapv.value is None for sapv in schema_attribute_path_values)): + if any(sapv.value is None for sapv in schema_attribute_path_values): return matching_node_ids = results_index.get_node_ids_for_value_group(schema_attribute_path_values) diff --git a/backend/infrahub/core/node/permissions.py b/backend/infrahub/core/node/permissions.py new file mode 100644 index 0000000000..6223520f22 --- /dev/null +++ b/backend/infrahub/core/node/permissions.py @@ -0,0 +1,51 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Optional + +from . import Node + +if TYPE_CHECKING: + from infrahub.database import InfrahubDatabase + + +class CoreGlobalPermission(Node): + async def to_graphql( + self, + db: InfrahubDatabase, + fields: Optional[dict] = None, + related_node_ids: Optional[set] = None, + filter_sensitive: bool = False, + ) -> dict: + response = await super().to_graphql( + db, fields=fields, related_node_ids=related_node_ids, filter_sensitive=filter_sensitive + ) + + if fields: + if "identifier" in fields: + response["identifier"] = {"value": f"global:{self.action.value}:{self.decision.value.value}"} # type: ignore[attr-defined] + + return response + + +class CoreObjectPermission(Node): + async def to_graphql( + self, + db: InfrahubDatabase, + fields: Optional[dict] = None, + related_node_ids: Optional[set] = None, + filter_sensitive: bool = False, + ) -> dict: + response = await super().to_graphql( + db, fields=fields, related_node_ids=related_node_ids, filter_sensitive=filter_sensitive + ) + + if fields: + if "identifier" in fields: + response["identifier"] = { + "value": ( + f"object:{self.branch.value}:{self.namespace.value}:{self.name.value}:{self.action.value.value}:" # type: ignore[attr-defined] + f"{self.decision.value.value}" # type: ignore[attr-defined] + ) + } + + return response diff --git a/backend/infrahub/core/node/standard.py b/backend/infrahub/core/node/standard.py index eaf762f852..cb0160b8c9 100644 --- a/backend/infrahub/core/node/standard.py +++ b/backend/infrahub/core/node/standard.py @@ -2,7 +2,7 @@ import inspect from typing import TYPE_CHECKING, Any, Optional, Union, get_args, get_origin -from uuid import UUID # noqa: TCH003 +from uuid import UUID import ujson from infrahub_sdk import UUIDT diff --git a/backend/infrahub/core/protocols.py b/backend/infrahub/core/protocols.py index 3138cc6dc8..a49de3ff52 100644 --- a/backend/infrahub/core/protocols.py +++ b/backend/infrahub/core/protocols.py @@ -66,6 +66,12 @@ class CoreArtifactTarget(CoreNode): artifacts: RelationshipManager +class CoreBasePermission(CoreNode): + decision: Enum + identifier: StringOptional + roles: RelationshipManager + + class CoreCheck(CoreNode): name: StringOptional label: StringOptional @@ -127,6 +133,20 @@ class CoreGroup(CoreNode): children: RelationshipManager +class CoreMenu(CoreNode): + namespace: String + name: String + label: StringOptional + path: StringOptional + description: StringOptional + icon: StringOptional + protected: Boolean + order_weight: Integer + section: Enum + parent: RelationshipManager + children: RelationshipManager + + class CoreProfile(CoreNode): profile_name: String profile_priority: IntegerOptional @@ -194,6 +214,16 @@ class CoreAccount(LineageOwner, LineageSource, CoreGenericAccount): pass +class CoreAccountGroup(CoreGroup): + roles: RelationshipManager + + +class CoreAccountRole(CoreNode): + name: String + groups: RelationshipManager + permissions: RelationshipManager + + class CoreArtifact(CoreTaskTarget): name: String status: Enum @@ -311,6 +341,11 @@ class CoreGeneratorValidator(CoreValidator): definition: RelationshipManager +class CoreGlobalPermission(CoreBasePermission): + name: String + action: Dropdown + + class CoreGraphQLQuery(CoreNode): name: String description: StringOptional @@ -344,6 +379,10 @@ class CoreIPPrefixPool(CoreResourcePool, LineageSource): ip_namespace: RelationshipManager +class CoreMenuItem(CoreMenu): + pass + + class CoreNumberPool(CoreResourcePool, LineageSource): node: String node_attribute: String @@ -351,6 +390,13 @@ class CoreNumberPool(CoreResourcePool, LineageSource): end_range: Integer +class CoreObjectPermission(CoreBasePermission): + branch: String + namespace: String + name: String + action: Enum + + class CoreObjectThread(CoreThread): object_path: String diff --git a/backend/infrahub/core/protocols_base.py b/backend/infrahub/core/protocols_base.py index 31fc37f277..f7a5fb5a90 100644 --- a/backend/infrahub/core/protocols_base.py +++ b/backend/infrahub/core/protocols_base.py @@ -7,11 +7,9 @@ if TYPE_CHECKING: from neo4j import AsyncResult, AsyncSession, AsyncTransaction, Record -# pylint: disable=redefined-builtin - + from infrahub.core.schema.schema_branch import SchemaBranch -@runtime_checkable -class SchemaBranch(Protocol): ... +# pylint: disable=redefined-builtin @runtime_checkable diff --git a/backend/infrahub/core/query/attribute.py b/backend/infrahub/core/query/attribute.py index f85a508ebc..a949610666 100644 --- a/backend/infrahub/core/query/attribute.py +++ b/backend/infrahub/core/query/attribute.py @@ -46,7 +46,7 @@ class AttributeUpdateValueQuery(AttributeQuery): raise_error_if_empty: bool = True - async def query_init(self, db: InfrahubDatabase, **kwargs): + async def query_init(self, db: InfrahubDatabase, **kwargs) -> None: at = self.at or self.attr.at self.params["attr_uuid"] = self.attr.id @@ -95,7 +95,7 @@ def __init__( super().__init__(**kwargs) - async def query_init(self, db: InfrahubDatabase, **kwargs): + async def query_init(self, db: InfrahubDatabase, **kwargs) -> None: at = self.at or self.attr.at self.params["attr_uuid"] = self.attr.id @@ -132,7 +132,7 @@ def __init__( super().__init__(**kwargs) - async def query_init(self, db: InfrahubDatabase, **kwargs): + async def query_init(self, db: InfrahubDatabase, **kwargs) -> None: at = self.at or self.attr.at self.params["attr_uuid"] = self.attr.id @@ -161,7 +161,7 @@ class AttributeGetQuery(AttributeQuery): name = "attribute_get" type: QueryType = QueryType.READ - async def query_init(self, db: InfrahubDatabase, **kwargs): + async def query_init(self, db: InfrahubDatabase, **kwargs) -> None: self.params["attr_uuid"] = self.attr.id self.params["node_uuid"] = self.attr.node.id diff --git a/backend/infrahub/core/query/diff.py b/backend/infrahub/core/query/diff.py index ee991039d6..5d95ead3f1 100644 --- a/backend/infrahub/core/query/diff.py +++ b/backend/infrahub/core/query/diff.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Optional, Union +from typing import TYPE_CHECKING, Any, Optional, Union from infrahub.core.constants import BranchSupportType from infrahub.core.query import Query, QueryResult, QueryType, sort_results_by_time @@ -66,7 +66,7 @@ def __init__( super().__init__(**kwargs) - async def query_init(self, db: InfrahubDatabase, **kwargs): + async def query_init(self, db: InfrahubDatabase, **kwargs) -> None: # TODO need to improve the query to capture an object that has been deleted into the branch # TODO probably also need to consider a node what was merged already @@ -132,7 +132,7 @@ def __init__( super().__init__(**kwargs) - async def query_init(self, db: InfrahubDatabase, **kwargs): + async def query_init(self, db: InfrahubDatabase, **kwargs) -> None: # TODO need to improve the query to capture an object that has been deleted into the branch rels_filters, rels_params = self.branch.get_query_filter_relationships_diff( @@ -195,7 +195,7 @@ def __init__( super().__init__(**kwargs) - async def query_init(self, db: InfrahubDatabase, **kwargs): + async def query_init(self, db: InfrahubDatabase, **kwargs) -> None: where_clause = "" if self.namespaces_include: where_clause += "(src.namespace IN $namespaces_include OR dst.namespace IN $namespaces_include) AND " @@ -257,7 +257,7 @@ class DiffRelationshipPropertyQuery(DiffQuery): name: str = "diff_relationship_property" type: QueryType = QueryType.READ - async def query_init(self, db: InfrahubDatabase, **kwargs): + async def query_init(self, db: InfrahubDatabase, **kwargs) -> None: rels_filter, rels_params = self.branch.get_query_filter_relationships_range( rel_labels=["r"], start_time=self.diff_from, end_time=self.diff_to ) @@ -307,7 +307,7 @@ def __init__(self, ids: list[str], diff_from: str, diff_to: str, account=None, * super().__init__(order_by=["a.name"], **kwargs) - async def query_init(self, db: InfrahubDatabase, **kwargs): + async def query_init(self, db: InfrahubDatabase, **kwargs) -> None: self.params["ids"] = self.ids rels_filter, rels_params = self.branch.get_query_filter_relationships_range( @@ -358,7 +358,7 @@ def __init__( super().__init__(**kwargs) - async def query_init(self, db: InfrahubDatabase, **kwargs): + async def query_init(self, db: InfrahubDatabase, **kwargs) -> None: self.params["ids"] = self.ids rels_filter, rels_params = self.branch.get_query_filter_relationships( @@ -412,7 +412,7 @@ def __init__( super().__init__(**kwargs) - async def query_init(self, db: InfrahubDatabase, **kwargs): + async def query_init(self, db: InfrahubDatabase, **kwargs) -> None: self.params["ids"] = self.ids rels_filter, rels_params = self.branch.get_query_filter_relationships_range( @@ -464,7 +464,7 @@ def __init__( self.diff_to = diff_to super().__init__(**kwargs) - async def query_init(self, db: InfrahubDatabase, **kwargs): + async def query_init(self, db: InfrahubDatabase, **kwargs) -> None: self.params = { "from_time": self.diff_from.to_string(), "to_time": self.diff_to.to_string(), @@ -511,8 +511,7 @@ def __init__( branch_support: list[BranchSupportType] | None = None, current_node_field_specifiers: list[tuple[str, str]] | None = None, new_node_field_specifiers: list[tuple[str, str]] | None = None, - *args, - **kwargs, + **kwargs: Any, ): self.base_branch = base_branch self.diff_branch_from_time = diff_branch_from_time @@ -520,9 +519,9 @@ def __init__( self.current_node_field_specifiers = current_node_field_specifiers self.new_node_field_specifiers = new_node_field_specifiers - super().__init__(*args, **kwargs) + super().__init__(**kwargs) - async def query_init(self, db: InfrahubDatabase, **kwargs): + async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: from_str = self.diff_from.to_string() self.params.update( { diff --git a/backend/infrahub/core/query/ipam.py b/backend/infrahub/core/query/ipam.py index 1c69b61c85..ff73615f4b 100644 --- a/backend/infrahub/core/query/ipam.py +++ b/backend/infrahub/core/query/ipam.py @@ -60,7 +60,7 @@ def __init__( super().__init__(**kwargs) - async def query_init(self, db: InfrahubDatabase, **kwargs): + async def query_init(self, db: InfrahubDatabase, **kwargs) -> None: self.params["ns_id"] = self.namespace_id prefix_bin = convert_ip_to_binary_str(self.obj)[: self.obj.prefixlen] @@ -124,7 +124,7 @@ async def query_init(self, db: InfrahubDatabase, **kwargs): self.return_labels = ["prefixes_to_check[0] as pfx", "prefixes_to_check[1] as av"] self.order_by = ["av.binary_address"] - def get_subnets(self): + def get_subnets(self) -> list[IPPrefixData]: """Return a list of all subnets fitting in the prefix.""" subnets: list[IPPrefixData] = [] @@ -151,7 +151,7 @@ def __init__( super().__init__(**kwargs) - async def query_init(self, db: InfrahubDatabase, **kwargs): + async def query_init(self, db: InfrahubDatabase, **kwargs) -> None: self.params["ns_id"] = self.namespace_id prefix_bin = convert_ip_to_binary_str(self.obj)[: self.obj.prefixlen] @@ -197,7 +197,7 @@ async def query_init(self, db: InfrahubDatabase, **kwargs): self.return_labels = ["addr", "av"] self.order_by = ["av.binary_address"] - def get_addresses(self): + def get_addresses(self) -> list[IPAddressData]: """Return a list of all addresses fitting in the prefix.""" addresses: list[IPAddressData] = [] @@ -249,7 +249,7 @@ def __init__(self, ip_prefixes: list[str], **kwargs): self.ip_prefixes = ip_prefixes super().__init__(**kwargs) - async def query_init(self, db: InfrahubDatabase, **kwargs): + async def query_init(self, db: InfrahubDatabase, **kwargs) -> None: self.params["ids"] = [p.get_id() for p in self.ip_prefixes] self.params["time_at"] = self.at.to_string() @@ -323,7 +323,7 @@ def __init__( self.namespace_id = _get_namespace_id(namespace) super().__init__(**kwargs) - async def query_init(self, db: InfrahubDatabase, **kwargs): + async def query_init(self, db: InfrahubDatabase, **kwargs) -> None: branch_filter, branch_params = self.branch.get_query_filter_path(at=self.at.to_string()) self.params.update(branch_params) self.params["namespace_kind"] = InfrahubKind.IPNAMESPACE diff --git a/backend/infrahub/core/query/node.py b/backend/infrahub/core/query/node.py index 3b607f4a5c..a990458f1c 100644 --- a/backend/infrahub/core/query/node.py +++ b/backend/infrahub/core/query/node.py @@ -122,7 +122,7 @@ class NodeCreateAllQuery(NodeQuery): raise_error_if_empty: bool = True - async def query_init(self, db: InfrahubDatabase, **kwargs): + async def query_init(self, db: InfrahubDatabase, **kwargs) -> None: at = self.at or self.node._at self.params["uuid"] = self.node.id self.params["branch"] = self.branch.name @@ -355,7 +355,7 @@ class NodeDeleteQuery(NodeQuery): raise_error_if_empty: bool = True - async def query_init(self, db: InfrahubDatabase, **kwargs): + async def query_init(self, db: InfrahubDatabase, **kwargs) -> None: self.params["uuid"] = self.node_id self.params["branch"] = self.branch.name self.params["branch_level"] = self.branch.hierarchy_level @@ -385,7 +385,7 @@ def __init__( self.node_id = node_id super().__init__(**kwargs) - async def query_init(self, db: InfrahubDatabase, **kwargs): + async def query_init(self, db: InfrahubDatabase, **kwargs) -> None: self.params["uuid"] = self.node_id query = """ @@ -424,7 +424,7 @@ def __init__( super().__init__(order_by=["n.uuid", "a.name"], **kwargs) - async def query_init(self, db: InfrahubDatabase, **kwargs): + async def query_init(self, db: InfrahubDatabase, **kwargs) -> None: self.params["ids"] = self.ids branch_filter, branch_params = self.branch.get_query_filter_path( @@ -563,7 +563,7 @@ def __init__(self, ids: list[str], **kwargs): super().__init__(**kwargs) - async def query_init(self, db: InfrahubDatabase, **kwargs): + async def query_init(self, db: InfrahubDatabase, **kwargs) -> None: self.params["ids"] = self.ids rels_filter, rels_params = self.branch.get_query_filter_path(at=self.at, branch_agnostic=self.branch_agnostic) @@ -1108,7 +1108,7 @@ def _get_field_requirements(self) -> list[FieldAttributeRequirement]: continue field = self.schema.get_field(field_name, raise_on_error=False) for field_attr_name, field_attr_value in attr_filters.items(): - field_requirements_map[(field_name, field_attr_name)] = FieldAttributeRequirement( + field_requirements_map[field_name, field_attr_name] = FieldAttributeRequirement( field_name=field_name, field=field, field_attr_name=field_attr_name, @@ -1138,7 +1138,7 @@ def _get_field_requirements(self) -> list[FieldAttributeRequirement]: ), ) field_req.types.append(FieldAttributeRequirementType.ORDER) - field_requirements_map[(order_by_field_name, order_by_attr_property_name)] = field_req + field_requirements_map[order_by_field_name, order_by_attr_property_name] = field_req index += 1 return list(field_requirements_map.values()) diff --git a/backend/infrahub/core/query/relationship.py b/backend/infrahub/core/query/relationship.py index b83f43a1a1..daad6eeeeb 100644 --- a/backend/infrahub/core/query/relationship.py +++ b/backend/infrahub/core/query/relationship.py @@ -37,7 +37,7 @@ class RelData: status: str @classmethod - def from_db(cls, obj: Neo4jRelationship): + def from_db(cls, obj: Neo4jRelationship) -> RelData: return cls(db_id=obj.element_id, branch=obj.get("branch"), type=obj.type, status=obj.get("status")) @@ -204,7 +204,7 @@ def __init__( super().__init__(destination=destination, destination_id=destination_id, **kwargs) - async def query_init(self, db: InfrahubDatabase, **kwargs): + async def query_init(self, db: InfrahubDatabase, **kwargs) -> None: self.params["source_id"] = self.source_id self.params["destination_id"] = self.destination_id self.params["name"] = self.schema.identifier @@ -249,22 +249,22 @@ async def query_init(self, db: InfrahubDatabase, **kwargs): self.return_labels = ["s", "d", "rl", "r1", "r2", "r3", "r4"] self.query_add_all_node_property_create() - def query_add_all_node_property_match(self): + def query_add_all_node_property_match(self) -> None: for prop_name in self.rel._node_properties: if hasattr(self.rel, f"{prop_name}_id") and getattr(self.rel, f"{prop_name}_id"): self.query_add_node_property_match(name=prop_name) - def query_add_node_property_match(self, name: str): + def query_add_node_property_match(self, name: str) -> None: self.add_to_query("MATCH (%s { uuid: $prop_%s_id })" % (name, name)) self.params[f"prop_{name}_id"] = getattr(self.rel, f"{name}_id") self.return_labels.append(name) - def query_add_all_node_property_create(self): + def query_add_all_node_property_create(self) -> None: for prop_name in self.rel._node_properties: if hasattr(self.rel, f"{prop_name}_id") and getattr(self.rel, f"{prop_name}_id"): self.query_add_node_property_create(name=prop_name) - def query_add_node_property_create(self, name: str): + def query_add_node_property_create(self, name: str) -> None: query = """ CREATE (rl)-[:HAS_%s { branch: $branch, branch_level: $branch_level, status: "active", from: $at }]->(%s) """ % ( @@ -290,7 +290,7 @@ def __init__( super().__init__(**kwargs) - async def query_init(self, db: InfrahubDatabase, **kwargs): + async def query_init(self, db: InfrahubDatabase, **kwargs) -> None: self.params["rel_node_id"] = self.data.rel_node_id self.params["branch"] = self.branch.name self.params["branch_level"] = self.branch.hierarchy_level @@ -307,32 +307,32 @@ async def query_init(self, db: InfrahubDatabase, **kwargs): self.query_add_all_flag_property_create() self.query_add_all_node_property_create() - def query_add_all_flag_property_merge(self): + def query_add_all_flag_property_merge(self) -> None: for prop_name in self.rel._flag_properties: if prop_name in self.properties_to_update: self.query_add_flag_property_merge(name=prop_name) - def query_add_flag_property_merge(self, name: str): + def query_add_flag_property_merge(self, name: str) -> None: self.add_to_query("MERGE (prop_%s:Boolean { value: $prop_%s })" % (name, name)) self.params[f"prop_{name}"] = getattr(self.rel, name) self.return_labels.append(f"prop_{name}") - def query_add_all_node_property_merge(self): + def query_add_all_node_property_merge(self) -> None: for prop_name in self.rel._node_properties: if prop_name in self.properties_to_update: self.query_add_node_property_merge(name=prop_name) - def query_add_node_property_merge(self, name: str): + def query_add_node_property_merge(self, name: str) -> None: self.add_to_query("MERGE (prop_%s:Node { uuid: $prop_%s })" % (name, name)) self.params[f"prop_{name}"] = getattr(self.rel, f"{name}_id") self.return_labels.append(f"prop_{name}") - def query_add_all_flag_property_create(self): + def query_add_all_flag_property_create(self) -> None: for prop_name in self.rel._flag_properties: if prop_name in self.properties_to_update: self.query_add_flag_property_create(name=prop_name) - def query_add_flag_property_create(self, name: str): + def query_add_flag_property_create(self, name: str) -> None: query = """ CREATE (rl)-[:%s { branch: $branch, branch_level: $branch_level, status: "active", from: $at }]->(prop_%s) """ % ( @@ -341,12 +341,12 @@ def query_add_flag_property_create(self, name: str): ) self.add_to_query(query) - def query_add_all_node_property_create(self): + def query_add_all_node_property_create(self) -> None: for prop_name in self.rel._node_properties: if prop_name in self.properties_to_update: self.query_add_node_property_create(name=prop_name) - def query_add_node_property_create(self, name: str): + def query_add_node_property_create(self, name: str) -> None: query = """ CREATE (rl)-[:%s { branch: $branch, branch_level: $branch_level, status: "active", from: $at }]->(prop_%s) """ % ( @@ -369,7 +369,7 @@ def __init__( self.data = data super().__init__(**kwargs) - async def query_init(self, db: InfrahubDatabase, **kwargs): + async def query_init(self, db: InfrahubDatabase, **kwargs) -> None: self.params["source_id"] = self.source_id self.params["destination_id"] = self.data.peer_id self.params["rel_node_id"] = self.data.rel_node_id @@ -434,7 +434,7 @@ def __init__(self, **kwargs): if inspect.isclass(self.rel): raise TypeError("An instance of Relationship must be provided to RelationshipDeleteQuery") - async def query_init(self, db: InfrahubDatabase, **kwargs): + async def query_init(self, db: InfrahubDatabase, **kwargs) -> None: self.params["source_id"] = self.source_id self.params["destination_id"] = self.destination_id self.params["rel_id"] = self.rel.id @@ -533,7 +533,7 @@ def __init__( super().__init__(**kwargs) - async def query_init(self, db: InfrahubDatabase, **kwargs): # pylint: disable=too-many-statements + async def query_init(self, db: InfrahubDatabase, **kwargs) -> None: # pylint: disable=too-many-statements branch_filter, branch_params = self.branch.get_query_filter_path( at=self.at, branch_agnostic=self.branch_agnostic ) @@ -733,7 +733,7 @@ class RelationshipGetQuery(RelationshipQuery): type: QueryType = QueryType.READ - async def query_init(self, db: InfrahubDatabase, **kwargs): + async def query_init(self, db: InfrahubDatabase, **kwargs) -> None: self.params["source_id"] = self.source_id self.params["destination_id"] = self.destination_id self.params["name"] = self.schema.identifier @@ -860,7 +860,7 @@ def __init__( super().__init__(**kwargs) - async def query_init(self, db: InfrahubDatabase, **kwargs): + async def query_init(self, db: InfrahubDatabase, **kwargs) -> None: branch_filter, branch_params = self.branch.get_query_filter_path(at=self.at.to_string()) self.params.update(branch_params) diff --git a/backend/infrahub/core/registry.py b/backend/infrahub/core/registry.py index 774e9af466..8fd3b608f9 100644 --- a/backend/infrahub/core/registry.py +++ b/backend/infrahub/core/registry.py @@ -15,10 +15,11 @@ from infrahub.core.branch import Branch from infrahub.core.manager import NodeManager from infrahub.core.schema import MainSchemaTypes, NodeSchema - from infrahub.core.schema_manager import SchemaManager + from infrahub.core.schema.manager import SchemaManager from infrahub.database import InfrahubDatabase from infrahub.graphql.mutations.attribute import BaseAttributeCreate, BaseAttributeUpdate from infrahub.graphql.types import InfrahubObject + from infrahub.permissions import PermissionBackend from infrahub.storage import InfrahubObjectStorage from infrahub.types import InfrahubDataType @@ -34,7 +35,7 @@ class Registry: _default_branch: Optional[str] = None _default_ipnamespace: Optional[str] = None _schema: Optional[SchemaManager] = None - default_graphql_type: dict[str, InfrahubObject] = field(default_factory=dict) + default_graphql_type: dict[str, InfrahubObject | type[BaseAttribute]] = field(default_factory=dict) graphql_type: dict = field(default_factory=lambda: defaultdict(dict)) data_type: dict[str, type[InfrahubDataType]] = field(default_factory=dict) input_type: dict[str, Union[BaseAttributeCreate, BaseAttributeUpdate]] = field(default_factory=dict) @@ -45,6 +46,7 @@ class Registry: _branch_object: Optional[type[Branch]] = None _manager: Optional[type[NodeManager]] = None _storage: Optional[InfrahubObjectStorage] = None + permission_backends: list[PermissionBackend] = field(default_factory=list) @property def branch_object(self) -> type[Branch]: diff --git a/backend/infrahub/core/relationship/__init__.py b/backend/infrahub/core/relationship/__init__.py index f2e9663a75..c14c2c2381 100644 --- a/backend/infrahub/core/relationship/__init__.py +++ b/backend/infrahub/core/relationship/__init__.py @@ -1,3 +1,3 @@ from .model import Relationship, RelationshipCreateData, RelationshipManager -__all__ = ["Relationship", "RelationshipManager", "RelationshipCreateData"] +__all__ = ["Relationship", "RelationshipCreateData", "RelationshipManager"] diff --git a/backend/infrahub/core/relationship/model.py b/backend/infrahub/core/relationship/model.py index 93c7aaed75..b6d63df66c 100644 --- a/backend/infrahub/core/relationship/model.py +++ b/backend/infrahub/core/relationship/model.py @@ -985,7 +985,7 @@ async def update( # pylint: disable=too-many-branches self._relationships.append(previous_relationships[str(item_id)]) continue - if isinstance(item, type(None)): + if item is None: if previous_relationships: for rel in previous_relationships.values(): await rel.delete(db=db) diff --git a/backend/infrahub/core/schema/__init__.py b/backend/infrahub/core/schema/__init__.py index 5428791b29..ee64f90510 100644 --- a/backend/infrahub/core/schema/__init__.py +++ b/backend/infrahub/core/schema/__init__.py @@ -7,13 +7,13 @@ from infrahub.core.constants import RESTRICTED_NAMESPACES from infrahub.core.models import HashableModel +from infrahub.exceptions import SchemaNotFoundError from .attribute_schema import AttributeSchema from .basenode_schema import AttributePathParsingError, BaseNodeSchema, SchemaAttributePath, SchemaAttributePathValue from .definitions.core import core_models from .definitions.internal import internal from .dropdown import DropdownChoice -from .filter import FilterSchema from .generic_schema import GenericSchema from .node_schema import NodeSchema from .profile_schema import ProfileSchema @@ -58,6 +58,15 @@ def has_schema(cls, values: dict[str, Any], name: str) -> bool: return True + def get(self, name: str) -> Union[NodeSchema, GenericSchema]: + """Check if a schema exist locally as a node or as a generic.""" + + for item in self.nodes + self.generics: + if item.kind == name: + return item + + raise SchemaNotFoundError(branch_name="undefined", identifier=name) + def validate_namespaces(self) -> list[str]: models = self.nodes + self.generics errors: list[str] = [] @@ -81,19 +90,18 @@ def generate_uuid(self) -> None: internal_schema = internal.to_dict() __all__ = [ - "core_models", - "internal_schema", "AttributePathParsingError", "AttributeSchema", "BaseNodeSchema", "DropdownChoice", - "FilterSchema", - "NodeSchema", "GenericSchema", + "MainSchemaTypes", + "NodeSchema", "ProfileSchema", "RelationshipSchema", "SchemaAttributePath", "SchemaAttributePathValue", "SchemaRoot", - "MainSchemaTypes", + "core_models", + "internal_schema", ] diff --git a/backend/infrahub/core/schema/basenode_schema.py b/backend/infrahub/core/schema/basenode_schema.py index b5fe047bd5..a63fb792ee 100644 --- a/backend/infrahub/core/schema/basenode_schema.py +++ b/backend/infrahub/core/schema/basenode_schema.py @@ -12,16 +12,15 @@ from infrahub.core.constants import RelationshipKind from infrahub.core.models import HashableModelDiff -from .attribute_schema import AttributeSchema # noqa: TCH001 -from .filter import FilterSchema # noqa: TCH001 +from .attribute_schema import AttributeSchema from .generated.base_node_schema import GeneratedBaseNodeSchema -from .relationship_schema import RelationshipSchema # noqa: TCH001 +from .relationship_schema import RelationshipSchema if TYPE_CHECKING: from typing_extensions import Self from infrahub.core.schema import GenericSchema, NodeSchema - from infrahub.core.schema_manager import SchemaBranch + from infrahub.core.schema.schema_branch import SchemaBranch # pylint: disable=redefined-builtin @@ -30,7 +29,7 @@ class BaseNodeSchema(GeneratedBaseNodeSchema): # pylint: disable=too-many-public-methods - _exclude_from_hash: list[str] = ["attributes", "relationships", "filters"] + _exclude_from_hash: list[str] = ["attributes", "relationships"] _sort_by: list[str] = ["namespace", "name"] @property @@ -77,9 +76,6 @@ def get_hash(self, display_values: bool = False) -> str: for rel_name in sorted(self.relationship_names): md5hash.update(self.get_relationship(name=rel_name).get_hash(display_values=display_values).encode()) - for filter_name in sorted(self.filter_names): - md5hash.update(self.get_filter(name=filter_name).get_hash(display_values=display_values).encode()) - return md5hash.hexdigest() def diff(self, other: Self) -> HashableModelDiff: @@ -114,7 +110,7 @@ def _diff_element( other: Self, get_func: Callable, get_map_func: Callable, - obj_type: type[Union[AttributeSchema, RelationshipSchema, FilterSchema]], + obj_type: type[Union[AttributeSchema, RelationshipSchema]], ) -> HashableModelDiff: """The goal of this function is to reduce the amount of code duplicated between Attribute and Relationship to calculate a diff The logic is the same for both, except that the functions we are using to access these objects are differents @@ -224,22 +220,6 @@ def get_relationship_by_id(self, id: str) -> RelationshipSchema: raise ValueError(f"Unable to find the relationship with the ID: {id}") - @overload - def get_filter(self, name: str, raise_on_error: Literal[True] = True) -> FilterSchema: ... - - @overload - def get_filter(self, name: str, raise_on_error: Literal[False] = False) -> Optional[FilterSchema]: ... - - def get_filter(self, name: str, raise_on_error: bool = True) -> Optional[FilterSchema]: - for item in self.filters: - if item.name == name: - return item - - if not raise_on_error: - return None - - raise ValueError(f"Unable to find the filter {name}") - def get_relationship_or_none(self, name: str) -> Optional[RelationshipSchema]: for item in self.relationships: if item.name == name: @@ -295,12 +275,6 @@ def has_parent_relationship(self) -> bool: return True return False - def get_filter_name_id_map(self) -> dict[str, str]: - name_id_map = {} - for filter in self.filters: - name_id_map[filter.name] = filter.id - return name_id_map - @property def valid_input_names(self) -> list[str]: return self.attribute_names + self.relationship_names + NODE_METADATA_ATTRIBUTES @@ -317,10 +291,6 @@ def attribute_names(self) -> list[str]: def relationship_names(self) -> list[str]: return [item.name for item in self.relationships] - @property - def filter_names(self) -> list[str]: - return [item.name for item in self.filters] - @property def mandatory_input_names(self) -> list[str]: return self.mandatory_attribute_names + self.mandatory_relationship_names diff --git a/backend/infrahub/core/schema/constants.py b/backend/infrahub/core/schema/constants.py new file mode 100644 index 0000000000..76c4ee8950 --- /dev/null +++ b/backend/infrahub/core/schema/constants.py @@ -0,0 +1,14 @@ +from pydantic import BaseModel + +from . import internal_schema + +INTERNAL_SCHEMA_NODE_KINDS = [node["namespace"] + node["name"] for node in internal_schema["nodes"]] + +SUPPORTED_SCHEMA_EXTENSION_TYPE = ["NodeExtensionSchema"] + +IGNORE_FOR_NODE = {"id", "state", "filters", "relationships", "attributes"} + + +class SchemaNamespace(BaseModel): + name: str + user_editable: bool diff --git a/backend/infrahub/core/schema/definitions/core.py b/backend/infrahub/core/schema/definitions/core.py index 91502d2759..c839c1ccf6 100644 --- a/backend/infrahub/core/schema/definitions/core.py +++ b/backend/infrahub/core/schema/definitions/core.py @@ -3,6 +3,7 @@ from infrahub.core.constants import ( DEFAULT_KIND_MAX_LENGTH, DEFAULT_KIND_MIN_LENGTH, + NAMESPACE_REGEX, AccountRole, AccountStatus, AccountType, @@ -12,7 +13,10 @@ BranchSupportType, ContentType, GeneratorInstanceStatus, + GlobalPermissions, InfrahubKind, + PermissionAction, + PermissionDecision, ProposedChangeState, RelationshipDeleteBehavior, RepositoryInternalStatus, @@ -53,6 +57,44 @@ ], } +# ----------------------------------------------- +# Menu Items +# ----------------------------------------------- +generic_menu_item: dict[str, Any] = { + "name": "Menu", + "namespace": "Core", + "include_in_menu": False, + "description": "Base node for the menu", + "label": "Menu Item", + "hierarchical": True, + "uniqueness_constraints": [["namespace__value", "name__value"]], + "attributes": [ + {"name": "namespace", "kind": "Text", "regex": NAMESPACE_REGEX, "order_weight": 1000}, + {"name": "name", "kind": "Text", "order_weight": 1000}, + {"name": "label", "kind": "Text", "optional": True, "order_weight": 2000}, + {"name": "path", "kind": "Text", "optional": True, "order_weight": 2500}, + {"name": "description", "kind": "Text", "optional": True, "order_weight": 3000}, + {"name": "icon", "kind": "Text", "optional": True, "order_weight": 4000}, + {"name": "protected", "kind": "Boolean", "default_value": False, "read_only": True, "order_weight": 5000}, + {"name": "order_weight", "kind": "Number", "default_value": 2000, "order_weight": 6000}, + { + "name": "section", + "kind": "Text", + "enum": ["object", "internal"], + "default_value": "object", + "order_weight": 7000, + }, + ], +} + +menu_item: dict[str, Any] = { + "name": "MenuItem", + "namespace": "Core", + "include_in_menu": False, + "description": "Menu Item", + "label": "Menu Item", + "inherit_from": ["CoreMenu"], +} core_models: dict[str, Any] = { "generics": [ @@ -877,7 +919,42 @@ }, ], "relationships": [ - {"name": "tokens", "peer": InfrahubKind.ACCOUNTTOKEN, "optional": True, "cardinality": "many"}, + {"name": "tokens", "peer": InfrahubKind.ACCOUNTTOKEN, "optional": True, "cardinality": "many"} + ], + }, + { + "name": "BasePermission", + "namespace": "Core", + "description": "A permission grants right to an account", + "label": "Base permission", + "icon": "mdi:user-key", + "include_in_menu": False, + "generate_profile": False, + "attributes": [ + { + "name": "decision", + "kind": "Text", + "enum": PermissionDecision.available_types(), + "default_value": PermissionDecision.ALLOW.value, + "order_weight": 5000, + }, + { + "name": "identifier", + "kind": "Text", + "read_only": True, + "optional": True, + "allow_override": AllowOverrideType.NONE, + }, + ], + "relationships": [ + { + "name": "roles", + "peer": InfrahubKind.ACCOUNTROLE, + "optional": True, + "identifier": "role__permissions", + "cardinality": "many", + "kind": "Attribute", + } ], }, { @@ -900,8 +977,10 @@ {"name": "description", "kind": "Text", "optional": True, "order_weight": 3000}, ], }, + generic_menu_item, ], "nodes": [ + menu_item, { "name": "StandardGroup", "namespace": "Core", @@ -2080,5 +2159,105 @@ }, ], }, + { + "name": "GlobalPermission", + "namespace": "Core", + "description": "A permission that grants global rights to perform actions in Infrahub", + "label": "Global permission", + "include_in_menu": False, + "order_by": ["name__value", "action__value"], + "display_labels": ["name__value"], + "generate_profile": False, + "inherit_from": [InfrahubKind.BASEPERMISSION], + "branch": BranchSupportType.AGNOSTIC.value, + "attributes": [ + {"name": "name", "kind": "Text", "unique": True, "order_weight": 1000}, + { + "name": "action", + "kind": "Dropdown", + "choices": [{"name": permission.value} for permission in GlobalPermissions], + "order_weight": 2000, + }, + ], + }, + { + "name": "ObjectPermission", + "namespace": "Core", + "description": "A permission that grants rights to perform actions on objects", + "label": "Object permission", + "include_in_menu": False, + "order_by": ["branch__value", "namespace__value", "name__value", "action__value", "decision__value"], + "display_labels": ["branch__value", "namespace__value", "name__value", "action__value", "decision__value"], + "uniqueness_constraints": [ + ["branch__value", "namespace__value", "name__value", "action__value", "decision__value"] + ], + "generate_profile": False, + "inherit_from": [InfrahubKind.BASEPERMISSION], + "attributes": [ + {"name": "branch", "kind": "Text", "order_weight": 1000}, + {"name": "namespace", "kind": "Text", "order_weight": 2000}, + {"name": "name", "kind": "Text", "order_weight": 3000}, + { + "name": "action", + "kind": "Text", + "enum": PermissionAction.available_types(), + "default_value": PermissionAction.ANY.value, + "order_weight": 4000, + }, + ], + }, + { + "name": "AccountRole", + "namespace": "Core", + "description": "A role defines a set of permissions to grant to a group of accounts", + "label": "Account role", + "icon": "mdi:user-badge", + "include_in_menu": False, + "order_by": ["name__value"], + "display_labels": ["name__value"], + "generate_profile": False, + "attributes": [{"name": "name", "kind": "Text", "unique": True}], + "relationships": [ + { + "name": "groups", + "peer": InfrahubKind.ACCOUNTGROUP, + "optional": True, + "identifier": "role__accountgroups", + "cardinality": "many", + "kind": "Attribute", + }, + { + "name": "permissions", + "peer": InfrahubKind.BASEPERMISSION, + "optional": True, + "identifier": "role__permissions", + "cardinality": "many", + "kind": "Attribute", + }, + ], + }, + { + "name": "AccountGroup", + "namespace": "Core", + "description": "A group of users to manage common permissions", + "label": "Account group", + "icon": "mdi:account-group", + "include_in_menu": False, + "order_by": ["name__value"], + "display_labels": ["name__value"], + "generate_profile": False, + "inherit_from": [InfrahubKind.GENERICGROUP], + "branch": BranchSupportType.AGNOSTIC.value, + "relationships": [ + { + "name": "roles", + "peer": InfrahubKind.ACCOUNTROLE, + "optional": True, + "identifier": "role__accountgroups", + "cardinality": "many", + "kind": "Attribute", + } + ], + }, ], } diff --git a/backend/infrahub/core/schema/definitions/internal.py b/backend/infrahub/core/schema/definitions/internal.py index e6c80564dc..f7c065fe15 100644 --- a/backend/infrahub/core/schema/definitions/internal.py +++ b/backend/infrahub/core/schema/definitions/internal.py @@ -30,10 +30,9 @@ RelationshipKind, UpdateSupport, ) -from infrahub.core.schema.attribute_schema import AttributeSchema # noqa: TCH001 +from infrahub.core.schema.attribute_schema import AttributeSchema from infrahub.core.schema.dropdown import DropdownChoice -from infrahub.core.schema.filter import FilterSchema -from infrahub.core.schema.relationship_schema import RelationshipSchema # noqa: TCH001 +from infrahub.core.schema.relationship_schema import RelationshipSchema from infrahub.types import ATTRIBUTE_KIND_LABELS @@ -181,7 +180,7 @@ def to_dict(self) -> dict[str, Any]: "attributes": [ attribute.to_dict() for attribute in self.attributes - if attribute.name not in ["id", "filters", "attributes", "relationships"] + if attribute.name not in ["id", "attributes", "relationships"] ], "relationships": [relationship.to_dict() for relationship in self.relationships], "display_labels": self.display_labels, @@ -346,15 +345,6 @@ def to_dict(self) -> dict[str, Any]: optional=True, extra={"update": UpdateSupport.NOT_APPLICABLE}, ), - SchemaAttribute( - name="filters", - kind="List", - internal_kind=FilterSchema, - description="Node filters", - default_factory="list", - optional=True, - extra={"update": UpdateSupport.NOT_APPLICABLE}, - ), SchemaAttribute( name="attributes", kind="List", @@ -776,15 +766,6 @@ def to_dict(self) -> dict[str, Any]: optional=True, extra={"update": UpdateSupport.NOT_APPLICABLE}, ), - SchemaAttribute( - name="filters", - kind="List", - internal_kind=FilterSchema, - description="Relationship filters", - default_factory="list", - optional=True, - extra={"update": UpdateSupport.NOT_APPLICABLE}, - ), SchemaAttribute( name="on_delete", kind="Text", diff --git a/backend/infrahub/core/schema/filter.py b/backend/infrahub/core/schema/filter.py deleted file mode 100644 index 0b56d3caa2..0000000000 --- a/backend/infrahub/core/schema/filter.py +++ /dev/null @@ -1,14 +0,0 @@ -from typing import Optional - -from infrahub.core.constants import FilterSchemaKind -from infrahub.core.models import HashableModel - - -class FilterSchema(HashableModel): - name: str - kind: FilterSchemaKind - enum: Optional[list] = None - object_kind: Optional[str] = None - description: Optional[str] = None - - _sort_by: list[str] = ["name"] diff --git a/backend/infrahub/core/schema/generated/base_node_schema.py b/backend/infrahub/core/schema/generated/base_node_schema.py index c9691af782..91f86ac8a6 100644 --- a/backend/infrahub/core/schema/generated/base_node_schema.py +++ b/backend/infrahub/core/schema/generated/base_node_schema.py @@ -9,7 +9,6 @@ from infrahub.core.constants import BranchSupportType, HashableModelState from infrahub.core.models import HashableModel from infrahub.core.schema.attribute_schema import AttributeSchema # noqa: TCH001 -from infrahub.core.schema.filter import FilterSchema # noqa: TCH001 from infrahub.core.schema.relationship_schema import RelationshipSchema # noqa: TCH001 @@ -101,9 +100,6 @@ class GeneratedBaseNodeSchema(HashableModel): description="Expected state of the node/generic after loading the schema", json_schema_extra={"update": "not_applicable"}, ) - filters: list[FilterSchema] = Field( - default_factory=list, description="Node filters", json_schema_extra={"update": "not_applicable"} - ) attributes: list[AttributeSchema] = Field( default_factory=list, description="Node attributes", json_schema_extra={"update": "not_applicable"} ) diff --git a/backend/infrahub/core/schema/generated/relationship_schema.py b/backend/infrahub/core/schema/generated/relationship_schema.py index 53d45f050c..c36e2f01ab 100644 --- a/backend/infrahub/core/schema/generated/relationship_schema.py +++ b/backend/infrahub/core/schema/generated/relationship_schema.py @@ -15,10 +15,7 @@ RelationshipDirection, RelationshipKind, ) - -# noqa: TCH001 from infrahub.core.models import HashableModel -from infrahub.core.schema.filter import FilterSchema # noqa: TCH001 class GeneratedRelationshipSchema(HashableModel): @@ -113,9 +110,6 @@ class GeneratedRelationshipSchema(HashableModel): description="Expected state of the relationship after loading the schema", json_schema_extra={"update": "not_applicable"}, ) - filters: list[FilterSchema] = Field( - default_factory=list, description="Relationship filters", json_schema_extra={"update": "not_applicable"} - ) on_delete: Optional[RelationshipDeleteBehavior] = Field( default=None, description="Default is no-action. If cascade, related node(s) are deleted when this node is deleted.", diff --git a/backend/infrahub/core/schema/manager.py b/backend/infrahub/core/schema/manager.py new file mode 100644 index 0000000000..ade133a361 --- /dev/null +++ b/backend/infrahub/core/schema/manager.py @@ -0,0 +1,673 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Optional, Union + +from infrahub import lock +from infrahub.core.constants import ( + InfrahubKind, +) +from infrahub.core.manager import NodeManager +from infrahub.core.models import ( + HashableModelDiff, + SchemaBranchDiff, + SchemaDiff, +) +from infrahub.core.node import Node +from infrahub.core.registry import registry +from infrahub.core.schema import ( + AttributeSchema, + GenericSchema, + MainSchemaTypes, + NodeSchema, + RelationshipSchema, + SchemaRoot, +) +from infrahub.core.utils import parse_node_kind +from infrahub.exceptions import SchemaNotFoundError +from infrahub.log import get_logger + +from .constants import IGNORE_FOR_NODE +from .schema_branch import SchemaBranch + +log = get_logger() + +if TYPE_CHECKING: + from infrahub.core.branch import Branch + from infrahub.core.timestamp import Timestamp + from infrahub.database import InfrahubDatabase + + +# pylint: disable=too-many-public-methods +class SchemaManager(NodeManager): + def __init__(self) -> None: + self._cache: dict[int, Any] = {} + self._branches: dict[str, SchemaBranch] = {} + + def _get_from_cache(self, key: int) -> Any: + return self._cache[key] + + def set(self, name: str, schema: Union[NodeSchema, GenericSchema], branch: Optional[str] = None) -> int: + branch = branch or registry.default_branch + + if branch not in self._branches: + self._branches[branch] = SchemaBranch(cache=self._cache, name=branch) + + self._branches[branch].set(name=name, schema=schema) + + return hash(self._branches[branch]) + + def has(self, name: str, branch: Optional[Union[Branch, str]] = None) -> bool: + try: + self.get(name=name, branch=branch, duplicate=False) + return True + except SchemaNotFoundError: + return False + + def get( + self, + name: str, + branch: Optional[Union[Branch, str]] = None, + duplicate: bool = True, + check_branch_only: bool = False, + ) -> MainSchemaTypes: + # For now we assume that all branches are present, will see how we need to pull new branches later. + check_branch_only = check_branch_only and bool(branch) + branch = registry.get_branch_from_registry(branch=branch) + + if branch.name in self._branches: + try: + return self._branches[branch.name].get(name=name, duplicate=duplicate) + except SchemaNotFoundError: + pass + + if check_branch_only: + raise SchemaNotFoundError( + branch_name=branch.name, identifier=name, message=f"Unable to find the schema {name!r} in the registry" + ) + + default_branch = registry.default_branch + return self._branches[default_branch].get(name=name, duplicate=duplicate) + + def get_node_schema( + self, name: str, branch: Optional[Union[Branch, str]] = None, duplicate: bool = True + ) -> NodeSchema: + schema = self.get(name=name, branch=branch, duplicate=duplicate) + if isinstance(schema, NodeSchema): + return schema + + raise ValueError("The selected node is not of type NodeSchema") + + def get_full( + self, branch: Optional[Union[Branch, str]] = None, duplicate: bool = True + ) -> dict[str, MainSchemaTypes]: + branch = registry.get_branch_from_registry(branch=branch) + + branch_name = None + if branch.name in self._branches: + branch_name = branch.name + else: + branch_name = registry.default_branch + + return self._branches[branch_name].get_all(duplicate=duplicate) + + async def get_full_safe( + self, branch: Optional[Union[Branch, str]] = None + ) -> dict[str, Union[NodeSchema, GenericSchema]]: + await lock.registry.local_schema_wait() + + return self.get_full(branch=branch) + + def get_schema_branch(self, name: str) -> SchemaBranch: + if name in self._branches: + return self._branches[name] + + self._branches[name] = SchemaBranch(cache=self._cache, name=name) + return self._branches[name] + + def set_schema_branch(self, name: str, schema: SchemaBranch) -> None: + schema.name = name + self._branches[name] = schema + + def process_schema_branch(self, name: str) -> None: + schema_branch = self.get_schema_branch(name=name) + schema_branch.process() + + async def update_schema_branch( + self, + schema: SchemaBranch, + db: InfrahubDatabase, + branch: Optional[Union[Branch, str]] = None, + diff: Optional[SchemaDiff] = None, + limit: Optional[list[str]] = None, + update_db: bool = True, + ) -> None: + branch = await registry.get_branch(branch=branch, db=db) + + updated_schema = None + if update_db: + schema_diff = None + if diff: + schema_diff = await self.update_schema_to_db(schema=schema, db=db, branch=branch, diff=diff) + else: + await self.load_schema_to_db(schema=schema, db=db, branch=branch, limit=limit) + # After updating the schema into the db + # we need to pull a fresh version because some default value are managed/generated within the node object + schema_diff = None + if limit: + schema_diff = SchemaBranchDiff( + nodes=[name for name in list(schema.nodes.keys()) if name in limit], + generics=[name for name in list(schema.generics.keys()) if name in limit], + ) + + updated_schema = await self.load_schema_from_db( + db=db, branch=branch, schema=schema, schema_diff=schema_diff + ) + + self.set_schema_branch(name=branch.name, schema=updated_schema or schema) + + def register_schema(self, schema: SchemaRoot, branch: Optional[str] = None) -> SchemaBranch: + """Register all nodes, generics & groups from a SchemaRoot object into the registry.""" + + branch = branch or registry.default_branch + schema_branch = self.get_schema_branch(name=branch) + schema_branch.load_schema(schema=schema) + schema_branch.process() + return schema_branch + + async def update_schema_to_db( + self, + schema: SchemaBranch, + db: InfrahubDatabase, + diff: SchemaDiff, + branch: Optional[Union[str, Branch]] = None, + ) -> SchemaBranchDiff: + """Load all nodes, generics and groups from a SchemaRoot object into the database.""" + + branch = await registry.get_branch(branch=branch, db=db) + + item_kinds = [] + for item_kind, item_diff in diff.added.items(): + item = schema.get(name=item_kind, duplicate=False) + node = await self.load_node_to_db(node=item, branch=branch, db=db) + schema.set(name=item_kind, schema=node) + item_kinds.append(item_kind) + + for item_kind, item_diff in diff.changed.items(): + item = schema.get(name=item_kind, duplicate=False) + if item_diff: + node = await self.update_node_in_db_based_on_diff(node=item, branch=branch, db=db, diff=item_diff) + else: + node = await self.update_node_in_db(node=item, branch=branch, db=db) + schema.set(name=item_kind, schema=node) + item_kinds.append(item_kind) + + for item_kind, item_diff in diff.removed.items(): + item = schema.get(name=item_kind, duplicate=False) + node = await self.delete_node_in_db(node=item, branch=branch, db=db) + schema.delete(name=item_kind) + + schema_diff = SchemaBranchDiff( + nodes=[name for name in schema.node_names if name in item_kinds], + generics=[name for name in schema.generic_names if name in item_kinds], + ) + return schema_diff + + async def load_schema_to_db( + self, + schema: SchemaBranch, + db: InfrahubDatabase, + branch: Optional[Union[str, Branch]] = None, + limit: Optional[list[str]] = None, + ) -> None: + """Load all nodes, generics and groups from a SchemaRoot object into the database.""" + + branch = await registry.get_branch(branch=branch, db=db) + + for item_kind in schema.node_names + schema.generic_names: + if item_kind == InfrahubKind.PROFILE: + continue + if limit and item_kind not in limit: + continue + item = schema.get(name=item_kind, duplicate=False) + if not item.id: + node = await self.load_node_to_db(node=item, branch=branch, db=db) + schema.set(name=item_kind, schema=node) + else: + node = await self.update_node_in_db(node=item, branch=branch, db=db) + schema.set(name=item_kind, schema=node) + + async def load_node_to_db( + self, + node: Union[NodeSchema, GenericSchema], + db: InfrahubDatabase, + branch: Optional[Union[str, Branch]] = None, + ) -> Union[NodeSchema, GenericSchema]: + """Load a Node with its attributes and its relationships to the database.""" + branch = await registry.get_branch(branch=branch, db=db) + + node_type = "SchemaNode" + if isinstance(node, GenericSchema): + node_type = "SchemaGeneric" + + node_schema = self.get_node_schema(name=node_type, branch=branch, duplicate=False) + attribute_schema = self.get_node_schema(name="SchemaAttribute", branch=branch, duplicate=False) + relationship_schema = self.get_node_schema(name="SchemaRelationship", branch=branch, duplicate=False) + + # Duplicate the node in order to store the IDs after inserting them in the database + new_node = node.duplicate() + + # Create the node first + schema_dict = node.model_dump(exclude={"id", "state", "filters", "relationships", "attributes"}) + obj = await Node.init(schema=node_schema, branch=branch, db=db) + await obj.new(**schema_dict, db=db) + await obj.save(db=db) + new_node.id = obj.id + + # Then create the Attributes and the relationships + if isinstance(node, (NodeSchema, GenericSchema)): + new_node.relationships = [] + new_node.attributes = [] + + for item in node.attributes: + new_attr = await self.create_attribute_in_db( + schema=attribute_schema, item=item, parent=obj, branch=branch, db=db + ) + new_node.attributes.append(new_attr) + + for item in node.relationships: + new_rel = await self.create_relationship_in_db( + schema=relationship_schema, item=item, parent=obj, branch=branch, db=db + ) + new_node.relationships.append(new_rel) + + # Save back the node with the newly created IDs in the SchemaManager + self.set(name=new_node.kind, schema=new_node, branch=branch.name) + return new_node + + async def update_node_in_db( + self, + db: InfrahubDatabase, + node: Union[NodeSchema, GenericSchema], + branch: Optional[Union[str, Branch]] = None, + ) -> Union[NodeSchema, GenericSchema]: + """Update a Node with its attributes and its relationships in the database.""" + branch = await registry.get_branch(branch=branch, db=db) + + obj = await self.get_one(id=node.get_id(), branch=branch, db=db) + if not obj: + raise SchemaNotFoundError( + branch_name=branch.name, + identifier=node.id, + message=f"Unable to find the Schema associated with {node.id}, {node.kind}", + ) + + schema_dict = node.model_dump(exclude=IGNORE_FOR_NODE) + for key, value in schema_dict.items(): + getattr(obj, key).value = value + + attribute_schema = self.get_node_schema(name="SchemaAttribute", branch=branch) + relationship_schema = self.get_node_schema(name="SchemaRelationship", branch=branch) + + new_node = node.duplicate() + + # Update the attributes and the relationships nodes as well + await obj.attributes.update(db=db, data=[item.id for item in node.local_attributes if item.id]) + await obj.relationships.update( + db=db, data=[item.id for item in node.local_relationships if item.id and item.name != "profiles"] + ) + await obj.save(db=db) + + # Then Update the Attributes and the relationships + + items = await self.get_many( + ids=[item.id for item in node.local_attributes + node.local_relationships if item.id], + db=db, + branch=branch, + include_owner=True, + include_source=True, + ) + + for item in node.local_attributes: + if item.id and item.id in items: + await self.update_attribute_in_db(item=item, attr=items[item.id], db=db) + elif not item.id: + new_attr = await self.create_attribute_in_db( + schema=attribute_schema, item=item, branch=branch, db=db, parent=obj + ) + new_node.attributes.append(new_attr) + + for item in node.local_relationships: + if item.id and item.id in items: + await self.update_relationship_in_db(item=item, rel=items[item.id], db=db) + elif not item.id: + new_rel = await self.create_relationship_in_db( + schema=relationship_schema, item=item, branch=branch, db=db, parent=obj + ) + new_node.relationships.append(new_rel) + + # Save back the node with the (potentially) newly created IDs in the SchemaManager + self.set(name=new_node.kind, schema=new_node, branch=branch.name) + return new_node + + async def update_node_in_db_based_on_diff( # pylint: disable=too-many-branches,too-many-statements + self, + db: InfrahubDatabase, + diff: HashableModelDiff, + node: Union[NodeSchema, GenericSchema], + branch: Optional[Union[str, Branch]] = None, + ) -> Union[NodeSchema, GenericSchema]: + """Update a Node with its attributes and its relationships in the database based on a HashableModelDiff.""" + branch = await registry.get_branch(branch=branch, db=db) + + obj = await self.get_one(id=node.get_id(), branch=branch, db=db) + if not obj: + raise SchemaNotFoundError( + branch_name=branch.name, + identifier=node.id, + message=f"Unable to find the Schema associated with {node.id}, {node.kind}", + ) + + properties_to_update = set(list(diff.added.keys()) + list(diff.changed.keys())) - IGNORE_FOR_NODE + + if properties_to_update: + schema_dict = node.model_dump(exclude=IGNORE_FOR_NODE) + for key, value in schema_dict.items(): + getattr(obj, key).value = value + + attribute_schema = self.get_node_schema(name="SchemaAttribute", branch=branch) + relationship_schema = self.get_node_schema(name="SchemaRelationship", branch=branch) + + new_node = node.duplicate() + + # Update the attributes and the relationships nodes as well + if "attributes" in diff.changed: + await obj.attributes.update(db=db, data=[item.id for item in node.local_attributes if item.id]) + + if "relationships" in diff.changed: + await obj.relationships.update(db=db, data=[item.id for item in node.local_relationships if item.id]) + + await obj.save(db=db) + + # Then Update the Attributes and the relationships + def get_attrs_rels_to_update(diff: HashableModelDiff) -> list[str]: + items_to_update = [] + if "attributes" in diff.changed.keys() and diff.changed["attributes"]: + items_to_update.extend(list(diff.changed["attributes"].added.keys())) + items_to_update.extend(list(diff.changed["attributes"].changed.keys())) + items_to_update.extend(list(diff.changed["attributes"].removed.keys())) + if "relationships" in diff.changed.keys() and diff.changed["relationships"]: + items_to_update.extend(list(diff.changed["relationships"].added.keys())) + items_to_update.extend(list(diff.changed["relationships"].changed.keys())) + items_to_update.extend(list(diff.changed["relationships"].removed.keys())) + return items_to_update + + attrs_rels_to_update = get_attrs_rels_to_update(diff=diff) + + items = await self.get_many( + ids=[ + item.id + for item in node.local_attributes + node.local_relationships + if item.id and item.name in attrs_rels_to_update + ], + db=db, + branch=branch, + include_owner=True, + include_source=True, + ) + + if "attributes" in diff.changed.keys() and diff.changed["attributes"]: + for item in node.local_attributes: + if item.name in diff.changed["attributes"].added: + created_item = await self.create_attribute_in_db( + schema=attribute_schema, item=item, branch=branch, db=db, parent=obj + ) + new_attr = new_node.get_attribute(name=item.name) + new_attr.id = created_item.id + elif item.name in diff.changed["attributes"].changed and item.id and item.id in items: + await self.update_attribute_in_db(item=item, attr=items[item.id], db=db) + elif item.name in diff.changed["attributes"].removed and item.id and item.id in items: + await items[item.id].delete(db=db) + elif ( + (item.name in diff.changed["attributes"].removed or item.name in diff.changed["attributes"].changed) + and item.id + and item.id not in items + ): + raise ValueError(f"Unable to find an attribute {item.name!r} to update or delete") + + if "relationships" in diff.changed.keys() and diff.changed["relationships"]: + for item in node.local_relationships: + if item.name in diff.changed["relationships"].added: + created_rel = await self.create_relationship_in_db( + schema=relationship_schema, item=item, branch=branch, db=db, parent=obj + ) + new_rel = new_node.get_relationship(name=item.name) + new_rel.id = created_rel.id + elif item.name in diff.changed["relationships"].changed and item.id and item.id in items: + await self.update_relationship_in_db(item=item, rel=items[item.id], db=db) + elif item.name in diff.changed["relationships"].removed and item.id and item.id in items: + await items[item.id].delete(db=db) + elif ( + ( + item.name in diff.changed["relationships"].removed + or item.name in diff.changed["relationships"].changed + ) + and item.id + and item.id not in items + ): + raise ValueError(f"Unable to find an relationship {item.name!r} to update or delete") + + # Save back the node with the (potentially) newly created IDs in the SchemaManager + self.set(name=new_node.kind, schema=new_node, branch=branch.name) + return new_node + + async def delete_node_in_db( + self, + db: InfrahubDatabase, + node: Union[NodeSchema, GenericSchema], + branch: Optional[Union[str, Branch]] = None, + ) -> None: + """Delete the node with its attributes and relationships.""" + branch = await registry.get_branch(branch=branch, db=db) + + obj = await self.get_one(id=node.get_id(), branch=branch, db=db) + if not obj: + raise SchemaNotFoundError( + branch_name=branch.name, + identifier=node.id, + message=f"Unable to find the Schema associated with {node.id}, {node.kind}", + ) + + # First delete the attributes and the relationships + items = await self.get_many( + ids=[item.id for item in node.local_attributes + node.local_relationships if item.id], + db=db, + branch=branch, + include_owner=True, + include_source=True, + ) + + for item in items.values(): + await item.delete(db=db) + + await obj.delete(db=db) + + @staticmethod + async def create_attribute_in_db( + schema: NodeSchema, item: AttributeSchema, branch: Branch, parent: Node, db: InfrahubDatabase + ) -> AttributeSchema: + obj = await Node.init(schema=schema, branch=branch, db=db) + await obj.new(**item.model_dump(exclude={"id", "state", "filters"}), node=parent, db=db) + await obj.save(db=db) + new_item = item.duplicate() + new_item.id = obj.id + return new_item + + @staticmethod + async def update_attribute_in_db(item: AttributeSchema, attr: Node, db: InfrahubDatabase) -> None: + item_dict = item.model_dump(exclude={"id", "state", "filters"}) + for key, value in item_dict.items(): + getattr(attr, key).value = value + await attr.save(db=db) + + @staticmethod + async def create_relationship_in_db( + schema: NodeSchema, item: RelationshipSchema, branch: Branch, parent: Node, db: InfrahubDatabase + ) -> RelationshipSchema: + obj = await Node.init(schema=schema, branch=branch, db=db) + await obj.new(**item.model_dump(exclude={"id", "state", "filters"}), node=parent, db=db) + await obj.save(db=db) + new_item = item.duplicate() + new_item.id = obj.id + return new_item + + @staticmethod + async def update_relationship_in_db(item: RelationshipSchema, rel: Node, db: InfrahubDatabase) -> None: + item_dict = item.model_dump(exclude={"id", "state", "filters"}) + for key, value in item_dict.items(): + getattr(rel, key).value = value + await rel.save(db=db) + + async def load_schema( + self, + db: InfrahubDatabase, + branch: Optional[Union[str, Branch]] = None, + ) -> SchemaBranch: + """Load the schema either from the cache or from the database""" + branch = await registry.get_branch(branch=branch, db=db) + + if not branch.is_default and branch.origin_branch: + origin_branch: Branch = await registry.get_branch(branch=branch.origin_branch, db=db) + + if origin_branch.schema_hash.main == branch.schema_hash.main: + origin_schema = self.get_schema_branch(name=origin_branch.name) + new_branch_schema = origin_schema.duplicate() + self.set_schema_branch(name=branch.name, schema=new_branch_schema) + log.info("Loading schema from cache") + return new_branch_schema + + current_schema = self.get_schema_branch(name=branch.name) + schema_diff = current_schema.get_hash_full().compare(branch.schema_hash) + branch_schema = await self.load_schema_from_db( + db=db, branch=branch, schema=current_schema, schema_diff=schema_diff + ) + branch_schema.clear_cache() + self.set_schema_branch(name=branch.name, schema=branch_schema) + return branch_schema + + async def load_schema_from_db( + self, + db: InfrahubDatabase, + branch: Optional[Union[str, Branch]] = None, + schema: Optional[SchemaBranch] = None, + schema_diff: Optional[SchemaBranchDiff] = None, + at: Optional[Timestamp] = None, + validate_schema: bool = True, + ) -> SchemaBranch: + """Query all the node of type NodeSchema and GenericSchema from the database and convert them to their respective type. + + Args: + db: Database Driver + branch: Name of the branch to load the schema from. Defaults to None. + schema: (Optional) If a schema is provided, it will be updated with the latest value, if not a new one will be created. + schema_diff: (Optional). list of nodes, generics & groups to query + + Returns: + SchemaBranch + """ + + branch = await registry.get_branch(branch=branch, db=db) + schema = schema or SchemaBranch(cache=self._cache, name=branch.name) + + # If schema_diff has been provided, we need to build the proper filters for the queries based on the namespace and the name of the object. + # the namespace and the name will be extracted from the kind with the function `parse_node_kind` + filters = {"generics": {}, "nodes": {}} + has_filters = False + + # If a diff is provided but is empty there is nothing to query + if schema_diff is not None and not schema_diff: + return schema + + if schema_diff: + log.info("Loading schema from DB", schema_to_update=schema_diff.to_list()) + + for node_type in list(filters.keys()): + filter_value = { + "namespace__values": list( + {parse_node_kind(item).namespace for item in getattr(schema_diff, node_type)} + ), + "name__values": list({parse_node_kind(item).name for item in getattr(schema_diff, node_type)}), + } + + if filter_value["namespace__values"]: + filters[node_type] = filter_value + has_filters = True + + if not has_filters or filters["generics"]: + generic_schema = self.get(name="SchemaGeneric", branch=branch) + for schema_node in await self.query( + schema=generic_schema, + branch=branch, + at=at, + filters=filters["generics"], + prefetch_relationships=True, + db=db, + ): + kind = f"{schema_node.namespace.value}{schema_node.name.value}" + schema.set( + name=kind, + schema=await self.convert_generic_schema_to_schema(schema_node=schema_node, db=db), + ) + + if not has_filters or filters["nodes"]: + node_schema = self.get(name="SchemaNode", branch=branch) + for schema_node in await self.query( + schema=node_schema, branch=branch, at=at, filters=filters["nodes"], prefetch_relationships=True, db=db + ): + kind = f"{schema_node.namespace.value}{schema_node.name.value}" + schema.set( + name=kind, + schema=await self.convert_node_schema_to_schema(schema_node=schema_node, db=db), + ) + + schema.process(validate_schema=validate_schema) + + return schema + + @classmethod + async def _prepare_node_data(cls, schema_node: Node, db: InfrahubDatabase) -> dict[str, Any]: + node_data = {"id": schema_node.id} + + # First pull all the local attributes at the top level, then convert all the local relationships + # for a standard node_schema, the relationships will be attributes and relationships + for attr_name in schema_node._attributes: + attr = getattr(schema_node, attr_name) + node_data[attr_name] = attr.get_value() + + for rel_name in schema_node._relationships: + if rel_name not in node_data: + if rel_name == "profiles": + continue + node_data[rel_name] = [] + + rm = getattr(schema_node, rel_name) + for rel in await rm.get(db=db): + item = await rel.get_peer(db=db) + item_data = {"id": item.id} + for item_name in item._attributes: + item_attr = getattr(item, item_name) + item_data[item_name] = item_attr.get_value() + + node_data[rel_name].append(item_data) + return node_data + + @classmethod + async def convert_node_schema_to_schema(cls, schema_node: Node, db: InfrahubDatabase) -> NodeSchema: + """Convert a schema_node object loaded from the database into NodeSchema object.""" + node_data = await cls._prepare_node_data(schema_node=schema_node, db=db) + return NodeSchema(**node_data) + + @classmethod + async def convert_generic_schema_to_schema(cls, schema_node: Node, db: InfrahubDatabase) -> GenericSchema: + """Convert a schema_node object loaded from the database into GenericSchema object.""" + node_data = await cls._prepare_node_data(schema_node=schema_node, db=db) + return GenericSchema(**node_data) diff --git a/backend/infrahub/core/schema_manager.py b/backend/infrahub/core/schema/schema_branch.py similarity index 67% rename from backend/infrahub/core/schema_manager.py rename to backend/infrahub/core/schema/schema_branch.py index eac391f9e9..116cf5ca42 100644 --- a/backend/infrahub/core/schema_manager.py +++ b/backend/infrahub/core/schema/schema_branch.py @@ -4,19 +4,17 @@ import hashlib from collections import defaultdict from itertools import chain -from typing import TYPE_CHECKING, Any, Optional, Union +from typing import TYPE_CHECKING, Any, Callable, Iterator, Optional, Union from infrahub_sdk.topological_sort import DependencyCycleExistsError, topological_sort from infrahub_sdk.utils import compare_lists, deep_merge_dict, duplicates, intersection -from pydantic import BaseModel +from typing_extensions import Self -from infrahub import lock from infrahub.core.constants import ( RESERVED_ATTR_GEN_NAMES, RESERVED_ATTR_REL_NAMES, RESTRICTED_NAMESPACES, BranchSupportType, - FilterSchemaKind, HashableModelState, InfrahubKind, RelationshipCardinality, @@ -25,23 +23,17 @@ RelationshipKind, SchemaElementPathType, ) -from infrahub.core.manager import NodeManager from infrahub.core.migrations import MIGRATION_MAP from infrahub.core.models import ( HashableModelDiff, - SchemaBranchDiff, SchemaBranchHash, SchemaDiff, SchemaUpdateValidationResult, ) -from infrahub.core.node import Node -from infrahub.core.property import FlagPropertyMixin, NodePropertyMixin -from infrahub.core.registry import registry from infrahub.core.schema import ( AttributePathParsingError, AttributeSchema, BaseNodeSchema, - FilterSchema, GenericSchema, MainSchemaTypes, NodeSchema, @@ -49,10 +41,8 @@ RelationshipSchema, SchemaAttributePath, SchemaRoot, - internal_schema, ) from infrahub.core.schema.definitions.core import core_profile_schema_definition -from infrahub.core.utils import parse_node_kind from infrahub.core.validators import CONSTRAINT_VALIDATOR_MAP from infrahub.exceptions import SchemaNotFoundError, ValidationError from infrahub.graphql.manager import GraphQLSchemaManager @@ -61,43 +51,22 @@ from infrahub.utils import format_label from infrahub.visuals import select_color +from .constants import INTERNAL_SCHEMA_NODE_KINDS, SchemaNamespace + log = get_logger() if TYPE_CHECKING: from graphql import GraphQLSchema + from pydantic import ValidationInfo - from infrahub.core.branch import Branch - from infrahub.core.timestamp import Timestamp - from infrahub.database import InfrahubDatabase # pylint: disable=redefined-builtin,too-many-public-methods,too-many-lines -INTERNAL_SCHEMA_NODE_KINDS = [node["namespace"] + node["name"] for node in internal_schema["nodes"]] - -SUPPORTED_SCHEMA_EXTENSION_TYPE = ["NodeExtensionSchema"] - -KIND_FILTER_MAP = { - "Text": FilterSchemaKind.TEXT, - "String": FilterSchemaKind.TEXT, - "Number": FilterSchemaKind.NUMBER, - "Integer": FilterSchemaKind.NUMBER, - "Boolean": FilterSchemaKind.BOOLEAN, - "Checkbox": FilterSchemaKind.BOOLEAN, - "Dropdown": FilterSchemaKind.TEXT, -} - -IGNORE_FOR_NODE = {"id", "state", "filters", "relationships", "attributes"} - - -class SchemaNamespace(BaseModel): - name: str - user_editable: bool - class SchemaBranch: - def __init__(self, cache: dict, name: Optional[str] = None, data: Optional[dict[str, int]] = None): + def __init__(self, cache: dict, name: str | None = None, data: dict[str, dict[str, str]] | None = None): self._cache: dict[str, Union[NodeSchema, GenericSchema]] = cache - self.name: Optional[str] = name + self.name: str | None = name self.nodes: dict[str, str] = {} self.generics: dict[str, str] = {} self.profiles: dict[str, str] = {} @@ -109,6 +78,18 @@ def __init__(self, cache: dict, name: Optional[str] = None, data: Optional[dict[ self.generics = data.get("generics", {}) self.profiles = data.get("profiles", {}) + @classmethod + def __get_validators__(cls) -> Iterator[Callable[..., Any]]: # noqa: PLW3201 + yield cls.validate + + @classmethod + def validate(cls, v: Any, info: ValidationInfo) -> Self: # pylint: disable=unused-argument + if isinstance(v, cls): + return v + if isinstance(v, dict): + return cls.from_dict_schema_object(data=v) + raise ValueError("must be a class or a dict") + @property def node_names(self) -> list[str]: return list(self.nodes.keys()) @@ -163,7 +144,28 @@ def to_dict_schema_object(self, duplicate: bool = False) -> dict[str, dict[str, "generics": {name: self.get(name, duplicate=duplicate) for name in self.generics}, } - def clear_cache(self): + @classmethod + def from_dict_schema_object(cls, data: dict) -> Self: + type_mapping = { + "nodes": NodeSchema, + "generics": GenericSchema, + "profiles": ProfileSchema, + } + + cache: dict[str, MainSchemaTypes] = {} + nodes: dict[str, dict[str, str]] = {"nodes": {}, "generics": {}, "profiles": {}} + + for node_type, node_class in type_mapping.items(): + for node_name, node_data in data[node_type].items(): + node: MainSchemaTypes = node_class(**node_data) + node_hash = node.get_hash() + nodes[node_type][node_name] = node_hash + + cache[node_hash] = node + + return cls(cache=cache, data=nodes) + + def clear_cache(self) -> None: self._graphql_manager = None self._graphql_schema = None @@ -501,7 +503,6 @@ def process_validate(self) -> None: def process_post_validation(self) -> None: self.add_groups() self.add_hierarchy() - self.process_filters() self.generate_weight() self.process_labels() self.process_dropdowns() @@ -756,7 +757,7 @@ def validate_default_filters(self) -> None: element_name="default_filter", ) - def validate_default_values(self): + def validate_default_values(self) -> None: for name in self.generic_names + self.node_names: node_schema = self.get(name=name, duplicate=False) for node_attr in node_schema.local_attributes: @@ -1253,22 +1254,6 @@ def process_default_values(self) -> None: self.set(name=name, schema=node) - def process_filters(self) -> Node: - # Generate the filters for all nodes and generics, at the NodeSchema and at the relationships level. - for name in self.all_names: - node = self.get(name=name) - node.filters = self.generate_filters(schema=node, include_relationships=True) - - for rel in node.relationships: - try: - peer_schema = self.get(name=rel.peer, duplicate=False) - except SchemaNotFoundError: - continue - - rel.filters = self.generate_filters(schema=peer_schema, include_relationships=False) - - self.set(name=name, schema=node) - def process_cardinality_counts(self) -> None: """Ensure that all relationships with a cardinality of ONE have a min_count and max_count of 1.""" # pylint: disable=too-many-branches @@ -1601,709 +1586,3 @@ def generate_profile_from_node(self, node: NodeSchema) -> ProfileSchema: profile.attributes.append(attr) return profile - - def generate_filters( - self, schema: Union[NodeSchema, GenericSchema], include_relationships: bool = False - ) -> list[FilterSchema]: - """Generate the FilterSchema for a given NodeSchema or GenericSchema object.""" - # pylint: disable=too-many-branches - filters = [] - - filters.append(FilterSchema(name="ids", kind=FilterSchemaKind.LIST)) - if schema.human_friendly_id: - filters.append(FilterSchema(name="hfid", kind=FilterSchemaKind.LIST)) - - for attr in schema.attributes: - filter_kind = KIND_FILTER_MAP.get(attr.kind, None) - if not filter_kind: - continue - - filter = FilterSchema(name=f"{attr.name}__value", kind=filter_kind) - - if attr.enum: - filter.enum = attr.enum - - filters.append(filter) - filters.append(FilterSchema(name=f"{attr.name}__values", kind=FilterSchemaKind.LIST)) - - for flag_prop in FlagPropertyMixin._flag_properties: - filters.append(FilterSchema(name=f"{attr.name}__{flag_prop}", kind=FilterSchemaKind.BOOLEAN)) - for node_prop in NodePropertyMixin._node_properties: - filters.append(FilterSchema(name=f"{attr.name}__{node_prop}__id", kind=FilterSchemaKind.TEXT)) - - # Define generic filters, mainly used to query all nodes associated with a given account - if include_relationships: - filters.append(FilterSchema(name="any__value", kind=FilterSchemaKind.TEXT)) - for flag_prop in FlagPropertyMixin._flag_properties: - filters.append(FilterSchema(name=f"any__{flag_prop}", kind=FilterSchemaKind.BOOLEAN)) - for node_prop in NodePropertyMixin._node_properties: - filters.append(FilterSchema(name=f"any__{node_prop}__id", kind=FilterSchemaKind.TEXT)) - - if not include_relationships: - return filters - - for rel in schema.relationships: - if rel.kind not in ["Attribute", "Parent"]: - continue - filters.append(FilterSchema(name=f"{rel.name}__ids", kind=FilterSchemaKind.LIST, object_kind=rel.peer)) - peer_schema = self.get(name=rel.peer, duplicate=False) - - for attr in peer_schema.attributes: - filter_kind = KIND_FILTER_MAP.get(attr.kind, None) - if not filter_kind: - continue - - filter = FilterSchema(name=f"{rel.name}__{attr.name}__value", kind=filter_kind) - - if attr.enum: - filter.enum = attr.enum - - filters.append(filter) - filters.append(FilterSchema(name=f"{rel.name}__{attr.name}__values", kind=FilterSchemaKind.LIST)) - - for flag_prop in FlagPropertyMixin._flag_properties: - filters.append( - FilterSchema(name=f"{rel.name}__{attr.name}__{flag_prop}", kind=FilterSchemaKind.BOOLEAN) - ) - for node_prop in NodePropertyMixin._node_properties: - filters.append( - FilterSchema(name=f"{rel.name}__{attr.name}__{node_prop}__id", kind=FilterSchemaKind.TEXT) - ) - - return filters - - -# pylint: disable=too-many-public-methods -class SchemaManager(NodeManager): - def __init__(self) -> None: - self._cache: dict[int, Any] = {} - self._branches: dict[str, SchemaBranch] = {} - - def _get_from_cache(self, key: int) -> Any: - return self._cache[key] - - def set(self, name: str, schema: Union[NodeSchema, GenericSchema], branch: Optional[str] = None) -> int: - branch = branch or registry.default_branch - - if branch not in self._branches: - self._branches[branch] = SchemaBranch(cache=self._cache, name=branch) - - self._branches[branch].set(name=name, schema=schema) - - return hash(self._branches[branch]) - - def has(self, name: str, branch: Optional[Union[Branch, str]] = None) -> bool: - try: - self.get(name=name, branch=branch, duplicate=False) - return True - except SchemaNotFoundError: - return False - - def get( - self, - name: str, - branch: Optional[Union[Branch, str]] = None, - duplicate: bool = True, - check_branch_only: bool = False, - ) -> MainSchemaTypes: - # For now we assume that all branches are present, will see how we need to pull new branches later. - check_branch_only = check_branch_only and bool(branch) - branch = registry.get_branch_from_registry(branch=branch) - - if branch.name in self._branches: - try: - return self._branches[branch.name].get(name=name, duplicate=duplicate) - except SchemaNotFoundError: - pass - - if check_branch_only: - raise SchemaNotFoundError( - branch_name=branch.name, identifier=name, message=f"Unable to find the schema {name!r} in the registry" - ) - - default_branch = registry.default_branch - return self._branches[default_branch].get(name=name, duplicate=duplicate) - - def get_node_schema( - self, name: str, branch: Optional[Union[Branch, str]] = None, duplicate: bool = True - ) -> NodeSchema: - schema = self.get(name=name, branch=branch, duplicate=duplicate) - if isinstance(schema, NodeSchema): - return schema - - raise ValueError("The selected node is not of type NodeSchema") - - def get_full( - self, branch: Optional[Union[Branch, str]] = None, duplicate: bool = True - ) -> dict[str, MainSchemaTypes]: - branch = registry.get_branch_from_registry(branch=branch) - - branch_name = None - if branch.name in self._branches: - branch_name = branch.name - else: - branch_name = registry.default_branch - - return self._branches[branch_name].get_all(duplicate=duplicate) - - async def get_full_safe( - self, branch: Optional[Union[Branch, str]] = None - ) -> dict[str, Union[NodeSchema, GenericSchema]]: - await lock.registry.local_schema_wait() - - return self.get_full(branch=branch) - - def get_schema_branch(self, name: str) -> SchemaBranch: - if name in self._branches: - return self._branches[name] - - self._branches[name] = SchemaBranch(cache=self._cache, name=name) - return self._branches[name] - - def set_schema_branch(self, name: str, schema: SchemaBranch) -> None: - schema.name = name - self._branches[name] = schema - - def process_schema_branch(self, name: str) -> None: - schema_branch = self.get_schema_branch(name=name) - schema_branch.process() - - async def update_schema_branch( - self, - schema: SchemaBranch, - db: InfrahubDatabase, - branch: Optional[Union[Branch, str]] = None, - diff: Optional[SchemaDiff] = None, - limit: Optional[list[str]] = None, - update_db: bool = True, - ) -> None: - branch = await registry.get_branch(branch=branch, db=db) - - updated_schema = None - if update_db: - schema_diff = None - if diff: - schema_diff = await self.update_schema_to_db(schema=schema, db=db, branch=branch, diff=diff) - else: - await self.load_schema_to_db(schema=schema, db=db, branch=branch, limit=limit) - # After updating the schema into the db - # we need to pull a fresh version because some default value are managed/generated within the node object - schema_diff = None - if limit: - schema_diff = SchemaBranchDiff( - nodes=[name for name in list(schema.nodes.keys()) if name in limit], - generics=[name for name in list(schema.generics.keys()) if name in limit], - ) - - updated_schema = await self.load_schema_from_db( - db=db, branch=branch, schema=schema, schema_diff=schema_diff - ) - - self.set_schema_branch(name=branch.name, schema=updated_schema or schema) - - def register_schema(self, schema: SchemaRoot, branch: Optional[str] = None) -> SchemaBranch: - """Register all nodes, generics & groups from a SchemaRoot object into the registry.""" - - branch = branch or registry.default_branch - schema_branch = self.get_schema_branch(name=branch) - schema_branch.load_schema(schema=schema) - schema_branch.process() - return schema_branch - - async def update_schema_to_db( - self, - schema: SchemaBranch, - db: InfrahubDatabase, - diff: SchemaDiff, - branch: Optional[Union[str, Branch]] = None, - ) -> SchemaBranchDiff: - """Load all nodes, generics and groups from a SchemaRoot object into the database.""" - - branch = await registry.get_branch(branch=branch, db=db) - - item_kinds = [] - for item_kind, item_diff in diff.added.items(): - item = schema.get(name=item_kind, duplicate=False) - node = await self.load_node_to_db(node=item, branch=branch, db=db) - schema.set(name=item_kind, schema=node) - item_kinds.append(item_kind) - - for item_kind, item_diff in diff.changed.items(): - item = schema.get(name=item_kind, duplicate=False) - if item_diff: - node = await self.update_node_in_db_based_on_diff(node=item, branch=branch, db=db, diff=item_diff) - else: - node = await self.update_node_in_db(node=item, branch=branch, db=db) - schema.set(name=item_kind, schema=node) - item_kinds.append(item_kind) - - for item_kind, item_diff in diff.removed.items(): - item = schema.get(name=item_kind, duplicate=False) - node = await self.delete_node_in_db(node=item, branch=branch, db=db) - schema.delete(name=item_kind) - - schema_diff = SchemaBranchDiff( - nodes=[name for name in schema.node_names if name in item_kinds], - generics=[name for name in schema.generic_names if name in item_kinds], - ) - return schema_diff - - async def load_schema_to_db( - self, - schema: SchemaBranch, - db: InfrahubDatabase, - branch: Optional[Union[str, Branch]] = None, - limit: Optional[list[str]] = None, - ) -> None: - """Load all nodes, generics and groups from a SchemaRoot object into the database.""" - - branch = await registry.get_branch(branch=branch, db=db) - - for item_kind in schema.node_names + schema.generic_names: - if item_kind == InfrahubKind.PROFILE: - continue - if limit and item_kind not in limit: - continue - item = schema.get(name=item_kind, duplicate=False) - if not item.id: - node = await self.load_node_to_db(node=item, branch=branch, db=db) - schema.set(name=item_kind, schema=node) - else: - node = await self.update_node_in_db(node=item, branch=branch, db=db) - schema.set(name=item_kind, schema=node) - - async def load_node_to_db( - self, - node: Union[NodeSchema, GenericSchema], - db: InfrahubDatabase, - branch: Optional[Union[str, Branch]] = None, - ) -> Union[NodeSchema, GenericSchema]: - """Load a Node with its attributes and its relationships to the database.""" - branch = await registry.get_branch(branch=branch, db=db) - - node_type = "SchemaNode" - if isinstance(node, GenericSchema): - node_type = "SchemaGeneric" - - node_schema = self.get_node_schema(name=node_type, branch=branch, duplicate=False) - attribute_schema = self.get_node_schema(name="SchemaAttribute", branch=branch, duplicate=False) - relationship_schema = self.get_node_schema(name="SchemaRelationship", branch=branch, duplicate=False) - - # Duplicate the node in order to store the IDs after inserting them in the database - new_node = node.duplicate() - - # Create the node first - schema_dict = node.model_dump(exclude={"id", "state", "filters", "relationships", "attributes"}) - obj = await Node.init(schema=node_schema, branch=branch, db=db) - await obj.new(**schema_dict, db=db) - await obj.save(db=db) - new_node.id = obj.id - - # Then create the Attributes and the relationships - if isinstance(node, (NodeSchema, GenericSchema)): - new_node.relationships = [] - new_node.attributes = [] - - for item in node.attributes: - new_attr = await self.create_attribute_in_db( - schema=attribute_schema, item=item, parent=obj, branch=branch, db=db - ) - new_node.attributes.append(new_attr) - - for item in node.relationships: - new_rel = await self.create_relationship_in_db( - schema=relationship_schema, item=item, parent=obj, branch=branch, db=db - ) - new_node.relationships.append(new_rel) - - # Save back the node with the newly created IDs in the SchemaManager - self.set(name=new_node.kind, schema=new_node, branch=branch.name) - return new_node - - async def update_node_in_db( - self, - db: InfrahubDatabase, - node: Union[NodeSchema, GenericSchema], - branch: Optional[Union[str, Branch]] = None, - ) -> Union[NodeSchema, GenericSchema]: - """Update a Node with its attributes and its relationships in the database.""" - branch = await registry.get_branch(branch=branch, db=db) - - obj = await self.get_one(id=node.get_id(), branch=branch, db=db) - if not obj: - raise SchemaNotFoundError( - branch_name=branch.name, - identifier=node.id, - message=f"Unable to find the Schema associated with {node.id}, {node.kind}", - ) - - schema_dict = node.model_dump(exclude=IGNORE_FOR_NODE) - for key, value in schema_dict.items(): - getattr(obj, key).value = value - - attribute_schema = self.get_node_schema(name="SchemaAttribute", branch=branch) - relationship_schema = self.get_node_schema(name="SchemaRelationship", branch=branch) - - new_node = node.duplicate() - - # Update the attributes and the relationships nodes as well - await obj.attributes.update(db=db, data=[item.id for item in node.local_attributes if item.id]) - await obj.relationships.update( - db=db, data=[item.id for item in node.local_relationships if item.id and item.name != "profiles"] - ) - await obj.save(db=db) - - # Then Update the Attributes and the relationships - - items = await self.get_many( - ids=[item.id for item in node.local_attributes + node.local_relationships if item.id], - db=db, - branch=branch, - include_owner=True, - include_source=True, - ) - - for item in node.local_attributes: - if item.id and item.id in items: - await self.update_attribute_in_db(item=item, attr=items[item.id], db=db) - elif not item.id: - new_attr = await self.create_attribute_in_db( - schema=attribute_schema, item=item, branch=branch, db=db, parent=obj - ) - new_node.attributes.append(new_attr) - - for item in node.local_relationships: - if item.id and item.id in items: - await self.update_relationship_in_db(item=item, rel=items[item.id], db=db) - elif not item.id: - new_rel = await self.create_relationship_in_db( - schema=relationship_schema, item=item, branch=branch, db=db, parent=obj - ) - new_node.relationships.append(new_rel) - - # Save back the node with the (potentially) newly created IDs in the SchemaManager - self.set(name=new_node.kind, schema=new_node, branch=branch.name) - return new_node - - async def update_node_in_db_based_on_diff( # pylint: disable=too-many-branches,too-many-statements - self, - db: InfrahubDatabase, - diff: HashableModelDiff, - node: Union[NodeSchema, GenericSchema], - branch: Optional[Union[str, Branch]] = None, - ) -> Union[NodeSchema, GenericSchema]: - """Update a Node with its attributes and its relationships in the database based on a HashableModelDiff.""" - branch = await registry.get_branch(branch=branch, db=db) - - obj = await self.get_one(id=node.get_id(), branch=branch, db=db) - if not obj: - raise SchemaNotFoundError( - branch_name=branch.name, - identifier=node.id, - message=f"Unable to find the Schema associated with {node.id}, {node.kind}", - ) - - properties_to_update = set(list(diff.added.keys()) + list(diff.changed.keys())) - IGNORE_FOR_NODE - - if properties_to_update: - schema_dict = node.model_dump(exclude=IGNORE_FOR_NODE) - for key, value in schema_dict.items(): - getattr(obj, key).value = value - - attribute_schema = self.get_node_schema(name="SchemaAttribute", branch=branch) - relationship_schema = self.get_node_schema(name="SchemaRelationship", branch=branch) - - new_node = node.duplicate() - - # Update the attributes and the relationships nodes as well - if "attributes" in diff.changed: - await obj.attributes.update(db=db, data=[item.id for item in node.local_attributes if item.id]) - - if "relationships" in diff.changed: - await obj.relationships.update(db=db, data=[item.id for item in node.local_relationships if item.id]) - - await obj.save(db=db) - - # Then Update the Attributes and the relationships - def get_attrs_rels_to_update(diff: HashableModelDiff) -> list[str]: - items_to_update = [] - if "attributes" in diff.changed.keys() and diff.changed["attributes"]: - items_to_update.extend(list(diff.changed["attributes"].added.keys())) - items_to_update.extend(list(diff.changed["attributes"].changed.keys())) - items_to_update.extend(list(diff.changed["attributes"].removed.keys())) - if "relationships" in diff.changed.keys() and diff.changed["relationships"]: - items_to_update.extend(list(diff.changed["relationships"].added.keys())) - items_to_update.extend(list(diff.changed["relationships"].changed.keys())) - items_to_update.extend(list(diff.changed["relationships"].removed.keys())) - return items_to_update - - attrs_rels_to_update = get_attrs_rels_to_update(diff=diff) - - items = await self.get_many( - ids=[ - item.id - for item in node.local_attributes + node.local_relationships - if item.id and item.name in attrs_rels_to_update - ], - db=db, - branch=branch, - include_owner=True, - include_source=True, - ) - - if "attributes" in diff.changed.keys() and diff.changed["attributes"]: - for item in node.local_attributes: - if item.name in diff.changed["attributes"].added: - created_item = await self.create_attribute_in_db( - schema=attribute_schema, item=item, branch=branch, db=db, parent=obj - ) - new_attr = new_node.get_attribute(name=item.name) - new_attr.id = created_item.id - elif item.name in diff.changed["attributes"].changed and item.id and item.id in items: - await self.update_attribute_in_db(item=item, attr=items[item.id], db=db) - elif item.name in diff.changed["attributes"].removed and item.id and item.id in items: - await items[item.id].delete(db=db) - elif ( - (item.name in diff.changed["attributes"].removed or item.name in diff.changed["attributes"].changed) - and item.id - and item.id not in items - ): - raise ValueError(f"Unable to find an attribute {item.name!r} to update or delete") - - if "relationships" in diff.changed.keys() and diff.changed["relationships"]: - for item in node.local_relationships: - if item.name in diff.changed["relationships"].added: - created_rel = await self.create_relationship_in_db( - schema=relationship_schema, item=item, branch=branch, db=db, parent=obj - ) - new_rel = new_node.get_relationship(name=item.name) - new_rel.id = created_rel.id - elif item.name in diff.changed["relationships"].changed and item.id and item.id in items: - await self.update_relationship_in_db(item=item, rel=items[item.id], db=db) - elif item.name in diff.changed["relationships"].removed and item.id and item.id in items: - await items[item.id].delete(db=db) - elif ( - ( - item.name in diff.changed["relationships"].removed - or item.name in diff.changed["relationships"].changed - ) - and item.id - and item.id not in items - ): - raise ValueError(f"Unable to find an relationship {item.name!r} to update or delete") - - # Save back the node with the (potentially) newly created IDs in the SchemaManager - self.set(name=new_node.kind, schema=new_node, branch=branch.name) - return new_node - - async def delete_node_in_db( - self, - db: InfrahubDatabase, - node: Union[NodeSchema, GenericSchema], - branch: Optional[Union[str, Branch]] = None, - ) -> None: - """Delete the node with its attributes and relationships.""" - branch = await registry.get_branch(branch=branch, db=db) - - obj = await self.get_one(id=node.get_id(), branch=branch, db=db) - if not obj: - raise SchemaNotFoundError( - branch_name=branch.name, - identifier=node.id, - message=f"Unable to find the Schema associated with {node.id}, {node.kind}", - ) - - # First delete the attributes and the relationships - items = await self.get_many( - ids=[item.id for item in node.local_attributes + node.local_relationships if item.id], - db=db, - branch=branch, - include_owner=True, - include_source=True, - ) - - for item in items.values(): - await item.delete(db=db) - - await obj.delete(db=db) - - @staticmethod - async def create_attribute_in_db( - schema: NodeSchema, item: AttributeSchema, branch: Branch, parent: Node, db: InfrahubDatabase - ) -> AttributeSchema: - obj = await Node.init(schema=schema, branch=branch, db=db) - await obj.new(**item.model_dump(exclude={"id", "state", "filters"}), node=parent, db=db) - await obj.save(db=db) - new_item = item.duplicate() - new_item.id = obj.id - return new_item - - @staticmethod - async def update_attribute_in_db(item: AttributeSchema, attr: Node, db: InfrahubDatabase) -> None: - item_dict = item.model_dump(exclude={"id", "state", "filters"}) - for key, value in item_dict.items(): - getattr(attr, key).value = value - await attr.save(db=db) - - @staticmethod - async def create_relationship_in_db( - schema: NodeSchema, item: RelationshipSchema, branch: Branch, parent: Node, db: InfrahubDatabase - ) -> RelationshipSchema: - obj = await Node.init(schema=schema, branch=branch, db=db) - await obj.new(**item.model_dump(exclude={"id", "state", "filters"}), node=parent, db=db) - await obj.save(db=db) - new_item = item.duplicate() - new_item.id = obj.id - return new_item - - @staticmethod - async def update_relationship_in_db(item: RelationshipSchema, rel: Node, db: InfrahubDatabase) -> None: - item_dict = item.model_dump(exclude={"id", "state", "filters"}) - for key, value in item_dict.items(): - getattr(rel, key).value = value - await rel.save(db=db) - - async def load_schema( - self, - db: InfrahubDatabase, - branch: Optional[Union[str, Branch]] = None, - ) -> SchemaBranch: - """Load the schema either from the cache or from the database""" - branch = await registry.get_branch(branch=branch, db=db) - - if not branch.is_default and branch.origin_branch: - origin_branch: Branch = await registry.get_branch(branch=branch.origin_branch, db=db) - - if origin_branch.schema_hash.main == branch.schema_hash.main: - origin_schema = self.get_schema_branch(name=origin_branch.name) - new_branch_schema = origin_schema.duplicate() - self.set_schema_branch(name=branch.name, schema=new_branch_schema) - log.info("Loading schema from cache") - return new_branch_schema - - current_schema = self.get_schema_branch(name=branch.name) - schema_diff = current_schema.get_hash_full().compare(branch.schema_hash) - branch_schema = await self.load_schema_from_db( - db=db, branch=branch, schema=current_schema, schema_diff=schema_diff - ) - branch_schema.clear_cache() - self.set_schema_branch(name=branch.name, schema=branch_schema) - return branch_schema - - async def load_schema_from_db( - self, - db: InfrahubDatabase, - branch: Optional[Union[str, Branch]] = None, - schema: Optional[SchemaBranch] = None, - schema_diff: Optional[SchemaBranchDiff] = None, - at: Optional[Timestamp] = None, - validate_schema: bool = True, - ) -> SchemaBranch: - """Query all the node of type NodeSchema and GenericSchema from the database and convert them to their respective type. - - Args: - db: Database Driver - branch: Name of the branch to load the schema from. Defaults to None. - schema: (Optional) If a schema is provided, it will be updated with the latest value, if not a new one will be created. - schema_diff: (Optional). list of nodes, generics & groups to query - - Returns: - SchemaBranch - """ - - branch = await registry.get_branch(branch=branch, db=db) - schema = schema or SchemaBranch(cache=self._cache, name=branch.name) - - # If schema_diff has been provided, we need to build the proper filters for the queries based on the namespace and the name of the object. - # the namespace and the name will be extracted from the kind with the function `parse_node_kind` - filters = {"generics": {}, "nodes": {}} - has_filters = False - - # If a diff is provided but is empty there is nothing to query - if schema_diff is not None and not schema_diff: - return schema - - if schema_diff: - log.info("Loading schema from DB", schema_to_update=schema_diff.to_list()) - - for node_type in list(filters.keys()): - filter_value = { - "namespace__values": list( - {parse_node_kind(item).namespace for item in getattr(schema_diff, node_type)} - ), - "name__values": list({parse_node_kind(item).name for item in getattr(schema_diff, node_type)}), - } - - if filter_value["namespace__values"]: - filters[node_type] = filter_value - has_filters = True - - if not has_filters or filters["generics"]: - generic_schema = self.get(name="SchemaGeneric", branch=branch) - for schema_node in await self.query( - schema=generic_schema, - branch=branch, - at=at, - filters=filters["generics"], - prefetch_relationships=True, - db=db, - ): - kind = f"{schema_node.namespace.value}{schema_node.name.value}" - schema.set( - name=kind, - schema=await self.convert_generic_schema_to_schema(schema_node=schema_node, db=db), - ) - - if not has_filters or filters["nodes"]: - node_schema = self.get(name="SchemaNode", branch=branch) - for schema_node in await self.query( - schema=node_schema, branch=branch, at=at, filters=filters["nodes"], prefetch_relationships=True, db=db - ): - kind = f"{schema_node.namespace.value}{schema_node.name.value}" - schema.set( - name=kind, - schema=await self.convert_node_schema_to_schema(schema_node=schema_node, db=db), - ) - - schema.process(validate_schema=validate_schema) - - return schema - - @classmethod - async def _prepare_node_data(cls, schema_node: Node, db: InfrahubDatabase) -> dict[str, Any]: - node_data = {"id": schema_node.id} - - # First pull all the local attributes at the top level, then convert all the local relationships - # for a standard node_schema, the relationships will be attributes and relationships - for attr_name in schema_node._attributes: - attr = getattr(schema_node, attr_name) - node_data[attr_name] = attr.get_value() - - for rel_name in schema_node._relationships: - if rel_name not in node_data: - if rel_name == "profiles": - continue - node_data[rel_name] = [] - - rm = getattr(schema_node, rel_name) - for rel in await rm.get(db=db): - item = await rel.get_peer(db=db) - item_data = {"id": item.id} - for item_name in item._attributes: - item_attr = getattr(item, item_name) - item_data[item_name] = item_attr.get_value() - - node_data[rel_name].append(item_data) - return node_data - - @classmethod - async def convert_node_schema_to_schema(cls, schema_node: Node, db: InfrahubDatabase) -> NodeSchema: - """Convert a schema_node object loaded from the database into NodeSchema object.""" - node_data = await cls._prepare_node_data(schema_node=schema_node, db=db) - return NodeSchema(**node_data) - - @classmethod - async def convert_generic_schema_to_schema(cls, schema_node: Node, db: InfrahubDatabase) -> GenericSchema: - """Convert a schema_node object loaded from the database into GenericSchema object.""" - node_data = await cls._prepare_node_data(schema_node=schema_node, db=db) - return GenericSchema(**node_data) diff --git a/backend/infrahub/core/task/user_task.py b/backend/infrahub/core/task/user_task.py index 034da5c049..a2fa6de467 100644 --- a/backend/infrahub/core/task/user_task.py +++ b/backend/infrahub/core/task/user_task.py @@ -18,7 +18,7 @@ from infrahub.core.protocols import CoreGenericAccount from infrahub.database import InfrahubDatabase - from infrahub.graphql import GraphqlContext + from infrahub.graphql.initialization import GraphqlContext from infrahub.services.protocols import InfrahubLogger diff --git a/backend/infrahub/core/utils.py b/backend/infrahub/core/utils.py index 8befab1a6e..239d542944 100644 --- a/backend/infrahub/core/utils.py +++ b/backend/infrahub/core/utils.py @@ -11,6 +11,7 @@ from infrahub.core.timestamp import Timestamp if TYPE_CHECKING: + from neo4j import Record from neo4j.graph import Node as Neo4jNode from infrahub.database import InfrahubDatabase @@ -25,7 +26,7 @@ async def add_relationship( branch_level: Optional[int] = None, at: Optional[Timestamp] = None, status=RelationshipStatus.ACTIVE, -): +) -> Record | None: create_rel_query = """ MATCH (s) WHERE %(id_func)s(s) = $src_node_id MATCH (d) WHERE %(id_func)s(d) = $dst_node_id @@ -51,7 +52,7 @@ async def add_relationship( return results[0][0] -async def delete_all_relationships_for_branch(branch_name: str, db: InfrahubDatabase): +async def delete_all_relationships_for_branch(branch_name: str, db: InfrahubDatabase) -> None: query = """ MATCH ()-[r { branch: $branch_name }]-() DELETE r """ @@ -60,7 +61,7 @@ async def delete_all_relationships_for_branch(branch_name: str, db: InfrahubData await db.execute_query(query=query, params=params, name="delete_all_relationships_for_branch") -async def update_relationships_to(ids: list[str], db: InfrahubDatabase, to: Timestamp = None): +async def update_relationships_to(ids: list[str], db: InfrahubDatabase, to: Timestamp = None) -> list[Record] | None: """Update the "to" field on one or multiple relationships.""" if not ids: return None @@ -86,7 +87,7 @@ async def get_paths_between_nodes( relationships: Optional[list[str]] = None, max_length: Optional[int] = None, print_query=False, -): +) -> list[Record]: """Return all paths between 2 nodes.""" length_limit = f"..{max_length}" if max_length else "" @@ -154,7 +155,7 @@ async def count_nodes(db: InfrahubDatabase, label: Optional[str] = None) -> int: return result[0][0] -async def delete_all_nodes(db: InfrahubDatabase): +async def delete_all_nodes(db: InfrahubDatabase) -> list[Record]: query = """ MATCH (n) DETACH DELETE n @@ -210,7 +211,7 @@ class _NewClass: _all_vars = set(dir(_OldClass) + dir(_NewClass)) -def props(x): +def props(x) -> dict[str, Any]: return {key: vars(x).get(key, getattr(x, key)) for key in dir(x) if key not in _all_vars} @@ -254,5 +255,5 @@ def __init_subclass__(cls, **meta_options): super_class.__init_subclass_with_meta__(**options) @classmethod - def __init_subclass_with_meta__(cls, **meta_options): + def __init_subclass_with_meta__(cls, **meta_options) -> None: """This method just terminates the super() chain""" diff --git a/backend/infrahub/core/validators/attribute/unique.py b/backend/infrahub/core/validators/attribute/unique.py index d77ea4e4c6..793db6a62c 100644 --- a/backend/infrahub/core/validators/attribute/unique.py +++ b/backend/infrahub/core/validators/attribute/unique.py @@ -91,7 +91,7 @@ async def get_paths(self) -> GroupedDataPaths: class AttributeUniquenessChecker(ConstraintCheckerInterface): query_classes = [AttributeUniqueUpdateValidatorQuery] - def __init__(self, db: InfrahubDatabase, branch: Optional[Branch]): + def __init__(self, db: InfrahubDatabase, branch: Optional[Branch]) -> None: self.db = db self.branch = branch diff --git a/backend/infrahub/core/validators/checker.py b/backend/infrahub/core/validators/checker.py index ea4dd300b5..1a7d3d1034 100644 --- a/backend/infrahub/core/validators/checker.py +++ b/backend/infrahub/core/validators/checker.py @@ -8,7 +8,7 @@ if TYPE_CHECKING: from infrahub.core.branch import Branch from infrahub.core.models import SchemaUpdateConstraintInfo - from infrahub.core.schema_manager import SchemaBranch + from infrahub.core.schema.schema_branch import SchemaBranch from infrahub.message_bus.messages.schema_validator_path import SchemaValidatorPathResponse from infrahub.services import InfrahubServices diff --git a/backend/infrahub/core/validators/determiner.py b/backend/infrahub/core/validators/determiner.py index 305db13a65..e3447c9797 100644 --- a/backend/infrahub/core/validators/determiner.py +++ b/backend/infrahub/core/validators/determiner.py @@ -10,7 +10,7 @@ from infrahub.core.path import SchemaPath from infrahub.core.schema import AttributeSchema, MainSchemaTypes from infrahub.core.schema.relationship_schema import RelationshipSchema -from infrahub.core.schema_manager import SchemaBranch +from infrahub.core.schema.schema_branch import SchemaBranch from infrahub.core.validators import CONSTRAINT_VALIDATOR_MAP from infrahub.log import get_logger @@ -18,7 +18,7 @@ class ConstraintValidatorDeterminer: - def __init__(self, schema_branch: SchemaBranch): + def __init__(self, schema_branch: SchemaBranch) -> None: self.schema_branch = schema_branch self._node_diffs_by_kind: dict[str, NodeDiff] = defaultdict(list) self._attribute_element_map: dict[str, dict[str, list[NodeDiff]]] = {} diff --git a/backend/infrahub/core/validators/interface.py b/backend/infrahub/core/validators/interface.py index 02db3577bd..16aa5fbe53 100644 --- a/backend/infrahub/core/validators/interface.py +++ b/backend/infrahub/core/validators/interface.py @@ -1,7 +1,7 @@ from abc import ABC, abstractmethod from typing import List -from infrahub.core.path import GroupedDataPaths # noqa: TCH001 +from infrahub.core.path import GroupedDataPaths from .model import SchemaConstraintValidatorRequest diff --git a/sync/examples/infrahub_to_peering-manager/peeringmanager/__init__.py b/backend/infrahub/core/validators/models/__init__.py similarity index 100% rename from sync/examples/infrahub_to_peering-manager/peeringmanager/__init__.py rename to backend/infrahub/core/validators/models/__init__.py diff --git a/backend/infrahub/core/validators/models/validate_migration.py b/backend/infrahub/core/validators/models/validate_migration.py new file mode 100644 index 0000000000..b6d61cc0a8 --- /dev/null +++ b/backend/infrahub/core/validators/models/validate_migration.py @@ -0,0 +1,14 @@ +from pydantic import BaseModel, ConfigDict + +from infrahub.core.branch import Branch +from infrahub.core.models import SchemaUpdateConstraintInfo +from infrahub.core.schema.schema_branch import SchemaBranch + + +class SchemaValidateMigrationData(BaseModel): + model_config = ConfigDict( + arbitrary_types_allowed=True, json_encoders={SchemaBranch: SchemaBranch.to_dict_schema_object} + ) + branch: Branch + schema_branch: SchemaBranch + constraints: list[SchemaUpdateConstraintInfo] diff --git a/backend/infrahub/core/validators/models/violation.py b/backend/infrahub/core/validators/models/violation.py new file mode 100644 index 0000000000..510eebd6dd --- /dev/null +++ b/backend/infrahub/core/validators/models/violation.py @@ -0,0 +1,22 @@ +from typing import Union + +from pydantic import BaseModel, Field + +from infrahub.core.branch import Branch +from infrahub.core.path import SchemaPath +from infrahub.core.schema import GenericSchema, NodeSchema + + +class SchemaConstraintValidatorRequest(BaseModel): + branch: Branch = Field(..., description="The name of the branch to target") + constraint_name: str = Field(..., description="The name of the constraint to validate") + node_schema: Union[NodeSchema, GenericSchema] = Field(..., description="Schema of Node or Generic to validate") + schema_path: SchemaPath = Field(..., description="SchemaPath to the element of the schema to validate") + + +class SchemaViolation(BaseModel): + node_id: str + node_kind: str + display_label: str + full_display_label: str + message: str = "" diff --git a/backend/infrahub/core/validators/node/generate_profile.py b/backend/infrahub/core/validators/node/generate_profile.py index 1784e65eb0..7181508ed6 100644 --- a/backend/infrahub/core/validators/node/generate_profile.py +++ b/backend/infrahub/core/validators/node/generate_profile.py @@ -24,7 +24,7 @@ def __init__( self, *args: Any, **kwargs: Any, - ): + ) -> None: super().__init__(*args, **kwargs) self.profile_kind = f"Profile{self.node_schema.kind}" @@ -61,7 +61,7 @@ async def get_paths(self) -> GroupedDataPaths: class NodeGenerateProfileChecker(ConstraintCheckerInterface): query_classes = [NodeGenerateProfileValidatorQuery] - def __init__(self, db: InfrahubDatabase, branch: Optional[Branch]): + def __init__(self, db: InfrahubDatabase, branch: Optional[Branch]) -> None: self.db = db self.branch = branch diff --git a/backend/infrahub/core/validators/node/hierarchy.py b/backend/infrahub/core/validators/node/hierarchy.py index 4411b258d0..a95a7c8f66 100644 --- a/backend/infrahub/core/validators/node/hierarchy.py +++ b/backend/infrahub/core/validators/node/hierarchy.py @@ -26,7 +26,7 @@ def __init__( check_children: bool = False, check_parent: bool = False, **kwargs: Any, - ): + ) -> None: self.check_children = check_children self.check_parent = check_parent super().__init__(**kwargs) @@ -142,7 +142,7 @@ async def get_paths(self) -> GroupedDataPaths: class NodeHierarchyChecker(ConstraintCheckerInterface): query_classes = [NodeHierarchyUpdateValidatorQuery] - def __init__(self, db: InfrahubDatabase, branch: Optional[Branch]): + def __init__(self, db: InfrahubDatabase, branch: Optional[Branch]) -> None: self.db = db self.branch = branch diff --git a/backend/infrahub/core/validators/node/inherit_from.py b/backend/infrahub/core/validators/node/inherit_from.py index c5f99dec8c..5ad49b093c 100644 --- a/backend/infrahub/core/validators/node/inherit_from.py +++ b/backend/infrahub/core/validators/node/inherit_from.py @@ -18,7 +18,7 @@ class NodeInheritFromChecker(ConstraintCheckerInterface): - def __init__(self, db: InfrahubDatabase, branch: Optional[Branch]): + def __init__(self, db: InfrahubDatabase, branch: Optional[Branch]) -> None: self.db = db self.branch = branch diff --git a/backend/infrahub/core/validators/relationship/count.py b/backend/infrahub/core/validators/relationship/count.py index 30c477205a..85ab9f1d29 100644 --- a/backend/infrahub/core/validators/relationship/count.py +++ b/backend/infrahub/core/validators/relationship/count.py @@ -25,7 +25,7 @@ def __init__( min_count_override: Optional[int] = None, max_count_override: Optional[int] = None, **kwargs: Any, - ): + ) -> None: self.min_count_override = min_count_override self.max_count_override = max_count_override super().__init__(**kwargs) @@ -146,7 +146,7 @@ async def get_paths(self) -> GroupedDataPaths: class RelationshipCountChecker(ConstraintCheckerInterface): query_classes = [RelationshipCountUpdateValidatorQuery] - def __init__(self, db: InfrahubDatabase, branch: Optional[Branch]): + def __init__(self, db: InfrahubDatabase, branch: Optional[Branch]) -> None: self.db = db self.branch = branch diff --git a/backend/infrahub/core/validators/relationship/optional.py b/backend/infrahub/core/validators/relationship/optional.py index 4b5aaa7b57..24dacb50a6 100644 --- a/backend/infrahub/core/validators/relationship/optional.py +++ b/backend/infrahub/core/validators/relationship/optional.py @@ -86,7 +86,7 @@ async def get_paths(self) -> GroupedDataPaths: class RelationshipOptionalChecker(ConstraintCheckerInterface): query_classes = [RelationshipOptionalUpdateValidatorQuery] - def __init__(self, db: InfrahubDatabase, branch: Optional[Branch]): + def __init__(self, db: InfrahubDatabase, branch: Optional[Branch]) -> None: self.db = db self.branch = branch diff --git a/backend/infrahub/core/validators/relationship/peer.py b/backend/infrahub/core/validators/relationship/peer.py index 885f1a9589..7ff63d069f 100644 --- a/backend/infrahub/core/validators/relationship/peer.py +++ b/backend/infrahub/core/validators/relationship/peer.py @@ -110,7 +110,7 @@ async def get_paths(self) -> GroupedDataPaths: class RelationshipPeerChecker(ConstraintCheckerInterface): query_classes = [RelationshipPeerUpdateValidatorQuery] - def __init__(self, db: InfrahubDatabase, branch: Optional[Branch]): + def __init__(self, db: InfrahubDatabase, branch: Optional[Branch]) -> None: self.db = db self.branch = branch diff --git a/backend/infrahub/core/validators/shared.py b/backend/infrahub/core/validators/shared.py index 543ff3efb3..2d1f91bd22 100644 --- a/backend/infrahub/core/validators/shared.py +++ b/backend/infrahub/core/validators/shared.py @@ -15,7 +15,7 @@ def __init__( node_schema: Union[NodeSchema, GenericSchema], schema_path: SchemaPath, **kwargs: Any, - ): + ) -> None: self.node_schema = node_schema self.schema_path = schema_path super().__init__(**kwargs) diff --git a/backend/infrahub/core/validators/tasks.py b/backend/infrahub/core/validators/tasks.py new file mode 100644 index 0000000000..8ba163eaf0 --- /dev/null +++ b/backend/infrahub/core/validators/tasks.py @@ -0,0 +1,44 @@ +from __future__ import annotations + +from infrahub_sdk.batch import InfrahubBatch +from prefect import flow + +from infrahub.message_bus.messages.schema_validator_path import ( + SchemaValidatorPathData, +) +from infrahub.message_bus.operations.schema.validator import schema_path_validate +from infrahub.services import services + +from .models.validate_migration import SchemaValidateMigrationData # noqa: TCH001 + + +@flow +async def schema_validate_migrations(message: SchemaValidateMigrationData) -> list[str]: + batch = InfrahubBatch(return_exceptions=True) + error_messages: list[str] = [] + service = services.service + + if not message.constraints: + return error_messages + + for constraint in message.constraints: + service.log.info( + f"Preparing validator for constraint {constraint.constraint_name!r} ({constraint.routing_key})", + branch=message.branch.name, + constraint_name=constraint.constraint_name, + routing_key=constraint.routing_key, + ) + + msg = SchemaValidatorPathData( + branch=message.branch, + constraint_name=constraint.constraint_name, + node_schema=message.schema_branch.get(name=constraint.path.schema_kind), + schema_path=constraint.path, + ) + batch.add(task=schema_path_validate, message=msg) + + async for _, result in batch.execute(): + for violation in result.violations: + error_messages.append(violation.message) + + return error_messages diff --git a/backend/infrahub/core/validators/uniqueness/checker.py b/backend/infrahub/core/validators/uniqueness/checker.py index 8bb058d81e..0a06e7afb2 100644 --- a/backend/infrahub/core/validators/uniqueness/checker.py +++ b/backend/infrahub/core/validators/uniqueness/checker.py @@ -46,7 +46,7 @@ def get_attribute_path_from_string( class UniquenessChecker(ConstraintCheckerInterface): def __init__( self, db: InfrahubDatabase, branch: Optional[Union[Branch, str]] = None, max_concurrent_execution: int = 5 - ): + ) -> None: self.db = db self.branch = branch self.semaphore = asyncio.Semaphore(max_concurrent_execution) diff --git a/backend/infrahub/core/validators/uniqueness/index.py b/backend/infrahub/core/validators/uniqueness/index.py index 5465040225..dc79573fea 100644 --- a/backend/infrahub/core/validators/uniqueness/index.py +++ b/backend/infrahub/core/validators/uniqueness/index.py @@ -31,7 +31,7 @@ def __hash__(self) -> int: class UniquenessQueryResultsIndex: - def __init__(self, query_results: Iterable[QueryResult], exclude_node_ids: Optional[set[str]] = None): + def __init__(self, query_results: Iterable[QueryResult], exclude_node_ids: Optional[set[str]] = None) -> None: self._relationship_index: dict[str, dict[str, set[str]]] = {} self._attribute_index: dict[str, dict[Any, set[str]]] = {} self._node_index: dict[str, dict[str, Any]] = {} diff --git a/backend/infrahub/core/validators/uniqueness/query.py b/backend/infrahub/core/validators/uniqueness/query.py index d709fb50f3..c7ab8fbf11 100644 --- a/backend/infrahub/core/validators/uniqueness/query.py +++ b/backend/infrahub/core/validators/uniqueness/query.py @@ -21,7 +21,7 @@ def __init__( query_request: NodeUniquenessQueryRequest, min_count_required: int = 1, **kwargs: Any, - ): + ) -> None: self.query_request = query_request self.min_count_required = min_count_required super().__init__(**kwargs) diff --git a/backend/infrahub/database/__init__.py b/backend/infrahub/database/__init__.py index b5e59977cd..e49418909f 100644 --- a/backend/infrahub/database/__init__.py +++ b/backend/infrahub/database/__init__.py @@ -2,7 +2,7 @@ import asyncio import random -from typing import TYPE_CHECKING, Any, Optional, Union +from typing import TYPE_CHECKING, Any, Callable, Coroutine, Optional, TypeVar, Union from neo4j import ( READ_ACCESS, @@ -40,11 +40,12 @@ from infrahub.core.branch import Branch from infrahub.core.schema import MainSchemaTypes, NodeSchema - from infrahub.core.schema_manager import SchemaBranch + from infrahub.core.schema.schema_branch import SchemaBranch from .manager import DatabaseManager validated_database = {} +R = TypeVar("R") log = get_logger() @@ -70,7 +71,7 @@ def get_branch_name(branch: Optional[Union[Branch, str]] = None) -> str: class DatabaseSchemaManager: - def __init__(self, db: InfrahubDatabase): + def __init__(self, db: InfrahubDatabase) -> None: self._db = db def get(self, name: str, branch: Optional[Union[Branch, str]] = None, duplicate: bool = True) -> MainSchemaTypes: @@ -133,7 +134,7 @@ def __init__( session: Optional[AsyncSession] = None, session_mode: InfrahubDatabaseSessionMode = InfrahubDatabaseSessionMode.WRITE, transaction: Optional[AsyncTransaction] = None, - ): + ) -> None: self._mode: InfrahubDatabaseMode = mode self._driver: AsyncDriver = driver self._session: Optional[AsyncSession] = session @@ -400,7 +401,7 @@ async def get_db(retry: int = 0) -> AsyncDriver: encrypted=config.SETTINGS.database.tls_enabled, trusted_certificates=trusted_certificates, notifications_disabled_categories=[NotificationDisabledCategory.UNRECOGNIZED], - notifications_min_severity=NotificationMinimumSeverity.OFF, + notifications_min_severity=NotificationMinimumSeverity.WARNING, ) if config.SETTINGS.database.database_name not in validated_database: @@ -411,9 +412,12 @@ async def get_db(retry: int = 0) -> AsyncDriver: return driver -def retry_db_transaction(name: str): - def func_wrapper(func): - async def wrapper(*args, **kwargs): +def retry_db_transaction( + name: str, +) -> Callable[[Callable[..., Coroutine[Any, Any, R]]], Callable[..., Coroutine[Any, Any, R]]]: + def func_wrapper(func: Callable[..., Coroutine[Any, Any, R]]) -> Callable[..., Coroutine[Any, Any, R]]: + async def wrapper(*args: Any, **kwargs: Any) -> R: + error = Exception() for attempt in range(1, config.SETTINGS.database.retry_limit + 1): try: return await func(*args, **kwargs) @@ -423,11 +427,13 @@ async def wrapper(*args, **kwargs): f"Retrying database transaction, attempt {attempt}/{config.SETTINGS.database.retry_limit}", retry_time=retry_time, ) - log.debug("database transaction failed", message=exc.message) + log.debug("Database transaction failed", message=exc.message) TRANSACTION_RETRIES.labels(name).inc() await asyncio.sleep(retry_time) if attempt == config.SETTINGS.database.retry_limit: - raise + error = exc + break + raise error return wrapper diff --git a/backend/infrahub/database/index.py b/backend/infrahub/database/index.py index 9120a64341..3f0ec27718 100644 --- a/backend/infrahub/database/index.py +++ b/backend/infrahub/database/index.py @@ -33,7 +33,7 @@ def get_drop_query(self) -> str: class IndexManagerBase(ABC): - def __init__(self, db: InfrahubDatabase): + def __init__(self, db: InfrahubDatabase) -> None: self.db = db self.nodes: list[IndexItem] = [] diff --git a/backend/infrahub/database/manager.py b/backend/infrahub/database/manager.py index 7b70a1efaf..321c07c1b9 100644 --- a/backend/infrahub/database/manager.py +++ b/backend/infrahub/database/manager.py @@ -11,5 +11,5 @@ class DatabaseManager(ABC): index: IndexManagerBase - def __init__(self, db: InfrahubDatabase): + def __init__(self, db: InfrahubDatabase) -> None: self.db = db diff --git a/backend/infrahub/database/memgraph.py b/backend/infrahub/database/memgraph.py index 1922178151..0a9509478c 100644 --- a/backend/infrahub/database/memgraph.py +++ b/backend/infrahub/database/memgraph.py @@ -54,6 +54,6 @@ async def list(self) -> list[IndexInfo]: class DatabaseManagerMemgraph(DatabaseManager): - def __init__(self, db: InfrahubDatabase): + def __init__(self, db: InfrahubDatabase) -> None: super().__init__(db=db) self.index = IndexManagerMemgraph(db=db) diff --git a/backend/infrahub/database/neo4j.py b/backend/infrahub/database/neo4j.py index 45170988ce..8e08bd4279 100644 --- a/backend/infrahub/database/neo4j.py +++ b/backend/infrahub/database/neo4j.py @@ -69,6 +69,6 @@ async def list(self) -> list[IndexInfo]: class DatabaseManagerNeo4j(DatabaseManager): - def __init__(self, db: InfrahubDatabase): + def __init__(self, db: InfrahubDatabase) -> None: super().__init__(db=db) self.index = IndexManagerNeo4j(db=db) diff --git a/backend/infrahub/events/__init__.py b/backend/infrahub/events/__init__.py new file mode 100644 index 0000000000..857036ba61 --- /dev/null +++ b/backend/infrahub/events/__init__.py @@ -0,0 +1,4 @@ +from .models import EventMeta, InfrahubEvent +from .node_action import NodeMutatedEvent + +__all__ = ["EventMeta", "InfrahubEvent", "NodeMutatedEvent"] diff --git a/backend/infrahub/events/constants.py b/backend/infrahub/events/constants.py new file mode 100644 index 0000000000..9c5c628d46 --- /dev/null +++ b/backend/infrahub/events/constants.py @@ -0,0 +1 @@ +EVENT_NAMESPACE = "infrahub" diff --git a/backend/infrahub/events/models.py b/backend/infrahub/events/models.py new file mode 100644 index 0000000000..1717fc97b8 --- /dev/null +++ b/backend/infrahub/events/models.py @@ -0,0 +1,93 @@ +from typing import Any + +from pydantic import BaseModel, Field + +from infrahub.message_bus import InfrahubMessage, Meta + +from .constants import EVENT_NAMESPACE + + +class EventMeta(BaseModel): + request_id: str = "" + account_id: str = "" + initiator_id: str | None = Field( + default=None, description="The worker identity of the initial sender of this message" + ) + + +class InfrahubEvent(BaseModel): + meta: EventMeta | None = None + + def get_event_namespace(self) -> str: + return EVENT_NAMESPACE + + def get_name(self) -> str: + return f"{self.get_event_namespace()}.unknown" + + def get_resource(self) -> dict[str, str]: + raise NotImplementedError + + def get_message(self) -> InfrahubMessage: + raise NotImplementedError + + def get_related(self) -> list[dict[str, str]]: + related: list[dict[str, str]] = [] + + if not self.meta: + return related + + if self.meta.account_id: + related.append( + { + "prefect.resource.id": f"infrahub.account.{self.meta.account_id}", + "prefect.resource.role": "account", + } + ) + + if self.meta.request_id: + related.append( + { + "prefect.resource.id": f"infrahub.request.{self.meta.request_id}", + "prefect.resource.role": "request", + } + ) + + if self.meta.initiator_id: + related.append( + { + "prefect.resource.id": f"infrahub.source.{self.meta.initiator_id}", + "prefect.resource.role": "event_source", + } + ) + + return related + + def get_payload(self) -> dict[str, Any]: + return {} + + def get_message_meta(self) -> Meta: + meta = Meta() + if not self.meta: + return meta + + if self.meta.initiator_id: + meta.initiator_id = self.meta.initiator_id + if self.meta.request_id: + meta.initiator_id = self.meta.request_id + + return meta + + +class InfrahubBranchEvent(InfrahubEvent): # pylint: disable=abstract-method + branch: str = Field(..., description="The branch on which the event happend") + + def get_related(self) -> list[dict[str, str]]: + related = super().get_related() + related.append( + { + "prefect.resource.id": "infrahub.branch", + "prefect.resource.name": self.branch, + "prefect.resource.role": "branch", + } + ) + return related diff --git a/backend/infrahub/events/node_action.py b/backend/infrahub/events/node_action.py new file mode 100644 index 0000000000..ad40f664d9 --- /dev/null +++ b/backend/infrahub/events/node_action.py @@ -0,0 +1,40 @@ +from typing import Any + +from pydantic import Field + +from infrahub.core.constants import MutationAction +from infrahub.message_bus.messages.event_node_mutated import EventNodeMutated + +from .models import InfrahubBranchEvent + + +class NodeMutatedEvent(InfrahubBranchEvent): + """Event generated when a node has been mutated""" + + kind: str = Field(..., description="The type of object modified") + node_id: str = Field(..., description="The ID of the mutated node") + action: MutationAction = Field(..., description="The action taken on the node") + data: dict[str, Any] = Field(..., description="Data on modified object") + + def get_name(self) -> str: + return f"{self.get_event_namespace()}.node.{self.action.value}" + + def get_resource(self) -> dict[str, str]: + return { + "prefect.resource.id": f"infrahub.node.{self.node_id}", + "infrahub.node.kind": self.kind, + "infrahub.node.action": self.action.value, + } + + def get_payload(self) -> dict[str, Any]: + return self.data + + def get_message(self) -> EventNodeMutated: + return EventNodeMutated( + branch=self.branch, + kind=self.kind, + node_id=self.node_id, + action=self.action.value, + data=self.data, + meta=self.get_message_meta(), + ) diff --git a/backend/infrahub/exceptions.py b/backend/infrahub/exceptions.py index 2451a91d6e..c5b5b2d3a9 100644 --- a/backend/infrahub/exceptions.py +++ b/backend/infrahub/exceptions.py @@ -20,7 +20,7 @@ def api_response(self) -> dict[str, Any]: class RPCError(Error): HTTP_CODE: int = 502 - def __init__(self, message: str): + def __init__(self, message: str) -> None: self.message = message @@ -32,7 +32,7 @@ class DatabaseError(Error): HTTP_CODE: int = 503 DESCRIPTION = "Database unavailable" - def __init__(self, message: str): + def __init__(self, message: str) -> None: self.message = message super().__init__(self.message) @@ -44,12 +44,12 @@ class LockError(Error): class GraphQLQueryError(Error): HTTP_CODE = 502 - def __init__(self, errors: list): + def __init__(self, errors: list) -> None: self.errors = errors class RepositoryError(Error): - def __init__(self, identifier: str, message: Optional[str] = None): + def __init__(self, identifier: str, message: Optional[str] = None) -> None: self.identifier = identifier self.message = message or f"An error occurred with GitRepository '{identifier}'." super().__init__(self.message) @@ -58,7 +58,7 @@ def __init__(self, identifier: str, message: Optional[str] = None): class CommitNotFoundError(Error): HTTP_CODE: int = 400 - def __init__(self, identifier: str, commit: str, message: Optional[str] = None): + def __init__(self, identifier: str, commit: str, message: Optional[str] = None) -> None: self.identifier = identifier self.commit = commit self.message = message or f"Commit {commit} not found with GitRepository '{identifier}'." @@ -68,7 +68,7 @@ def __init__(self, identifier: str, commit: str, message: Optional[str] = None): class DataTypeNotFoundError(Error): HTTP_CODE: int = 400 - def __init__(self, name: str, message: Optional[str] = None): + def __init__(self, name: str, message: Optional[str] = None) -> None: self.name = name self.message = message or f"Unable to find the DataType '{name}'." super().__init__(self.message) @@ -77,7 +77,7 @@ def __init__(self, name: str, message: Optional[str] = None): class RepositoryFileNotFoundError(Error): HTTP_CODE: int = 404 - def __init__(self, repository_name: str, location: str, commit: str, message: Optional[str] = None): + def __init__(self, repository_name: str, location: str, commit: str, message: Optional[str] = None) -> None: self.repository_name = repository_name self.location = location self.commit = commit @@ -88,7 +88,7 @@ def __init__(self, repository_name: str, location: str, commit: str, message: Op class FileOutOfRepositoryError(Error): HTTP_CODE: int = 403 - def __init__(self, repository_name: str, location: str, commit: str, message: Optional[str] = None): + def __init__(self, repository_name: str, location: str, commit: str, message: Optional[str] = None) -> None: self.repository_name = repository_name self.location = location self.commit = commit @@ -97,7 +97,7 @@ def __init__(self, repository_name: str, location: str, commit: str, message: Op class TransformError(Error): - def __init__(self, repository_name: str, location: str, commit: str, message: Optional[str] = None): + def __init__(self, repository_name: str, location: str, commit: str, message: Optional[str] = None) -> None: self.repository_name = repository_name self.location = location self.commit = commit @@ -110,7 +110,7 @@ def __init__(self, repository_name: str, location: str, commit: str, message: Op class CheckError(Error): def __init__( self, repository_name: str, location: str, class_name: str, commit: str, message: Optional[str] = None - ): + ) -> None: self.repository_name = repository_name self.location = location self.commit = commit @@ -123,7 +123,7 @@ def __init__( class TransformNotFoundError(TransformError): - def __init__(self, repository_name: str, location: str, commit: str, message: Optional[str] = None): + def __init__(self, repository_name: str, location: str, commit: str, message: Optional[str] = None) -> None: self.message = ( message or f"Unable to locate the transform function at '{repository_name}::{commit}::{location}'." ) @@ -133,7 +133,7 @@ def __init__(self, repository_name: str, location: str, commit: str, message: Op class BranchNotFoundError(Error): HTTP_CODE: int = 400 - def __init__(self, identifier: str, message: Optional[str] = None): + def __init__(self, identifier: str, message: Optional[str] = None) -> None: self.identifier = identifier self.message = message or f"Branch: {identifier} not found." super().__init__(self.message) @@ -144,7 +144,7 @@ class NodeNotFoundError(Error): def __init__( self, node_type: str, identifier: str, branch_name: Optional[str] = None, message: Optional[str] = None - ): + ) -> None: self.node_type = node_type self.identifier = identifier self.branch_name = branch_name @@ -161,7 +161,7 @@ def __str__(self) -> str: class ResourceNotFoundError(Error): HTTP_CODE: int = 404 - def __init__(self, message: Optional[str] = None): + def __init__(self, message: Optional[str] = None) -> None: self.message = message or "The requested resource was not found" super().__init__(self.message) @@ -170,7 +170,7 @@ class AuthorizationError(Error): HTTP_CODE: int = 401 message: str = "Access to the requested resource was denied" - def __init__(self, message: Optional[str] = None): + def __init__(self, message: Optional[str] = None) -> None: self.message = message or self.message super().__init__(self.message) @@ -179,7 +179,7 @@ class PermissionDeniedError(Error): HTTP_CODE: int = 403 message: str = "The requested operation was not authorized" - def __init__(self, message: Optional[str] = None): + def __init__(self, message: Optional[str] = None) -> None: self.message = message or self.message super().__init__(self.message) @@ -188,7 +188,7 @@ class ProcessingError(Error): HTTP_CODE: int = 400 message: str = "Unable to process the request" - def __init__(self, message: Optional[str] = None): + def __init__(self, message: Optional[str] = None) -> None: self.message = message or self.message super().__init__(self.message) @@ -197,7 +197,7 @@ class PoolExhaustedError(Error): HTTP_CODE: int = 409 message: str = "No more resources available in the pool" - def __init__(self, message: Optional[str] = None): + def __init__(self, message: Optional[str] = None) -> None: self.message = message or self.message super().__init__(self.message) @@ -205,7 +205,7 @@ def __init__(self, message: Optional[str] = None): class SchemaNotFoundError(Error): HTTP_CODE: int = 422 - def __init__(self, branch_name: str, identifier: str, message: Optional[str] = None): + def __init__(self, branch_name: str, identifier: str, message: Optional[str] = None) -> None: self.branch_name = branch_name self.identifier = identifier self.message = message or f"Unable to find the schema {identifier} in the database." @@ -219,7 +219,7 @@ def __str__(self) -> str: class QueryError(Error): - def __init__(self, query: str, params: dict, message: str = "Unable to execute the CYPHER query."): + def __init__(self, query: str, params: dict, message: str = "Unable to execute the CYPHER query.") -> None: self.query = query self.params = params @@ -237,21 +237,28 @@ def __str__(self) -> str: class QueryValidationError(Error): HTTP_CODE = 400 - def __init__(self, message: str): + def __init__(self, message: str) -> None: + self.message = message + + +class GatewayError(Error): + HTTP_CODE = 502 + + def __init__(self, message: str) -> None: self.message = message class MigrationError(Error): HTTP_CODE = 502 - def __init__(self, message: str): + def __init__(self, message: str) -> None: self.message = message class ValidationError(Error): HTTP_CODE = 422 - def __init__(self, input_value: Union[str, dict, list]): + def __init__(self, input_value: Union[str, dict, list]) -> None: self.message = "" self.location = None self.messages = {} @@ -285,7 +292,7 @@ def __str__(self) -> str: class DiffError(Error): HTTP_CODE = 400 - def __init__(self, message: str): + def __init__(self, message: str) -> None: self.message = message @@ -293,3 +300,20 @@ class DiffRangeValidationError(DiffError): ... class DiffFromRequiredOnDefaultBranchError(DiffError): ... + + +class HTTPServerError(Error): + """Errors raised when communicating with external HTTP servers""" + + HTTP_CODE = 502 + + def __init__(self, message: str) -> None: + self.message = message + + +class HTTPServerTimeoutError(HTTPServerError): + HTTP_CODE = 504 + + +class HTTPServerSSLError(HTTPServerError): + HTTP_CODE = 503 diff --git a/backend/infrahub/git/integrator.py b/backend/infrahub/git/integrator.py index 8f3b807949..c7b199af6d 100644 --- a/backend/infrahub/git/integrator.py +++ b/backend/infrahub/git/integrator.py @@ -16,6 +16,16 @@ InfrahubRepositoryConfig, ValidationError, ) +from infrahub_sdk.protocols import ( + CoreArtifact, + CoreArtifactDefinition, + CoreCheckDefinition, + CoreGeneratorDefinition, + CoreGraphQLQuery, + CoreTransformation, + CoreTransformJinja2, + CoreTransformPython, +) from infrahub_sdk.schema import ( InfrahubCheckDefinitionConfig, InfrahubGeneratorDefinitionConfig, @@ -191,7 +201,7 @@ async def import_jinja2_transforms( transforms_in_graph = { transform.name.value: transform for transform in await self.sdk.filters( - kind=InfrahubKind.TRANSFORMJINJA2, branch=branch_name, repository__ids=[str(self.id)] + kind=CoreTransformJinja2, branch=branch_name, repository__ids=[str(self.id)] ) } @@ -256,18 +266,18 @@ async def import_jinja2_transforms( ) await transforms_in_graph[transform_name].delete() - async def create_jinja2_transform(self, branch_name: str, data: InfrahubRepositoryJinja2) -> InfrahubNode: + async def create_jinja2_transform(self, branch_name: str, data: InfrahubRepositoryJinja2) -> CoreTransformJinja2: schema = await self.sdk.schema.get(kind=InfrahubKind.TRANSFORMJINJA2, branch=branch_name) create_payload = self.sdk.schema.generate_payload_create( schema=schema, data=data.payload, source=self.id, is_protected=True ) - obj = await self.sdk.create(kind=InfrahubKind.TRANSFORMJINJA2, branch=branch_name, **create_payload) + obj = await self.sdk.create(kind=CoreTransformJinja2, branch=branch_name, **create_payload) await obj.save() return obj @classmethod async def compare_jinja2_transform( - cls, existing_transform: InfrahubNode, local_transform: InfrahubRepositoryJinja2 + cls, existing_transform: CoreTransformJinja2, local_transform: InfrahubRepositoryJinja2 ) -> bool: # pylint: disable=no-member if ( @@ -280,7 +290,7 @@ async def compare_jinja2_transform( return True async def update_jinja2_transform( - self, existing_transform: InfrahubNode, local_transform: InfrahubRepositoryJinja2 + self, existing_transform: CoreTransformJinja2, local_transform: InfrahubRepositoryJinja2 ) -> None: # pylint: disable=no-member if existing_transform.description.value != local_transform.description: @@ -303,7 +313,7 @@ async def import_artifact_definitions( artifact_defs_in_graph = { artdef.name.value: artdef - for artdef in await self.sdk.filters(kind=InfrahubKind.ARTIFACTDEFINITION, branch=branch_name) + for artdef in await self.sdk.filters(kind=CoreArtifactDefinition, branch=branch_name) } local_artifact_defs: dict[str, InfrahubRepositoryArtifactDefinitionConfig] = {} @@ -364,7 +374,7 @@ async def create_artifact_definition( @classmethod async def compare_artifact_definition( cls, - existing_artifact_definition: InfrahubNode, + existing_artifact_definition: CoreArtifactDefinition, local_artifact_definition: InfrahubRepositoryArtifactDefinitionConfig, ) -> bool: # pylint: disable=no-member @@ -379,7 +389,7 @@ async def compare_artifact_definition( async def update_artifact_definition( self, - existing_artifact_definition: InfrahubNode, + existing_artifact_definition: CoreArtifactDefinition, local_artifact_definition: InfrahubRepositoryArtifactDefinitionConfig, ) -> None: # pylint: disable=no-member @@ -533,7 +543,7 @@ async def import_all_graphql_query( queries_in_graph = { query.name.value: query for query in await self.sdk.filters( - kind=InfrahubKind.GRAPHQLQUERY, branch=branch_name, repository__ids=[str(self.id)] + kind=CoreGraphQLQuery, branch=branch_name, repository__ids=[str(self.id)] ) } @@ -574,7 +584,7 @@ async def import_all_graphql_query( ) await graph_query.delete() - async def create_graphql_query(self, branch_name: str, name: str, query_string: str) -> InfrahubNode: + async def create_graphql_query(self, branch_name: str, name: str, query_string: str) -> CoreGraphQLQuery: data = {"name": name, "query": query_string, "repository": self.id} schema = await self.sdk.schema.get(kind=InfrahubKind.GRAPHQLQUERY, branch=branch_name) @@ -584,7 +594,7 @@ async def create_graphql_query(self, branch_name: str, name: str, query_string: source=self.id, is_protected=True, ) - obj = await self.sdk.create(kind=InfrahubKind.GRAPHQLQUERY, branch=branch_name, **create_payload) + obj = await self.sdk.create(kind=CoreGraphQLQuery, branch=branch_name, **create_payload) await obj.save() return obj @@ -629,7 +639,7 @@ async def import_python_check_definitions( check_definition_in_graph = { check.name.value: check for check in await self.sdk.filters( - kind=InfrahubKind.CHECKDEFINITION, branch=branch_name, repository__ids=[str(self.id)] + kind=CoreCheckDefinition, branch=branch_name, repository__ids=[str(self.id)] ) } @@ -697,7 +707,7 @@ async def import_generator_definitions( generator_definition_in_graph = { generator.name.value: generator for generator in await self.sdk.filters( - kind=InfrahubKind.GENERATORDEFINITION, branch=branch_name, repository__ids=[str(self.id)] + kind=CoreGeneratorDefinition, branch=branch_name, repository__ids=[str(self.id)] ) } @@ -744,7 +754,10 @@ async def import_generator_definitions( await generator_definition_in_graph[generator_name].delete() async def _generator_requires_update( - self, generator: InfrahubGeneratorDefinitionConfig, existing_generator: InfrahubNode, branch_name: str + self, + generator: InfrahubGeneratorDefinitionConfig, + existing_generator: CoreGeneratorDefinition, + branch_name: str, ) -> bool: graphql_queries = await self.sdk.filters( kind=InfrahubKind.GRAPHQLQUERY, branch=branch_name, name__value=generator.query, populate_store=True @@ -955,7 +968,7 @@ async def _create_generator_definition( async def _update_generator_definition( self, generator: InfrahubGeneratorDefinitionConfig, - existing_generator: InfrahubNode, + existing_generator: CoreGeneratorDefinition, ) -> None: if existing_generator.query.id != generator.query: existing_generator.query = {"id": generator.query, "source": str(self.id), "is_protected": True} @@ -977,7 +990,9 @@ async def _update_generator_definition( await existing_generator.save() - async def create_python_check_definition(self, branch_name: str, check: CheckDefinitionInformation) -> InfrahubNode: + async def create_python_check_definition( + self, branch_name: str, check: CheckDefinitionInformation + ) -> CoreCheckDefinition: data = { "name": check.name, "repository": check.repository, @@ -999,7 +1014,7 @@ async def create_python_check_definition(self, branch_name: str, check: CheckDef source=self.id, is_protected=True, ) - obj = await self.sdk.create(kind=InfrahubKind.CHECKDEFINITION, branch=branch_name, **create_payload) + obj = await self.sdk.create(kind=CoreCheckDefinition, branch=branch_name, **create_payload) await obj.save() return obj @@ -1007,7 +1022,7 @@ async def create_python_check_definition(self, branch_name: str, check: CheckDef async def update_python_check_definition( self, check: CheckDefinitionInformation, - existing_check: InfrahubNode, + existing_check: CoreCheckDefinition, ) -> None: if existing_check.query.id != check.query: existing_check.query = {"id": check.query, "source": str(self.id), "is_protected": True} @@ -1025,7 +1040,7 @@ async def update_python_check_definition( @classmethod async def compare_python_check_definition( - cls, check: CheckDefinitionInformation, existing_check: InfrahubNode + cls, check: CheckDefinitionInformation, existing_check: CoreCheckDefinition ) -> bool: """Compare an existing Python Check Object with a Check Class and identify if we need to update the object in the database.""" @@ -1040,7 +1055,9 @@ async def compare_python_check_definition( return False return True - async def create_python_transform(self, branch_name: str, transform: TransformPythonInformation) -> InfrahubNode: + async def create_python_transform( + self, branch_name: str, transform: TransformPythonInformation + ) -> CoreTransformPython: schema = await self.sdk.schema.get(kind=InfrahubKind.TRANSFORMPYTHON, branch=branch_name) data = { "name": transform.name, @@ -1056,12 +1073,12 @@ async def create_python_transform(self, branch_name: str, transform: TransformPy source=self.id, is_protected=True, ) - obj = await self.sdk.create(kind=InfrahubKind.TRANSFORMPYTHON, branch=branch_name, **create_payload) + obj = await self.sdk.create(kind=CoreTransformPython, branch=branch_name, **create_payload) await obj.save() return obj async def update_python_transform( - self, existing_transform: InfrahubNode, local_transform: TransformPythonInformation + self, existing_transform: CoreTransformPython, local_transform: TransformPythonInformation ) -> None: if existing_transform.query.id != local_transform.query: existing_transform.query = {"id": local_transform.query, "source": str(self.id), "is_protected": True} @@ -1076,7 +1093,7 @@ async def update_python_transform( @classmethod async def compare_python_transform( - cls, existing_transform: InfrahubNode, local_transform: TransformPythonInformation + cls, existing_transform: CoreTransformPython, local_transform: TransformPythonInformation ) -> bool: if ( existing_transform.query.id != local_transform.query @@ -1244,11 +1261,11 @@ async def artifact_generate( self, branch_name: str, commit: str, - artifact: InfrahubNode, + artifact: CoreArtifact, target: InfrahubNode, - definition: InfrahubNode, - transformation: InfrahubNode, - query: InfrahubNode, + definition: CoreArtifactDefinition, + transformation: CoreTransformation, + query: CoreGraphQLQuery, ) -> ArtifactGenerateResult: variables = target.extract(params=definition.parameters.value) response = await self.sdk.query_gql_query( @@ -1298,7 +1315,7 @@ async def artifact_generate( return ArtifactGenerateResult(changed=True, checksum=checksum, storage_id=storage_id, artifact_id=artifact.id) async def render_artifact( - self, artifact: InfrahubNode, message: Union[messages.CheckArtifactCreate, messages.RequestArtifactGenerate] + self, artifact: CoreArtifact, message: Union[messages.CheckArtifactCreate, messages.RequestArtifactGenerate] ) -> ArtifactGenerateResult: response = await self.sdk.query_gql_query( name=message.query, diff --git a/backend/infrahub/git_credential/askpass.py b/backend/infrahub/git_credential/askpass.py index e05aeb8f72..eab658f800 100644 --- a/backend/infrahub/git_credential/askpass.py +++ b/backend/infrahub/git_credential/askpass.py @@ -18,7 +18,7 @@ def askpass( text: Optional[list[str]] = typer.Argument(None), config_file: str = typer.Option("infrahub.toml", envvar="INFRAHUB_CONFIG"), -): +) -> None: config.SETTINGS.initialize_and_exit(config_file=config_file) text = text or sys.stdin.read().strip() diff --git a/backend/infrahub/graphql/__init__.py b/backend/infrahub/graphql/__init__.py index cf7b144348..e69de29bb2 100644 --- a/backend/infrahub/graphql/__init__.py +++ b/backend/infrahub/graphql/__init__.py @@ -1,99 +0,0 @@ -from __future__ import annotations - -from dataclasses import dataclass -from typing import TYPE_CHECKING, Optional, Union - -from starlette.background import BackgroundTasks - -from infrahub.core import registry -from infrahub.core.timestamp import Timestamp - -from .manager import GraphQLSchemaManager - -if TYPE_CHECKING: - from graphql import GraphQLSchema - from starlette.requests import HTTPConnection - - from infrahub.auth import AccountSession - from infrahub.core.branch import Branch - from infrahub.database import InfrahubDatabase - from infrahub.services import InfrahubServices - - -@dataclass -class GraphqlParams: - schema: GraphQLSchema - context: GraphqlContext - - -@dataclass -class GraphqlContext: - db: InfrahubDatabase - branch: Branch - types: dict - at: Optional[Timestamp] = None - related_node_ids: Optional[set] = None - service: Optional[InfrahubServices] = None - account_session: Optional[AccountSession] = None - background: Optional[BackgroundTasks] = None - request: Optional[HTTPConnection] = None - - -def prepare_graphql_params( - db: InfrahubDatabase, - branch: Union[Branch, str], - at: Optional[Union[Timestamp, str]] = None, - account_session: Optional[AccountSession] = None, - request: Optional[HTTPConnection] = None, - service: Optional[InfrahubServices] = None, - include_query: bool = True, - include_mutation: bool = True, - include_subscription: bool = True, - include_types: bool = True, -) -> GraphqlParams: - branch = registry.get_branch_from_registry(branch=branch) - schema = registry.schema.get_schema_branch(name=branch.name) - - gqlm = schema.get_graphql_manager() - gql_schema = schema.get_graphql_schema( - include_query=include_query, - include_mutation=include_mutation, - include_subscription=include_subscription, - include_types=include_types, - ) - - if request and not service: - service = request.app.state.service - - return GraphqlParams( - schema=gql_schema, - context=GraphqlContext( - db=db, - branch=branch, - at=Timestamp(at), - types=gqlm._graphql_types, - related_node_ids=set(), - background=BackgroundTasks(), - request=request, - service=service, - account_session=account_session, - ), - ) - - -def generate_graphql_schema( - db: InfrahubDatabase, # pylint: disable=unused-argument - branch: Union[Branch, str], - include_query: bool = True, - include_mutation: bool = True, - include_subscription: bool = True, - include_types: bool = True, -) -> GraphQLSchema: - branch = registry.get_branch_from_registry(branch) - schema = registry.schema.get_schema_branch(name=branch.name) - return GraphQLSchemaManager(schema=schema).generate( - include_query=include_query, - include_mutation=include_mutation, - include_subscription=include_subscription, - include_types=include_types, - ) diff --git a/backend/infrahub/graphql/analyzer.py b/backend/infrahub/graphql/analyzer.py index f686e5ab6f..6aee1987c2 100644 --- a/backend/infrahub/graphql/analyzer.py +++ b/backend/infrahub/graphql/analyzer.py @@ -1,9 +1,6 @@ from typing import Any, Optional -from graphql import ( - GraphQLSchema, - OperationType, -) +from graphql import GraphQLSchema, OperationType from infrahub_sdk.analyzer import GraphQLQueryAnalyzer from infrahub_sdk.utils import extract_fields @@ -12,8 +9,17 @@ class InfrahubGraphQLQueryAnalyzer(GraphQLQueryAnalyzer): - def __init__(self, query: str, schema: Optional[GraphQLSchema] = None, branch: Optional[Branch] = None): + def __init__( + self, + query: str, + query_variables: Optional[dict[str, Any]] = None, + schema: Optional[GraphQLSchema] = None, + operation_name: Optional[str] = None, + branch: Optional[Branch] = None, + ) -> None: self.branch: Optional[Branch] = branch + self.operation_name: Optional[str] = operation_name + self.query_variables: dict[str, Any] = query_variables or {} super().__init__(query=query, schema=schema) async def get_models_in_use(self, types: dict[str, Any]) -> set[str]: diff --git a/backend/infrahub/graphql/api/dependencies.py b/backend/infrahub/graphql/api/dependencies.py index 71aa7574f5..7bd391d131 100644 --- a/backend/infrahub/graphql/api/dependencies.py +++ b/backend/infrahub/graphql/api/dependencies.py @@ -5,9 +5,17 @@ from ..app import InfrahubGraphQLApp from ..auth.query_permission_checker.anonymous_checker import AnonymousGraphQLPermissionChecker from ..auth.query_permission_checker.checker import GraphQLQueryPermissionChecker +from ..auth.query_permission_checker.default_branch_checker import DefaultBranchPermissionChecker from ..auth.query_permission_checker.default_checker import DefaultGraphQLPermissionChecker +from ..auth.query_permission_checker.merge_operation_checker import MergeBranchPermissionChecker +from ..auth.query_permission_checker.object_permission_checker import ( + AccountManagerPermissionChecker, + ObjectPermissionChecker, + PermissionManagerPermissionChecker, +) from ..auth.query_permission_checker.read_only_checker import ReadOnlyGraphQLPermissionChecker from ..auth.query_permission_checker.read_write_checker import ReadWriteGraphQLPermissionChecker +from ..auth.query_permission_checker.super_admin_checker import SuperAdminPermissionChecker def get_anonymous_access_setting() -> bool: @@ -17,8 +25,15 @@ def get_anonymous_access_setting() -> bool: def build_graphql_query_permission_checker() -> GraphQLQueryPermissionChecker: return GraphQLQueryPermissionChecker( [ - ReadWriteGraphQLPermissionChecker(), - ReadOnlyGraphQLPermissionChecker(), + # This checker never raises, it either terminates the checker chains (user is super admin) or go to the next one + SuperAdminPermissionChecker(), + DefaultBranchPermissionChecker(), + MergeBranchPermissionChecker(), + AccountManagerPermissionChecker(), + PermissionManagerPermissionChecker(), + ObjectPermissionChecker(), + ReadWriteGraphQLPermissionChecker(), # Deprecated, will be replace by either a global permission or object permissions + ReadOnlyGraphQLPermissionChecker(), # Deprecated, will be replace by either a global permission or object permissions AnonymousGraphQLPermissionChecker(get_anonymous_access_setting), DefaultGraphQLPermissionChecker(), ] diff --git a/backend/infrahub/graphql/app.py b/backend/infrahub/graphql/app.py index d838df0b8f..382d28d034 100644 --- a/backend/infrahub/graphql/app.py +++ b/backend/infrahub/graphql/app.py @@ -38,7 +38,7 @@ ) from opentelemetry import trace from starlette.datastructures import UploadFile -from starlette.requests import HTTPConnection, Request +from starlette.requests import ClientDisconnect, HTTPConnection, Request from starlette.responses import JSONResponse, Response from starlette.websockets import WebSocket, WebSocketDisconnect, WebSocketState @@ -47,8 +47,8 @@ from infrahub.core.registry import registry from infrahub.core.timestamp import Timestamp from infrahub.exceptions import BranchNotFoundError, Error -from infrahub.graphql import prepare_graphql_params from infrahub.graphql.analyzer import InfrahubGraphQLQueryAnalyzer +from infrahub.graphql.initialization import GraphqlParams, prepare_graphql_params from infrahub.log import get_logger from .metrics import ( @@ -104,7 +104,7 @@ def __init__( middleware: Optional[Middleware] = None, error_formatter: Callable[[GraphQLError], GraphQLFormattedError] = format_error, execution_context_class: Optional[type[ExecutionContext]] = None, - ): + ) -> None: self._schema = schema self.on_get = on_get self.root_value = root_value @@ -191,22 +191,37 @@ async def _handle_http_request( operations = await _get_operation_from_request(request) except ValueError as exc: return JSONResponse({"errors": [exc.args[0]]}, status_code=400) + except ClientDisconnect as exc: + self.logger.error("Exception ClientDisconnect in _handle_http_request") + return JSONResponse({"errors": [str(exc)]}, status_code=400) if isinstance(operations, list): return JSONResponse({"errors": ["This server does not support batching"]}, status_code=400) operation = operations query = operation["query"] + variable_values = operation.get("variables") + operation_name = operation.get("operationName") at = request.query_params.get("at", None) graphql_params = prepare_graphql_params( db=db, branch=branch, at=at, account_session=account_session, request=request ) - analyzed_query = InfrahubGraphQLQueryAnalyzer(query=query, schema=graphql_params.schema, branch=branch) - await self.permission_checker.check(account_session=account_session, analyzed_query=analyzed_query) - - variable_values = operation.get("variables") - operation_name = operation.get("operationName") + analyzed_query = InfrahubGraphQLQueryAnalyzer( + query=query, + query_variables=variable_values, + schema=graphql_params.schema, + operation_name=operation_name, + branch=branch, + ) + await self._evaluate_permissions( + db=db, + request=request, + query=analyzed_query, + query_parameters=graphql_params, + account_session=account_session, + branch=branch, + ) # if the query contains some mutation, it's not currently supported to set AT manually if analyzed_query.contains_mutation: @@ -221,13 +236,7 @@ async def _handle_http_request( "Processing IntrospectionQuery .. ", branch=branch.name, nbr_object_in_schema=nbr_object_in_schema ) - labels = { - "type": "mutation" if analyzed_query.contains_mutation else "query", - "branch": branch.name, - "operation": operation_name if operation_name is not None else "", - "name": analyzed_query.operations[0].name, - "query_id": "", - } + labels = self._set_labels(request=request, branch=branch, query=analyzed_query) with trace.get_tracer(__name__).start_as_current_span("execute_graphql") as span: span.set_attributes(labels) @@ -272,23 +281,43 @@ async def _handle_http_request( return json_response + def _set_labels(self, request: Request, branch: Branch, query: InfrahubGraphQLQueryAnalyzer) -> dict[str, Any]: + return { + "type": "mutation" if query.contains_mutation else "query", + "branch": branch.name, + "operation": query.operation_name if query.operation_name is not None else "", + "name": query.operations[0].name, + "query_id": "", + } + + async def _evaluate_permissions( + self, + request: Request, + db: InfrahubDatabase, + query: InfrahubGraphQLQueryAnalyzer, + query_parameters: GraphqlParams, + account_session: AccountSession, + branch: Branch, + ) -> None: + await self.permission_checker.check( + db=db, + account_session=account_session, + analyzed_query=query, + query_parameters=query_parameters, + branch=branch, + ) + def _log_error(self, error: Exception) -> None: if isinstance(error, Error): if 500 <= error.HTTP_CODE <= 500: - self.logger.error( - "An exception occurred in resolvers", - exc_info=error, - ) + self.logger.error("An exception occurred in resolvers", exc_info=error) elif error.HTTP_CODE == 401: self.logger.info("Permission denied within resolver", message=error.message) else: self.logger.debug("An exception occurred in resolvers", exc_info=error) else: - self.logger.critical( - "Unhandled exception occurred in resolvers", - exc_info=error, - ) + self.logger.critical("Unhandled exception occurred in resolvers", exc_info=error) async def _run_websocket_server(self, db: InfrahubDatabase, branch: Branch, websocket: WebSocket) -> None: subscriptions: dict[str, AsyncGenerator[Any, None]] = {} diff --git a/backend/infrahub/graphql/auth/query_permission_checker/anonymous_checker.py b/backend/infrahub/graphql/auth/query_permission_checker/anonymous_checker.py index 8b721b1704..254a5d3a90 100644 --- a/backend/infrahub/graphql/auth/query_permission_checker/anonymous_checker.py +++ b/backend/infrahub/graphql/auth/query_permission_checker/anonymous_checker.py @@ -1,20 +1,30 @@ from typing import Callable from infrahub.auth import AccountSession +from infrahub.core.branch import Branch +from infrahub.database import InfrahubDatabase from infrahub.exceptions import AuthorizationError from infrahub.graphql.analyzer import InfrahubGraphQLQueryAnalyzer +from infrahub.graphql.initialization import GraphqlParams -from .interface import GraphQLQueryPermissionCheckerInterface +from .interface import CheckerResolution, GraphQLQueryPermissionCheckerInterface class AnonymousGraphQLPermissionChecker(GraphQLQueryPermissionCheckerInterface): - def __init__(self, anonymous_access_allowed_func: Callable[[], bool]): + def __init__(self, anonymous_access_allowed_func: Callable[[], bool]) -> None: self.anonymous_access_allowed_func = anonymous_access_allowed_func - async def supports(self, account_session: AccountSession) -> bool: + async def supports(self, db: InfrahubDatabase, account_session: AccountSession, branch: Branch) -> bool: return not account_session.authenticated - async def check(self, analyzed_query: InfrahubGraphQLQueryAnalyzer) -> None: + async def check( + self, + db: InfrahubDatabase, + account_session: AccountSession, + analyzed_query: InfrahubGraphQLQueryAnalyzer, + query_parameters: GraphqlParams, + branch: Branch, + ) -> CheckerResolution: if self.anonymous_access_allowed_func() and not analyzed_query.contains_mutation: - return + return CheckerResolution.TERMINATE raise AuthorizationError("Authentication is required to perform this operation") diff --git a/backend/infrahub/graphql/auth/query_permission_checker/checker.py b/backend/infrahub/graphql/auth/query_permission_checker/checker.py index e9625a8dc0..15a26ba39d 100644 --- a/backend/infrahub/graphql/auth/query_permission_checker/checker.py +++ b/backend/infrahub/graphql/auth/query_permission_checker/checker.py @@ -1,19 +1,34 @@ -from typing import List - from infrahub.auth import AccountSession +from infrahub.core.branch import Branch +from infrahub.database import InfrahubDatabase from infrahub.exceptions import PermissionDeniedError from infrahub.graphql.analyzer import InfrahubGraphQLQueryAnalyzer +from infrahub.graphql.initialization import GraphqlParams -from .interface import GraphQLQueryPermissionCheckerInterface +from .interface import CheckerResolution, GraphQLQueryPermissionCheckerInterface class GraphQLQueryPermissionChecker: - def __init__(self, sub_checkers: List[GraphQLQueryPermissionCheckerInterface]): + def __init__(self, sub_checkers: list[GraphQLQueryPermissionCheckerInterface]) -> None: self.sub_checkers = sub_checkers - async def check(self, account_session: AccountSession, analyzed_query: InfrahubGraphQLQueryAnalyzer) -> None: + async def check( + self, + db: InfrahubDatabase, + account_session: AccountSession, + analyzed_query: InfrahubGraphQLQueryAnalyzer, + query_parameters: GraphqlParams, + branch: Branch, + ) -> None: for sub_checker in self.sub_checkers: - if await sub_checker.supports(account_session): - await sub_checker.check(analyzed_query) - return + if await sub_checker.supports(db=db, account_session=account_session, branch=branch): + resolution = await sub_checker.check( + db=db, + account_session=account_session, + analyzed_query=analyzed_query, + query_parameters=query_parameters, + branch=branch, + ) + if resolution == CheckerResolution.TERMINATE: + return raise PermissionDeniedError("The current account is not authorized to perform this operation") diff --git a/backend/infrahub/graphql/auth/query_permission_checker/default_branch_checker.py b/backend/infrahub/graphql/auth/query_permission_checker/default_branch_checker.py new file mode 100644 index 0000000000..ae04806ea4 --- /dev/null +++ b/backend/infrahub/graphql/auth/query_permission_checker/default_branch_checker.py @@ -0,0 +1,57 @@ +from infrahub.auth import AccountSession +from infrahub.core import registry +from infrahub.core.branch import Branch +from infrahub.core.constants import GLOBAL_BRANCH_NAME, GlobalPermissions, PermissionDecision +from infrahub.database import InfrahubDatabase +from infrahub.exceptions import PermissionDeniedError +from infrahub.graphql.analyzer import InfrahubGraphQLQueryAnalyzer +from infrahub.graphql.initialization import GraphqlParams + +from .interface import CheckerResolution, GraphQLQueryPermissionCheckerInterface + + +class DefaultBranchPermissionChecker(GraphQLQueryPermissionCheckerInterface): + """Checker that makes sure a user account can edit data in the default branch.""" + + permission_required = f"global:{GlobalPermissions.EDIT_DEFAULT_BRANCH.value}:{PermissionDecision.ALLOW.value}" + exempt_operations = ["BranchCreate"] + + async def supports(self, db: InfrahubDatabase, account_session: AccountSession, branch: Branch) -> bool: + return account_session.authenticated + + async def check( + self, + db: InfrahubDatabase, + account_session: AccountSession, + analyzed_query: InfrahubGraphQLQueryAnalyzer, + query_parameters: GraphqlParams, + branch: Branch, + ) -> CheckerResolution: + can_edit_default_branch = False + for permission_backend in registry.permission_backends: + can_edit_default_branch = await permission_backend.has_permission( + db=db, account_id=account_session.account_id, permission=self.permission_required, branch=branch + ) + if can_edit_default_branch: + break + + operates_on_default_branch = analyzed_query.branch is None or analyzed_query.branch.name in ( + GLOBAL_BRANCH_NAME, + registry.default_branch, + ) + is_exempt_operation = analyzed_query.operation_name is not None and ( + analyzed_query.operation_name in self.exempt_operations + or analyzed_query.operation_name.startswith("InfrahubAccount") # Allow user to manage self + ) + + if ( + not can_edit_default_branch + and operates_on_default_branch + and analyzed_query.contains_mutation + and not is_exempt_operation + ): + raise PermissionDeniedError( + f"You are not allowed to change data in the default branch '{registry.default_branch}'" + ) + + return CheckerResolution.NEXT_CHECKER diff --git a/backend/infrahub/graphql/auth/query_permission_checker/default_checker.py b/backend/infrahub/graphql/auth/query_permission_checker/default_checker.py index c4c1754fe5..3d156f9723 100644 --- a/backend/infrahub/graphql/auth/query_permission_checker/default_checker.py +++ b/backend/infrahub/graphql/auth/query_permission_checker/default_checker.py @@ -1,13 +1,23 @@ from infrahub.auth import AccountSession +from infrahub.core.branch import Branch +from infrahub.database import InfrahubDatabase from infrahub.exceptions import AuthorizationError from infrahub.graphql.analyzer import InfrahubGraphQLQueryAnalyzer +from infrahub.graphql.initialization import GraphqlParams -from .interface import GraphQLQueryPermissionCheckerInterface +from .interface import CheckerResolution, GraphQLQueryPermissionCheckerInterface class DefaultGraphQLPermissionChecker(GraphQLQueryPermissionCheckerInterface): - async def supports(self, account_session: AccountSession) -> bool: + async def supports(self, db: InfrahubDatabase, account_session: AccountSession, branch: Branch) -> bool: return True - async def check(self, analyzed_query: InfrahubGraphQLQueryAnalyzer) -> None: + async def check( + self, + db: InfrahubDatabase, + account_session: AccountSession, + analyzed_query: InfrahubGraphQLQueryAnalyzer, + query_parameters: GraphqlParams, + branch: Branch, + ) -> CheckerResolution: raise AuthorizationError("Authentication is required to perform this operation") diff --git a/backend/infrahub/graphql/auth/query_permission_checker/interface.py b/backend/infrahub/graphql/auth/query_permission_checker/interface.py index a5c62d8b41..ff0a00e2ea 100644 --- a/backend/infrahub/graphql/auth/query_permission_checker/interface.py +++ b/backend/infrahub/graphql/auth/query_permission_checker/interface.py @@ -1,12 +1,28 @@ from abc import ABC, abstractmethod +from enum import Enum from infrahub.auth import AccountSession +from infrahub.core.branch import Branch +from infrahub.database import InfrahubDatabase from infrahub.graphql.analyzer import InfrahubGraphQLQueryAnalyzer +from infrahub.graphql.initialization import GraphqlParams + + +class CheckerResolution(Enum): + TERMINATE = 0 + NEXT_CHECKER = 1 class GraphQLQueryPermissionCheckerInterface(ABC): @abstractmethod - async def supports(self, account_session: AccountSession) -> bool: ... + async def supports(self, db: InfrahubDatabase, account_session: AccountSession, branch: Branch) -> bool: ... @abstractmethod - async def check(self, analyzed_query: InfrahubGraphQLQueryAnalyzer) -> None: ... + async def check( + self, + db: InfrahubDatabase, + account_session: AccountSession, + analyzed_query: InfrahubGraphQLQueryAnalyzer, + query_parameters: GraphqlParams, + branch: Branch, + ) -> CheckerResolution: ... diff --git a/backend/infrahub/graphql/auth/query_permission_checker/merge_operation_checker.py b/backend/infrahub/graphql/auth/query_permission_checker/merge_operation_checker.py new file mode 100644 index 0000000000..dbadc65112 --- /dev/null +++ b/backend/infrahub/graphql/auth/query_permission_checker/merge_operation_checker.py @@ -0,0 +1,43 @@ +from infrahub.auth import AccountSession +from infrahub.core import registry +from infrahub.core.branch import Branch +from infrahub.core.constants import GlobalPermissions +from infrahub.database import InfrahubDatabase +from infrahub.exceptions import PermissionDeniedError +from infrahub.graphql.analyzer import InfrahubGraphQLQueryAnalyzer +from infrahub.graphql.initialization import GraphqlParams + +from .interface import CheckerResolution, GraphQLQueryPermissionCheckerInterface + + +class MergeBranchPermissionChecker(GraphQLQueryPermissionCheckerInterface): + """Checker that makes sure a user account can merge a branch without going through a proposed change.""" + + permission_required = f"global:{GlobalPermissions.MERGE_BRANCH.value}:allow" + + async def supports(self, db: InfrahubDatabase, account_session: AccountSession, branch: Branch) -> bool: + return account_session.authenticated + + async def check( + self, + db: InfrahubDatabase, + account_session: AccountSession, + analyzed_query: InfrahubGraphQLQueryAnalyzer, + query_parameters: GraphqlParams, + branch: Branch, + ) -> CheckerResolution: + if "BranchMerge" in [operation.name for operation in analyzed_query.operations]: + can_merge_branch = False + for permission_backend in registry.permission_backends: + can_merge_branch = await permission_backend.has_permission( + db=db, account_id=account_session.account_id, permission=self.permission_required, branch=branch + ) + if can_merge_branch: + break + + if not can_merge_branch: + raise PermissionDeniedError("You are not allowed to merge a branch") + + return CheckerResolution.TERMINATE + + return CheckerResolution.NEXT_CHECKER diff --git a/backend/infrahub/graphql/auth/query_permission_checker/object_permission_checker.py b/backend/infrahub/graphql/auth/query_permission_checker/object_permission_checker.py new file mode 100644 index 0000000000..2f31be77b4 --- /dev/null +++ b/backend/infrahub/graphql/auth/query_permission_checker/object_permission_checker.py @@ -0,0 +1,212 @@ +from infrahub.auth import AccountSession +from infrahub.core import registry +from infrahub.core.account import ObjectPermission +from infrahub.core.branch import Branch +from infrahub.core.constants import GlobalPermissions, InfrahubKind, PermissionDecision +from infrahub.core.manager import get_schema +from infrahub.core.schema.node_schema import NodeSchema +from infrahub.database import InfrahubDatabase +from infrahub.exceptions import PermissionDeniedError +from infrahub.graphql.analyzer import InfrahubGraphQLQueryAnalyzer +from infrahub.graphql.initialization import GraphqlParams +from infrahub.utils import extract_camelcase_words + +from .interface import CheckerResolution, GraphQLQueryPermissionCheckerInterface + + +class ObjectPermissionChecker(GraphQLQueryPermissionCheckerInterface): + """Checker that makes sure a user account can perform some action on some kind of objects.""" + + async def supports(self, db: InfrahubDatabase, account_session: AccountSession, branch: Branch) -> bool: + return account_session.authenticated + + async def check( + self, + db: InfrahubDatabase, + account_session: AccountSession, + analyzed_query: InfrahubGraphQLQueryAnalyzer, + query_parameters: GraphqlParams, + branch: Branch, + ) -> CheckerResolution: + kinds = await analyzed_query.get_models_in_use(types=query_parameters.context.types) + + # Identify which operations are performed. As we don't have a mapping between kinds and the + # operation we currently require permissions all defined permissions for all objects + # within the GraphQL query / mutation + actions: set[str] = set() + for operation in analyzed_query.operations: + for kind in kinds: + if operation.name and operation.name.startswith(kind): + # An empty string after prefix removal means a query to "view" + query_action = operation.name[len(kind) :].lower() or "view" + if query_action == "upsert": + # Require both create and update for Upsert mutations + actions.add("create") + actions.add("update") + else: + actions.add(query_action) + + # Infer required permissions from the kind/operation map + permissions: list[str] = [] + for action in actions: + for kind in kinds: + extracted_words = extract_camelcase_words(kind) + permissions.append( + str( + # Create a object permission instance just to get its string representation + ObjectPermission( + id="", + branch=branch.name, + namespace=extracted_words[0], + name="".join(extracted_words[1:]), + action=action.lower(), + decision=PermissionDecision.ALLOW.value, + ) + ) + ) + + for permission in permissions: + has_permission = False + for permission_backend in registry.permission_backends: + has_permission = await permission_backend.has_permission( + db=db, account_id=account_session.account_id, permission=permission, branch=branch + ) + if not has_permission: + raise PermissionDeniedError(f"You do not have the following permission: {permission}") + + return CheckerResolution.NEXT_CHECKER + + +class AccountManagerPermissionChecker(GraphQLQueryPermissionCheckerInterface): + """Checker that makes sure a user account can perform actions on account related objects. + + This is similar to object permission checker except that we care for any operations on any account related kinds. + """ + + permission_required = f"global:{GlobalPermissions.MANAGE_ACCOUNTS.value}:{PermissionDecision.ALLOW.value}" + + async def supports(self, db: InfrahubDatabase, account_session: AccountSession, branch: Branch) -> bool: + return account_session.authenticated + + async def check( + self, + db: InfrahubDatabase, + account_session: AccountSession, + analyzed_query: InfrahubGraphQLQueryAnalyzer, + query_parameters: GraphqlParams, + branch: Branch, + ) -> CheckerResolution: + is_account_operation = False + kinds = await analyzed_query.get_models_in_use(types=query_parameters.context.types) + operation_names = [operation.name for operation in analyzed_query.operations] + + for kind in kinds: + schema = get_schema(db=db, branch=branch, node_schema=kind) + if is_account_operation := kind in ( + InfrahubKind.GENERICACCOUNT, + InfrahubKind.ACCOUNTGROUP, + InfrahubKind.ACCOUNTROLE, + ) or (isinstance(schema, NodeSchema) and InfrahubKind.GENERICACCOUNT in schema.inherit_from): + break + + # Ignore non-account related operation or viewing account own profile + if not is_account_operation or operation_names == ["AccountProfile"]: + return CheckerResolution.NEXT_CHECKER + + has_permission = False + for permission_backend in registry.permission_backends: + if has_permission := await permission_backend.has_permission( + db=db, account_id=account_session.account_id, permission=self.permission_required, branch=branch + ): + break + + if not has_permission and analyzed_query.contains_mutation: + raise PermissionDeniedError("You do not have the permission to manage user accounts, groups or roles") + + return CheckerResolution.NEXT_CHECKER + + +class PermissionManagerPermissionChecker(GraphQLQueryPermissionCheckerInterface): + """Checker that makes sure a user account can perform actions on permission related object. + + This is similar to object permission checker except that we care for any operations on any permission related kinds. + """ + + permission_required = f"global:{GlobalPermissions.MANAGE_PERMISSIONS.value}:{PermissionDecision.ALLOW.value}" + + async def supports(self, db: InfrahubDatabase, account_session: AccountSession, branch: Branch) -> bool: + return account_session.authenticated + + async def check( + self, + db: InfrahubDatabase, + account_session: AccountSession, + analyzed_query: InfrahubGraphQLQueryAnalyzer, + query_parameters: GraphqlParams, + branch: Branch, + ) -> CheckerResolution: + is_permission_operation = False + kinds = await analyzed_query.get_models_in_use(types=query_parameters.context.types) + + for kind in kinds: + schema = get_schema(db=db, branch=branch, node_schema=kind) + if is_permission_operation := kind in ( + InfrahubKind.BASEPERMISSION, + InfrahubKind.GLOBALPERMISSION, + InfrahubKind.OBJECTPERMISSION, + ) or (isinstance(schema, NodeSchema) and InfrahubKind.BASEPERMISSION in schema.inherit_from): + break + + if not is_permission_operation: + return CheckerResolution.NEXT_CHECKER + + for permission_backend in registry.permission_backends: + if not await permission_backend.has_permission( + db=db, account_id=account_session.account_id, permission=self.permission_required, branch=branch + ): + raise PermissionDeniedError("You do not have the permission to manage permissions") + + return CheckerResolution.NEXT_CHECKER + + +class RepositoryManagerPermissionChecker(GraphQLQueryPermissionCheckerInterface): + """Checker that makes sure a user account can add/edit/delete repository objects. + + This is similar to object permission checker except that we only care about mutations on repositories. + """ + + permission_required = f"global:{GlobalPermissions.MANAGE_REPOSITORIES.value}:{PermissionDecision.ALLOW.value}" + + async def supports(self, db: InfrahubDatabase, account_session: AccountSession, branch: Branch) -> bool: + return account_session.authenticated + + async def check( + self, + db: InfrahubDatabase, + account_session: AccountSession, + analyzed_query: InfrahubGraphQLQueryAnalyzer, + query_parameters: GraphqlParams, + branch: Branch, + ) -> CheckerResolution: + is_repository_operation = False + kinds = await analyzed_query.get_models_in_use(types=query_parameters.context.types) + + for kind in kinds: + schema = get_schema(db=db, branch=branch, node_schema=kind) + if is_repository_operation := kind in ( + InfrahubKind.GENERICREPOSITORY, + InfrahubKind.REPOSITORY, + InfrahubKind.READONLYREPOSITORY, + ) or (isinstance(schema, NodeSchema) and InfrahubKind.GENERICREPOSITORY in schema.inherit_from): + break + + if not is_repository_operation or not analyzed_query.contains_mutation: + return CheckerResolution.NEXT_CHECKER + + for permission_backend in registry.permission_backends: + if not await permission_backend.has_permission( + db=db, account_id=account_session.account_id, permission=self.permission_required, branch=branch + ): + raise PermissionDeniedError("You do not have the permission to manage repositories") + + return CheckerResolution.NEXT_CHECKER diff --git a/backend/infrahub/graphql/auth/query_permission_checker/read_only_checker.py b/backend/infrahub/graphql/auth/query_permission_checker/read_only_checker.py index 26c606921e..da16978cf1 100644 --- a/backend/infrahub/graphql/auth/query_permission_checker/read_only_checker.py +++ b/backend/infrahub/graphql/auth/query_permission_checker/read_only_checker.py @@ -1,22 +1,34 @@ from graphql import OperationType from infrahub.auth import AccountSession +from infrahub.core.branch import Branch +from infrahub.database import InfrahubDatabase from infrahub.exceptions import PermissionDeniedError from infrahub.graphql.analyzer import InfrahubGraphQLQueryAnalyzer +from infrahub.graphql.initialization import GraphqlParams -from .interface import GraphQLQueryPermissionCheckerInterface +from .interface import CheckerResolution, GraphQLQueryPermissionCheckerInterface class ReadOnlyGraphQLPermissionChecker(GraphQLQueryPermissionCheckerInterface): allowed_readonly_mutations = ["InfrahubAccountSelfUpdate"] - async def supports(self, account_session: AccountSession) -> bool: + async def supports(self, db: InfrahubDatabase, account_session: AccountSession, branch: Branch) -> bool: return account_session.authenticated and account_session.read_only - async def check(self, analyzed_query: InfrahubGraphQLQueryAnalyzer) -> None: + async def check( + self, + db: InfrahubDatabase, + account_session: AccountSession, + analyzed_query: InfrahubGraphQLQueryAnalyzer, + query_parameters: GraphqlParams, + branch: Branch, + ) -> CheckerResolution: for operation in analyzed_query.operations: if ( operation.operation_type == OperationType.MUTATION and operation.name not in self.allowed_readonly_mutations ): raise PermissionDeniedError("The current account is not authorized to perform this operation") + + return CheckerResolution.TERMINATE diff --git a/backend/infrahub/graphql/auth/query_permission_checker/read_write_checker.py b/backend/infrahub/graphql/auth/query_permission_checker/read_write_checker.py index 1c3d72e27d..e236532ea3 100644 --- a/backend/infrahub/graphql/auth/query_permission_checker/read_write_checker.py +++ b/backend/infrahub/graphql/auth/query_permission_checker/read_write_checker.py @@ -1,12 +1,22 @@ from infrahub.auth import AccountSession +from infrahub.core.branch import Branch +from infrahub.database import InfrahubDatabase from infrahub.graphql.analyzer import InfrahubGraphQLQueryAnalyzer +from infrahub.graphql.initialization import GraphqlParams -from .interface import GraphQLQueryPermissionCheckerInterface +from .interface import CheckerResolution, GraphQLQueryPermissionCheckerInterface class ReadWriteGraphQLPermissionChecker(GraphQLQueryPermissionCheckerInterface): - async def supports(self, account_session: AccountSession) -> bool: + async def supports(self, db: InfrahubDatabase, account_session: AccountSession, branch: Branch) -> bool: return account_session.authenticated and not account_session.read_only - async def check(self, analyzed_query: InfrahubGraphQLQueryAnalyzer) -> None: - return + async def check( + self, + db: InfrahubDatabase, + account_session: AccountSession, + analyzed_query: InfrahubGraphQLQueryAnalyzer, + query_parameters: GraphqlParams, + branch: Branch, + ) -> CheckerResolution: + return CheckerResolution.TERMINATE diff --git a/backend/infrahub/graphql/auth/query_permission_checker/super_admin_checker.py b/backend/infrahub/graphql/auth/query_permission_checker/super_admin_checker.py new file mode 100644 index 0000000000..a0e5982b26 --- /dev/null +++ b/backend/infrahub/graphql/auth/query_permission_checker/super_admin_checker.py @@ -0,0 +1,34 @@ +from infrahub.auth import AccountSession +from infrahub.core import registry +from infrahub.core.branch import Branch +from infrahub.core.constants import GlobalPermissions +from infrahub.database import InfrahubDatabase +from infrahub.graphql.analyzer import InfrahubGraphQLQueryAnalyzer +from infrahub.graphql.initialization import GraphqlParams + +from .interface import CheckerResolution, GraphQLQueryPermissionCheckerInterface + + +class SuperAdminPermissionChecker(GraphQLQueryPermissionCheckerInterface): + """Checker allows a user to do anything (if the checker runs first).""" + + permission_required = f"global:{GlobalPermissions.SUPER_ADMIN.value}:allow" + + async def supports(self, db: InfrahubDatabase, account_session: AccountSession, branch: Branch) -> bool: + return account_session.authenticated + + async def check( + self, + db: InfrahubDatabase, + account_session: AccountSession, + analyzed_query: InfrahubGraphQLQueryAnalyzer, + query_parameters: GraphqlParams, + branch: Branch, + ) -> CheckerResolution: + for permission_backend in registry.permission_backends: + if await permission_backend.has_permission( + db=db, account_id=account_session.account_id, permission=self.permission_required, branch=branch + ): + return CheckerResolution.TERMINATE + + return CheckerResolution.NEXT_CHECKER diff --git a/backend/infrahub/graphql/initialization.py b/backend/infrahub/graphql/initialization.py new file mode 100644 index 0000000000..757957e478 --- /dev/null +++ b/backend/infrahub/graphql/initialization.py @@ -0,0 +1,110 @@ +from __future__ import annotations + +from dataclasses import dataclass +from typing import TYPE_CHECKING, Optional, Union + +from starlette.background import BackgroundTasks + +from infrahub.core import registry +from infrahub.core.timestamp import Timestamp +from infrahub.exceptions import InitializationError + +from .manager import GraphQLSchemaManager + +if TYPE_CHECKING: + from graphql import GraphQLSchema + from starlette.requests import HTTPConnection + + from infrahub.auth import AccountSession + from infrahub.core.branch import Branch + from infrahub.database import InfrahubDatabase + from infrahub.services import InfrahubServices + + +@dataclass +class GraphqlParams: + schema: GraphQLSchema + context: GraphqlContext + + +@dataclass +class GraphqlContext: + db: InfrahubDatabase + branch: Branch + types: dict + at: Optional[Timestamp] = None + related_node_ids: Optional[set] = None + service: Optional[InfrahubServices] = None + account_session: Optional[AccountSession] = None + background: Optional[BackgroundTasks] = None + request: Optional[HTTPConnection] = None + + @property + def active_account_session(self) -> AccountSession: + """Return an account session or raise an error + + Eventualy this property should be removed, that can be done after self.account_session is no longer optional + """ + if self.account_session: + return self.account_session + raise InitializationError("GraphQLContext doesn't contain an account_session") + + +def prepare_graphql_params( + db: InfrahubDatabase, + branch: Union[Branch, str], + at: Optional[Union[Timestamp, str]] = None, + account_session: Optional[AccountSession] = None, + request: Optional[HTTPConnection] = None, + service: Optional[InfrahubServices] = None, + include_query: bool = True, + include_mutation: bool = True, + include_subscription: bool = True, + include_types: bool = True, +) -> GraphqlParams: + branch = registry.get_branch_from_registry(branch=branch) + schema = registry.schema.get_schema_branch(name=branch.name) + + gqlm = schema.get_graphql_manager() + gql_schema = schema.get_graphql_schema( + include_query=include_query, + include_mutation=include_mutation, + include_subscription=include_subscription, + include_types=include_types, + ) + + if request and not service: + service = request.app.state.service + + return GraphqlParams( + schema=gql_schema, + context=GraphqlContext( + db=db, + branch=branch, + at=Timestamp(at), + types=gqlm._graphql_types, + related_node_ids=set(), + background=BackgroundTasks(), + request=request, + service=service, + account_session=account_session, + ), + ) + + +def generate_graphql_schema( + db: InfrahubDatabase, # pylint: disable=unused-argument + branch: Union[Branch, str], + include_query: bool = True, + include_mutation: bool = True, + include_subscription: bool = True, + include_types: bool = True, +) -> GraphQLSchema: + branch = registry.get_branch_from_registry(branch) + schema = registry.schema.get_schema_branch(name=branch.name) + return GraphQLSchemaManager(schema=schema).generate( + include_query=include_query, + include_mutation=include_mutation, + include_subscription=include_subscription, + include_types=include_types, + ) diff --git a/backend/infrahub/graphql/manager.py b/backend/infrahub/graphql/manager.py index 246105db18..805575aa1c 100644 --- a/backend/infrahub/graphql/manager.py +++ b/backend/infrahub/graphql/manager.py @@ -23,15 +23,18 @@ from .directives import DIRECTIVES from .enums import generate_graphql_enum, get_enum_attribute_type_name from .metrics import SCHEMA_GENERATE_GRAPHQL_METRICS -from .mutations import ( - InfrahubArtifactDefinitionMutation, +from .mutations.artifact_definition import InfrahubArtifactDefinitionMutation +from .mutations.ipam import ( InfrahubIPAddressMutation, InfrahubIPNamespaceMutation, InfrahubIPPrefixMutation, - InfrahubMutation, +) +from .mutations.main import InfrahubMutation +from .mutations.menu import InfrahubCoreMenuMutation +from .mutations.proposed_change import InfrahubProposedChangeMutation +from .mutations.repository import InfrahubRepositoryMutation +from .mutations.resource_manager import ( InfrahubNumberPoolMutation, - InfrahubProposedChangeMutation, - InfrahubRepositoryMutation, ) from .queries.diff.diff import ( DiffSummaryElementAttribute, @@ -39,17 +42,20 @@ DiffSummaryElementRelationshipOne, ) from .resolver import ( + account_resolver, ancestors_resolver, + default_paginated_list_resolver, default_resolver, descendants_resolver, many_relationship_resolver, single_relationship_resolver, ) -from .schema import InfrahubBaseMutation, InfrahubBaseQuery, account_resolver, default_paginated_list_resolver +from .schema import InfrahubBaseMutation, InfrahubBaseQuery from .subscription import InfrahubBaseSubscription from .types import ( InfrahubInterface, InfrahubObject, + PaginatedObjectPermission, RelatedIPAddressNodeInput, RelatedNodeInput, RelatedPrefixNodeInput, @@ -60,7 +66,7 @@ if TYPE_CHECKING: from graphql import GraphQLSchema - from infrahub.core.schema_manager import SchemaBranch + from infrahub.core.schema.schema_branch import SchemaBranch class DeleteInput(graphene.InputObjectType): @@ -94,7 +100,7 @@ class GraphQLSchemaManager: # pylint: disable=too-many-public-methods "DiffSummaryElementRelationshipMany": DiffSummaryElementRelationshipMany, } - def __init__(self, schema: SchemaBranch): + def __init__(self, schema: SchemaBranch) -> None: self.schema = schema self._graphql_types: dict[str, GraphQLTypes] = {} @@ -410,6 +416,7 @@ def generate_mutation_mixin(self) -> type[object]: InfrahubKind.GRAPHQLQUERY: InfrahubGraphQLQueryMutation, InfrahubKind.NAMESPACE: InfrahubIPNamespaceMutation, InfrahubKind.NUMBERPOOL: InfrahubNumberPoolMutation, + InfrahubKind.MENUITEM: InfrahubCoreMenuMutation, } if isinstance(node_schema, NodeSchema) and node_schema.is_ip_prefix(): @@ -878,6 +885,9 @@ def generate_graphql_paginated_object( "Meta": type("Meta", (object,), meta_attrs), } + if isinstance(schema, (NodeSchema, GenericSchema)): + main_attrs["permissions"] = graphene.Field(PaginatedObjectPermission, required=True) + graphql_paginated_object = type(object_name, (InfrahubObject,), main_attrs) if populate_cache: diff --git a/backend/infrahub/graphql/mutations/__init__.py b/backend/infrahub/graphql/mutations/__init__.py index 4ab2dcc100..e69de29bb2 100644 --- a/backend/infrahub/graphql/mutations/__init__.py +++ b/backend/infrahub/graphql/mutations/__init__.py @@ -1,98 +0,0 @@ -from .account import InfrahubAccountSelfUpdate, InfrahubAccountTokenCreate, InfrahubAccountTokenDelete -from .artifact_definition import InfrahubArtifactDefinitionMutation -from .attribute import ( - AnyAttributeCreate, - AnyAttributeUpdate, - BoolAttributeCreate, - BoolAttributeUpdate, - CheckboxAttributeCreate, - CheckboxAttributeUpdate, - JSONAttributeCreate, - JSONAttributeUpdate, - ListAttributeCreate, - ListAttributeUpdate, - NumberAttributeCreate, - NumberAttributeUpdate, - StringAttributeCreate, - StringAttributeUpdate, - TextAttributeCreate, - TextAttributeUpdate, -) -from .branch import ( - BranchCreate, - BranchCreateInput, - BranchDelete, - BranchMerge, - BranchNameInput, - BranchRebase, - BranchUpdate, - BranchValidate, -) -from .diff import DiffUpdateMutation -from .diff_conflict import ResolveDiffConflict -from .ipam import InfrahubIPAddressMutation, InfrahubIPNamespaceMutation, InfrahubIPPrefixMutation -from .main import InfrahubMutation, InfrahubMutationMixin, InfrahubMutationOptions -from .proposed_change import ( - InfrahubProposedChangeMutation, - ProposedChangeRequestRunCheck, -) -from .relationship import RelationshipAdd, RelationshipRemove -from .repository import InfrahubRepositoryMutation, ProcessRepository, ValidateRepositoryConnectivity -from .resource_manager import InfrahubNumberPoolMutation, IPAddressPoolGetResource, IPPrefixPoolGetResource -from .schema import SchemaDropdownAdd, SchemaDropdownRemove, SchemaEnumAdd, SchemaEnumRemove -from .task import TaskCreate, TaskUpdate - -__all__ = [ - "AnyAttributeCreate", - "AnyAttributeUpdate", - "BoolAttributeCreate", - "BoolAttributeUpdate", - "BranchCreate", - "BranchCreateInput", - "BranchRebase", - "BranchValidate", - "BranchDelete", - "BranchMerge", - "BranchNameInput", - "BranchUpdate", - "CheckboxAttributeCreate", - "CheckboxAttributeUpdate", - "DiffUpdateMutation", - "InfrahubAccountSelfUpdate", - "InfrahubAccountTokenCreate", - "InfrahubAccountTokenDelete", - "IPPrefixPoolGetResource", - "IPAddressPoolGetResource", - "InfrahubArtifactDefinitionMutation", - "InfrahubNumberPoolMutation", - "InfrahubIPAddressMutation", - "InfrahubIPNamespaceMutation", - "InfrahubIPPrefixMutation", - "InfrahubRepositoryMutation", - "InfrahubMutationOptions", - "InfrahubMutation", - "InfrahubMutationMixin", - "InfrahubProposedChangeMutation", - "JSONAttributeCreate", - "JSONAttributeUpdate", - "ListAttributeCreate", - "ListAttributeUpdate", - "NumberAttributeCreate", - "NumberAttributeUpdate", - "ProposedChangeRequestRunCheck", - "ProcessRepository", - "RelationshipAdd", - "RelationshipRemove", - "ResolveDiffConflict", - "StringAttributeCreate", - "StringAttributeUpdate", - "TextAttributeCreate", - "TextAttributeUpdate", - "SchemaDropdownAdd", - "SchemaDropdownRemove", - "SchemaEnumAdd", - "SchemaEnumRemove", - "TaskCreate", - "TaskUpdate", - "ValidateRepositoryConnectivity", -] diff --git a/backend/infrahub/graphql/mutations/account.py b/backend/infrahub/graphql/mutations/account.py index c1d15c28d2..1ecb40b303 100644 --- a/backend/infrahub/graphql/mutations/account.py +++ b/backend/infrahub/graphql/mutations/account.py @@ -18,7 +18,7 @@ from ..types import InfrahubObjectType if TYPE_CHECKING: - from .. import GraphqlContext + from ..initialization import GraphqlContext # pylint: disable=unused-argument diff --git a/backend/infrahub/graphql/mutations/artifact_definition.py b/backend/infrahub/graphql/mutations/artifact_definition.py index 7a9268fc96..caef54be9c 100644 --- a/backend/infrahub/graphql/mutations/artifact_definition.py +++ b/backend/infrahub/graphql/mutations/artifact_definition.py @@ -17,7 +17,7 @@ from infrahub.core.branch import Branch from infrahub.core.node import Node from infrahub.database import InfrahubDatabase - from infrahub.graphql import GraphqlContext + from infrahub.graphql.initialization import GraphqlContext log = get_logger() @@ -44,7 +44,6 @@ def __init_subclass_with_meta__( # pylint: disable=arguments-differ @classmethod async def mutate_create( cls, - root: dict, info: GraphQLResolveInfo, data: InputObjectType, branch: Branch, @@ -53,7 +52,7 @@ async def mutate_create( ) -> tuple[Node, Self]: context: GraphqlContext = info.context - artifact_definition, result = await super().mutate_create(root=root, info=info, data=data, branch=branch, at=at) + artifact_definition, result = await super().mutate_create(info=info, data=data, branch=branch, at=at) events = [ messages.RequestArtifactDefinitionGenerate(artifact_definition=artifact_definition.id, branch=branch.name), @@ -68,7 +67,6 @@ async def mutate_create( @classmethod async def mutate_update( cls, - root: dict, info: GraphQLResolveInfo, data: InputObjectType, branch: Branch, @@ -78,7 +76,7 @@ async def mutate_update( ) -> tuple[Node, Self]: context: GraphqlContext = info.context - artifact_definition, result = await super().mutate_update(root=root, info=info, data=data, branch=branch, at=at) + artifact_definition, result = await super().mutate_update(info=info, data=data, branch=branch, at=at) events = [ messages.RequestArtifactDefinitionGenerate(artifact_definition=artifact_definition.id, branch=branch.name), diff --git a/backend/infrahub/graphql/mutations/attribute.py b/backend/infrahub/graphql/mutations/attribute.py index 244ad10ab8..568b1edf6b 100644 --- a/backend/infrahub/graphql/mutations/attribute.py +++ b/backend/infrahub/graphql/mutations/attribute.py @@ -12,7 +12,7 @@ class BaseAttributeCreate(InputObjectType): owner = String(required=False) @classmethod - def __init_subclass__(cls, **kwargs): + def __init_subclass__(cls, **kwargs) -> None: super().__init_subclass__(**kwargs) registry.input_type[cls.__name__] = cls @@ -25,7 +25,7 @@ class BaseAttributeUpdate(InputObjectType): owner = String(required=False) @classmethod - def __init_subclass__(cls, **kwargs): + def __init_subclass__(cls, **kwargs) -> None: super().__init_subclass__(**kwargs) registry.input_type[cls.__name__] = cls diff --git a/backend/infrahub/graphql/mutations/branch.py b/backend/infrahub/graphql/mutations/branch.py index 2c484ff625..a86c03e311 100644 --- a/backend/infrahub/graphql/mutations/branch.py +++ b/backend/infrahub/graphql/mutations/branch.py @@ -29,7 +29,7 @@ if TYPE_CHECKING: from graphql import GraphQLResolveInfo - from .. import GraphqlContext + from ..initialization import GraphqlContext # pylint: disable=unused-argument diff --git a/backend/infrahub/graphql/mutations/diff.py b/backend/infrahub/graphql/mutations/diff.py index 5c2e32f137..8941b999ff 100644 --- a/backend/infrahub/graphql/mutations/diff.py +++ b/backend/infrahub/graphql/mutations/diff.py @@ -9,7 +9,7 @@ from infrahub.message_bus import messages if TYPE_CHECKING: - from .. import GraphqlContext + from ..initialization import GraphqlContext class DiffUpdateInput(InputObjectType): diff --git a/backend/infrahub/graphql/mutations/diff_conflict.py b/backend/infrahub/graphql/mutations/diff_conflict.py index 8877934f24..b5ae8aa11f 100644 --- a/backend/infrahub/graphql/mutations/diff_conflict.py +++ b/backend/infrahub/graphql/mutations/diff_conflict.py @@ -15,7 +15,7 @@ if TYPE_CHECKING: from graphql import GraphQLResolveInfo - from .. import GraphqlContext + from ..initialization import GraphqlContext # pylint: disable=unused-argument diff --git a/backend/infrahub/graphql/mutations/graphql_query.py b/backend/infrahub/graphql/mutations/graphql_query.py index e08a8862c5..fbe8c81f4f 100644 --- a/backend/infrahub/graphql/mutations/graphql_query.py +++ b/backend/infrahub/graphql/mutations/graphql_query.py @@ -14,7 +14,7 @@ from .main import InfrahubMutationOptions if TYPE_CHECKING: - from infrahub.graphql import GraphqlContext + from infrahub.graphql.initialization import GraphqlContext class InfrahubGraphQLQueryMutation(InfrahubMutationMixin, Mutation): @@ -60,7 +60,6 @@ async def extract_query_info( @classmethod async def mutate_create( cls, - root: dict, info: GraphQLResolveInfo, data: InputObjectType, branch: Branch, @@ -71,14 +70,13 @@ async def mutate_create( data.update(await cls.extract_query_info(info=info, data=data, branch=context.branch)) - obj, result = await super().mutate_create(root=root, info=info, data=data, branch=branch, at=at) + obj, result = await super().mutate_create(info=info, data=data, branch=branch, at=at) return obj, result @classmethod async def mutate_update( cls, - root: dict, info: GraphQLResolveInfo, data: InputObjectType, branch: Branch, @@ -90,6 +88,6 @@ async def mutate_update( data.update(await cls.extract_query_info(info=info, data=data, branch=context.branch)) - obj, result = await super().mutate_update(root=root, info=info, data=data, branch=branch, at=at) + obj, result = await super().mutate_update(info=info, data=data, branch=branch, at=at) return obj, result diff --git a/backend/infrahub/graphql/mutations/ipam.py b/backend/infrahub/graphql/mutations/ipam.py index d147b0412c..17e3b36139 100644 --- a/backend/infrahub/graphql/mutations/ipam.py +++ b/backend/infrahub/graphql/mutations/ipam.py @@ -20,7 +20,7 @@ from .main import InfrahubMutationMixin, InfrahubMutationOptions if TYPE_CHECKING: - from infrahub.graphql import GraphqlContext + from infrahub.graphql.initialization import GraphqlContext log = get_logger() @@ -64,7 +64,6 @@ def __init_subclass_with_meta__( # pylint: disable=arguments-differ @classmethod async def mutate_delete( cls, - root, info: GraphQLResolveInfo, data: InputObjectType, branch: Branch, @@ -73,7 +72,7 @@ async def mutate_delete( if data["id"] == registry.default_ipnamespace: raise ValueError("Cannot delete default IPAM namespace") - return await super().mutate_delete(root=root, info=info, data=data, branch=branch, at=at) + return await super().mutate_delete(info=info, data=data, branch=branch, at=at) class InfrahubIPAddressMutation(InfrahubMutationMixin, Mutation): @@ -98,7 +97,6 @@ def __init_subclass_with_meta__( # pylint: disable=arguments-differ @retry_db_transaction(name="ipaddress_create") async def mutate_create( cls, - root: dict, info: GraphQLResolveInfo, data: InputObjectType, branch: Branch, @@ -125,7 +123,6 @@ async def mutate_create( @retry_db_transaction(name="ipaddress_update") async def mutate_update( cls, - root: dict, info: GraphQLResolveInfo, data: InputObjectType, branch: Branch, @@ -165,7 +162,6 @@ async def mutate_update( @classmethod async def mutate_upsert( cls, - root: dict, info: GraphQLResolveInfo, data: InputObjectType, branch: Branch, @@ -178,7 +174,7 @@ async def mutate_upsert( await validate_namespace(db=db, data=data) prefix, result, created = await super().mutate_upsert( - root=root, info=info, data=data, branch=branch, at=at, node_getters=node_getters, database=db + info=info, data=data, branch=branch, at=at, node_getters=node_getters, database=db ) return prefix, result, created @@ -186,13 +182,12 @@ async def mutate_upsert( @classmethod async def mutate_delete( cls, - root, info: GraphQLResolveInfo, data: InputObjectType, branch: Branch, at: str, ): - return await super().mutate_delete(root=root, info=info, data=data, branch=branch, at=at) + return await super().mutate_delete(info=info, data=data, branch=branch, at=at) class InfrahubIPPrefixMutation(InfrahubMutationMixin, Mutation): @@ -217,7 +212,6 @@ def __init_subclass_with_meta__( # pylint: disable=arguments-differ @retry_db_transaction(name="ipprefix_create") async def mutate_create( cls, - root: dict, info: GraphQLResolveInfo, data: InputObjectType, branch: Branch, @@ -244,7 +238,6 @@ async def mutate_create( @retry_db_transaction(name="ipprefix_update") async def mutate_update( cls, - root: dict, info: GraphQLResolveInfo, data: InputObjectType, branch: Branch, @@ -284,7 +277,6 @@ async def mutate_update( @classmethod async def mutate_upsert( cls, - root: dict, info: GraphQLResolveInfo, data: InputObjectType, branch: Branch, @@ -297,7 +289,7 @@ async def mutate_upsert( await validate_namespace(db=db, data=data) prefix, result, created = await super().mutate_upsert( - root=root, info=info, data=data, branch=branch, at=at, node_getters=node_getters, database=db + info=info, data=data, branch=branch, at=at, node_getters=node_getters, database=db ) return prefix, result, created @@ -306,7 +298,6 @@ async def mutate_upsert( @retry_db_transaction(name="ipprefix_delete") async def mutate_delete( cls, - root, info: GraphQLResolveInfo, data: InputObjectType, branch: Branch, diff --git a/backend/infrahub/graphql/mutations/main.py b/backend/infrahub/graphql/mutations/main.py index 7e65113fc7..2f8a3de052 100644 --- a/backend/infrahub/graphql/mutations/main.py +++ b/backend/infrahub/graphql/mutations/main.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Optional, Union +from typing import TYPE_CHECKING, Any, Optional, Union from graphene import InputObjectType, Mutation from graphene.types.mutation import MutationOptions @@ -23,10 +23,9 @@ from infrahub.core.timestamp import Timestamp from infrahub.database import retry_db_transaction from infrahub.dependencies.registry import get_component_registry +from infrahub.events import EventMeta, NodeMutatedEvent from infrahub.exceptions import ValidationError from infrahub.log import get_log_data, get_logger -from infrahub.message_bus import Meta, messages -from infrahub.services import services from infrahub.worker import WORKER_IDENTITY from .node_getter.by_default_filter import MutationNodeGetterByDefaultFilter @@ -39,7 +38,7 @@ from infrahub.core.branch import Branch from infrahub.database import InfrahubDatabase - from .. import GraphqlContext + from ..initialization import GraphqlContext from .node_getter.interface import MutationNodeGetterInterface # pylint: disable=unused-argument @@ -56,7 +55,7 @@ class InfrahubMutationOptions(MutationOptions): class InfrahubMutationMixin: @classmethod - async def mutate(cls, root: dict, info: GraphQLResolveInfo, *args, **kwargs): + async def mutate(cls, root: dict, info: GraphQLResolveInfo, *args: Any, **kwargs): context: GraphqlContext = info.context obj = None @@ -65,14 +64,10 @@ async def mutate(cls, root: dict, info: GraphQLResolveInfo, *args, **kwargs): validate_mutation_permissions(operation=cls.__name__, account_session=context.account_session) if "Create" in cls.__name__: - obj, mutation = await cls.mutate_create( - root=root, info=info, branch=context.branch, at=context.at, **kwargs - ) + obj, mutation = await cls.mutate_create(info=info, branch=context.branch, at=context.at, **kwargs) action = MutationAction.ADDED elif "Update" in cls.__name__: - obj, mutation = await cls.mutate_update( - root=root, info=info, branch=context.branch, at=context.at, **kwargs - ) + obj, mutation = await cls.mutate_update(info=info, branch=context.branch, at=context.at, **kwargs) action = MutationAction.UPDATED elif "Upsert" in cls.__name__: node_manager = NodeManager() @@ -82,16 +77,14 @@ async def mutate(cls, root: dict, info: GraphQLResolveInfo, *args, **kwargs): MutationNodeGetterByDefaultFilter(db=context.db, node_manager=node_manager), ] obj, mutation, created = await cls.mutate_upsert( - root=root, info=info, branch=context.branch, at=context.at, node_getters=node_getters, **kwargs + info=info, branch=context.branch, at=context.at, node_getters=node_getters, **kwargs ) if created: action = MutationAction.ADDED else: action = MutationAction.UPDATED elif "Delete" in cls.__name__: - obj, mutation = await cls.mutate_delete( - root=root, info=info, branch=context.branch, at=context.at, **kwargs - ) + obj, mutation = await cls.mutate_delete(info=info, branch=context.branch, at=context.at, **kwargs) action = MutationAction.REMOVED else: raise ValueError( @@ -107,15 +100,16 @@ async def mutate(cls, root: dict, info: GraphQLResolveInfo, *args, **kwargs): data = await obj.to_graphql(db=context.db, filter_sensitive=True) - message = messages.EventNodeMutated( + event = NodeMutatedEvent( branch=context.branch.name, kind=obj._schema.kind, node_id=obj.id, data=data, - action=action.value, - meta=Meta(initiator_id=WORKER_IDENTITY, request_id=request_id), + action=action, + meta=EventMeta(initiator_id=WORKER_IDENTITY, request_id=request_id), ) - context.background.add_task(services.send, message) + + context.background.add_task(context.service.event.send, event) return mutation @@ -147,7 +141,6 @@ async def _refresh_for_profile_update( @classmethod async def mutate_create( cls, - root: dict, info: GraphQLResolveInfo, data: InputObjectType, branch: Branch, @@ -206,7 +199,6 @@ async def mutate_create_to_graphql(cls, info: GraphQLResolveInfo, db: InfrahubDa @retry_db_transaction(name="object_update") async def mutate_update( cls, - root: dict, info: GraphQLResolveInfo, data: InputObjectType, branch: Branch, @@ -285,7 +277,6 @@ async def mutate_update_to_graphql( @retry_db_transaction(name="object_upsert") async def mutate_upsert( cls, - root: dict, info: GraphQLResolveInfo, data: InputObjectType, branch: Branch, @@ -308,26 +299,25 @@ async def mutate_upsert( if node: updated_obj, mutation = await cls.mutate_update( - root=root, info=info, data=data, branch=branch, at=at, database=db, node=node + info=info, data=data, branch=branch, at=at, database=db, node=node ) return updated_obj, mutation, False # We need to convert the InputObjectType into a dict in order to remove hfid that isn't a valid input when creating the object data_dict = dict(data) if "hfid" in data: del data_dict["hfid"] - created_obj, mutation = await cls.mutate_create(root=root, info=info, data=data_dict, branch=branch, at=at) + created_obj, mutation = await cls.mutate_create(info=info, data=data_dict, branch=branch, at=at) return created_obj, mutation, True @classmethod @retry_db_transaction(name="object_delete") async def mutate_delete( cls, - root, info: GraphQLResolveInfo, data: InputObjectType, branch: Branch, at: str, - ): + ) -> tuple[Node, Self]: context: GraphqlContext = info.context obj = await NodeManager.find_object( diff --git a/backend/infrahub/graphql/mutations/menu.py b/backend/infrahub/graphql/mutations/menu.py new file mode 100644 index 0000000000..158f753798 --- /dev/null +++ b/backend/infrahub/graphql/mutations/menu.py @@ -0,0 +1,103 @@ +from typing import TYPE_CHECKING, Any, Optional + +from graphene import InputObjectType, Mutation +from graphql import GraphQLResolveInfo +from typing_extensions import Self + +from infrahub.core.branch import Branch +from infrahub.core.constants import RESTRICTED_NAMESPACES +from infrahub.core.manager import NodeManager +from infrahub.core.node import Node +from infrahub.core.protocols import CoreMenuItem +from infrahub.core.schema import NodeSchema +from infrahub.database import InfrahubDatabase +from infrahub.exceptions import ValidationError +from infrahub.graphql.mutations.main import InfrahubMutationMixin + +from .main import InfrahubMutationOptions + +if TYPE_CHECKING: + from infrahub.graphql.initialization import GraphqlContext + +EXTENDED_RESTRICTED_NAMESPACES = RESTRICTED_NAMESPACES + ["Builtin"] + + +def validate_namespace(data: InputObjectType) -> None: + namespace = data.get("namespace") + if isinstance(namespace, dict) and "value" in namespace: + namespace_value = str(namespace.get("value")) + if namespace_value in EXTENDED_RESTRICTED_NAMESPACES: + raise ValidationError( + input_value={"namespace": f"{namespace_value} is not valid, it's a restricted namespace"} + ) + + +class InfrahubCoreMenuMutation(InfrahubMutationMixin, Mutation): + @classmethod + def __init_subclass_with_meta__( # pylint: disable=arguments-differ + cls, schema: NodeSchema, _meta: Optional[Any] = None, **options: dict[str, Any] + ) -> None: + # Make sure schema is a valid NodeSchema Node Class + if not isinstance(schema, NodeSchema): + raise ValueError(f"You need to pass a valid NodeSchema in '{cls.__name__}.Meta', received '{schema}'") + + if not _meta: + _meta = InfrahubMutationOptions(cls) + _meta.schema = schema + + super().__init_subclass_with_meta__(_meta=_meta, **options) + + @classmethod + async def mutate_create( + cls, + info: GraphQLResolveInfo, + data: InputObjectType, + branch: Branch, + at: str, + database: Optional[InfrahubDatabase] = None, + ) -> tuple[Node, Self]: + validate_namespace(data=data) + + obj, result = await super().mutate_create(info=info, data=data, branch=branch, at=at) + + return obj, result + + @classmethod + async def mutate_update( + cls, + info: GraphQLResolveInfo, + data: InputObjectType, + branch: Branch, + at: str, + database: Optional[InfrahubDatabase] = None, + node: Optional[Node] = None, + ) -> tuple[Node, Self]: + context: GraphqlContext = info.context + + obj = await NodeManager.find_object( + db=context.db, kind=CoreMenuItem, id=data.get("id"), hfid=data.get("hfid"), branch=branch, at=at + ) + validate_namespace(data=data) + + if obj.protected.value: + raise ValidationError(input_value="This object is protected, it can't be modified.") + + obj, result = await super().mutate_update(info=info, data=data, branch=branch, at=at, node=obj) # type: ignore[assignment] + return obj, result # type: ignore[return-value] + + @classmethod + async def mutate_delete( + cls, + info: GraphQLResolveInfo, + data: InputObjectType, + branch: Branch, + at: str, + ) -> tuple[Node, Self]: + context: GraphqlContext = info.context + obj = await NodeManager.find_object( + db=context.db, kind=CoreMenuItem, id=data.get("id"), hfid=data.get("hfid"), branch=branch, at=at + ) + if obj.protected.value: + raise ValidationError(input_value="This object is protected, it can't be deleted.") + + return await super().mutate_delete(info=info, data=data, branch=branch, at=at) diff --git a/backend/infrahub/graphql/mutations/node_getter/by_default_filter.py b/backend/infrahub/graphql/mutations/node_getter/by_default_filter.py index fc4c675418..f5f14cd240 100644 --- a/backend/infrahub/graphql/mutations/node_getter/by_default_filter.py +++ b/backend/infrahub/graphql/mutations/node_getter/by_default_filter.py @@ -12,7 +12,7 @@ class MutationNodeGetterByDefaultFilter(MutationNodeGetterInterface): - def __init__(self, db: InfrahubDatabase, node_manager: NodeManager): + def __init__(self, db: InfrahubDatabase, node_manager: NodeManager) -> None: self.db = db self.node_manager = node_manager diff --git a/backend/infrahub/graphql/mutations/node_getter/by_hfid.py b/backend/infrahub/graphql/mutations/node_getter/by_hfid.py index 05ff717ea4..808a699600 100644 --- a/backend/infrahub/graphql/mutations/node_getter/by_hfid.py +++ b/backend/infrahub/graphql/mutations/node_getter/by_hfid.py @@ -13,7 +13,7 @@ class MutationNodeGetterByHfid(MutationNodeGetterInterface): - def __init__(self, db: InfrahubDatabase, node_manager: NodeManager): + def __init__(self, db: InfrahubDatabase, node_manager: NodeManager) -> None: self.db = db self.node_manager = node_manager diff --git a/backend/infrahub/graphql/mutations/node_getter/by_id.py b/backend/infrahub/graphql/mutations/node_getter/by_id.py index 383db251b3..f307e7f436 100644 --- a/backend/infrahub/graphql/mutations/node_getter/by_id.py +++ b/backend/infrahub/graphql/mutations/node_getter/by_id.py @@ -12,7 +12,7 @@ class MutationNodeGetterById(MutationNodeGetterInterface): - def __init__(self, db: InfrahubDatabase, node_manager: NodeManager): + def __init__(self, db: InfrahubDatabase, node_manager: NodeManager) -> None: self.db = db self.node_manager = node_manager diff --git a/backend/infrahub/graphql/mutations/proposed_change.py b/backend/infrahub/graphql/mutations/proposed_change.py index 32b7f287ef..449211140d 100644 --- a/backend/infrahub/graphql/mutations/proposed_change.py +++ b/backend/infrahub/graphql/mutations/proposed_change.py @@ -5,7 +5,7 @@ from infrahub import lock from infrahub.core.branch import Branch -from infrahub.core.constants import CheckType, InfrahubKind, ProposedChangeState, ValidatorConclusion +from infrahub.core.constants import CheckType, GlobalPermissions, InfrahubKind, ProposedChangeState, ValidatorConclusion from infrahub.core.diff.ipam_diff_parser import IpamDiffParser from infrahub.core.manager import NodeManager from infrahub.core.merge import BranchMerger @@ -25,7 +25,7 @@ from .main import InfrahubMutationOptions if TYPE_CHECKING: - from .. import GraphqlContext + from ..initialization import GraphqlContext class InfrahubProposedChangeMutation(InfrahubMutationMixin, Mutation): @@ -45,7 +45,6 @@ def __init_subclass_with_meta__(cls, schema: NodeSchema = None, _meta=None, **op @retry_db_transaction(name="proposed_change_create") async def mutate_create( cls, - root: dict, info: GraphQLResolveInfo, data: InputObjectType, branch: Branch, @@ -57,7 +56,7 @@ async def mutate_create( async with db.start_transaction() as dbt: proposed_change, result = await super().mutate_create( - root=root, info=info, data=data, branch=branch, at=at, database=dbt + info=info, data=data, branch=branch, at=at, database=dbt ) destination_branch = proposed_change.destination_branch.value source_branch = await _get_source_branch(db=dbt, name=proposed_change.source_branch.value) @@ -85,9 +84,8 @@ async def mutate_create( @classmethod @retry_db_transaction(name="proposed_change_update") - async def mutate_update( + async def mutate_update( # pylint: disable=too-many-branches cls, - root: dict, info: GraphQLResolveInfo, data: InputObjectType, branch: Branch, @@ -97,6 +95,19 @@ async def mutate_update( ): context: GraphqlContext = info.context + has_merge_permission = False + if context.account_session: + for permission_backend in registry.permission_backends: + if has_merge_permission := await permission_backend.has_permission( + db=context.db, + account_id=context.active_account_session.account_id, + permission=f"global:{GlobalPermissions.MERGE_PROPOSED_CHANGE.value}:allow", + branch=branch, + ): + break + else: + has_merge_permission = True + obj = await NodeManager.get_one_by_id_or_default_filter( db=context.db, kind=cls._meta.schema.kind, @@ -114,10 +125,14 @@ async def mutate_update( updated_state = ProposedChangeState(state_update) state.validate_state_transition(updated_state) + # Check before starting a transaction, stopping in the middle of the transaction seems to break with memgraph + if updated_state == ProposedChangeState.MERGED and not has_merge_permission: + raise ValidationError("You do not have the permission to merge proposed changes") + merger: Optional[BranchMerger] = None async with context.db.start_transaction() as dbt: proposed_change, result = await super().mutate_update( - root=root, info=info, data=data, branch=branch, at=at, database=dbt, node=obj + info=info, data=data, branch=branch, at=at, database=dbt, node=obj ) if updated_state == ProposedChangeState.MERGED: diff --git a/backend/infrahub/graphql/mutations/relationship.py b/backend/infrahub/graphql/mutations/relationship.py index 1f14e944dc..c0ae39fbec 100644 --- a/backend/infrahub/graphql/mutations/relationship.py +++ b/backend/infrahub/graphql/mutations/relationship.py @@ -22,7 +22,7 @@ from infrahub.core.relationship import RelationshipManager - from .. import GraphqlContext + from ..initialization import GraphqlContext # pylint: disable=unused-argument,too-many-branches diff --git a/backend/infrahub/graphql/mutations/repository.py b/backend/infrahub/graphql/mutations/repository.py index 57be6fae76..2a5612d27b 100644 --- a/backend/infrahub/graphql/mutations/repository.py +++ b/backend/infrahub/graphql/mutations/repository.py @@ -23,7 +23,7 @@ from infrahub.core.branch import Branch from infrahub.core.node import Node from infrahub.database import InfrahubDatabase - from infrahub.graphql import GraphqlContext + from infrahub.graphql.initialization import GraphqlContext log = get_logger() @@ -45,7 +45,6 @@ def __init_subclass_with_meta__(cls, schema: Optional[NodeSchema] = None, _meta= @classmethod async def mutate_create( cls, - root: dict, info: GraphQLResolveInfo, data: InputObjectType, branch: Branch, @@ -56,7 +55,7 @@ async def mutate_create( cleanup_payload(data) # Create the object in the database - obj, result = await super().mutate_create(root, info, data, branch, at) + obj, result = await super().mutate_create(info, data, branch, at) obj = cast(CoreGenericRepository, obj) # First check the connectivity to the remote repository @@ -120,7 +119,6 @@ async def mutate_create( @classmethod async def mutate_update( cls, - root: dict, info: GraphQLResolveInfo, data: InputObjectType, branch: Branch, @@ -142,7 +140,7 @@ async def mutate_update( include_source=True, ) if node.get_kind() != InfrahubKind.READONLYREPOSITORY: - return await super().mutate_update(root, info, data, branch, at, database=context.db, node=node) + return await super().mutate_update(info, data, branch, at, database=context.db, node=node) node = cast(CoreReadOnlyRepository, node) current_commit = node.commit.value @@ -154,7 +152,7 @@ async def mutate_update( if data.ref and data.ref.value: new_ref = data.ref.value - obj, result = await super().mutate_update(root, info, data, branch, at, database=context.db, node=node) + obj, result = await super().mutate_update(info, data, branch, at, database=context.db, node=node) obj = cast(CoreReadOnlyRepository, obj) send_update_message = (new_commit and new_commit != current_commit) or (new_ref and new_ref != current_ref) diff --git a/backend/infrahub/graphql/mutations/resource_manager.py b/backend/infrahub/graphql/mutations/resource_manager.py index 2f116d612e..227a64b8d2 100644 --- a/backend/infrahub/graphql/mutations/resource_manager.py +++ b/backend/infrahub/graphql/mutations/resource_manager.py @@ -24,7 +24,7 @@ from infrahub.core.node.resource_manager.ip_prefix_pool import CoreIPPrefixPool from infrahub.database import InfrahubDatabase - from .. import GraphqlContext + from ..initialization import GraphqlContext class IPPrefixPoolGetResourceInput(InputObjectType): @@ -171,7 +171,6 @@ def __init_subclass_with_meta__( # pylint: disable=arguments-differ @classmethod async def mutate_create( cls, - root: dict, info: GraphQLResolveInfo, data: InputObjectType, branch: Branch, @@ -197,12 +196,11 @@ async def mutate_create( if data["start_range"].value > data["end_range"].value: raise ValidationError(input_value="start_range can't be larger than end_range") - return await super().mutate_create(root=root, info=info, data=data, branch=branch, at=at) + return await super().mutate_create(info=info, data=data, branch=branch, at=at) @classmethod async def mutate_update( cls, - root: dict, info: GraphQLResolveInfo, data: InputObjectType, branch: Branch, @@ -218,9 +216,9 @@ async def mutate_update( async with context.db.start_transaction() as dbt: number_pool, result = await super().mutate_update( - root=root, info=info, data=data, branch=branch, at=at, database=dbt, node=node + info=info, data=data, branch=branch, at=at, database=dbt, node=node ) - if number_pool.start_range.value > number_pool.end_range.value: + if number_pool.start_range.value > number_pool.end_range.value: # type: ignore[attr-defined] raise ValidationError(input_value="start_range can't be larger than end_range") return number_pool, result diff --git a/backend/infrahub/graphql/mutations/schema.py b/backend/infrahub/graphql/mutations/schema.py index bbdf32bbe3..9a7869ff93 100644 --- a/backend/infrahub/graphql/mutations/schema.py +++ b/backend/infrahub/graphql/mutations/schema.py @@ -19,7 +19,7 @@ from ..types import DropdownFields if TYPE_CHECKING: - from .. import GraphqlContext + from ..initialization import GraphqlContext log = get_logger() diff --git a/backend/infrahub/graphql/mutations/task.py b/backend/infrahub/graphql/mutations/task.py index ec074f9fa2..9d49b1b3ad 100644 --- a/backend/infrahub/graphql/mutations/task.py +++ b/backend/infrahub/graphql/mutations/task.py @@ -18,7 +18,7 @@ if TYPE_CHECKING: from graphql import GraphQLResolveInfo - from infrahub.graphql import GraphqlContext + from infrahub.graphql.initialization import GraphqlContext TaskConclusion = Enum.from_enum(PyTaskConclusion) diff --git a/backend/infrahub/graphql/permissions.py b/backend/infrahub/graphql/permissions.py new file mode 100644 index 0000000000..ada4c6d184 --- /dev/null +++ b/backend/infrahub/graphql/permissions.py @@ -0,0 +1,30 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any + +from infrahub.core.registry import registry +from infrahub.core.schema import GenericSchema +from infrahub.permissions.report import report_schema_permissions + +if TYPE_CHECKING: + from infrahub.core.schema import MainSchemaTypes + from infrahub.database import InfrahubDatabase + from infrahub.graphql.initialization import GraphqlContext + + +async def get_permissions(db: InfrahubDatabase, schema: MainSchemaTypes, context: GraphqlContext) -> dict[str, Any]: + schema_objects = [schema] + if isinstance(schema, GenericSchema): + for node_name in schema.used_by: + schema_objects.append( + registry.schema.get_node_schema(name=node_name, branch=context.branch, duplicate=False) + ) + + response: dict[str, Any] = {"count": len(schema_objects), "edges": []} + + nodes = await report_schema_permissions( + db=db, schemas=schema_objects, branch=context.branch, account_session=context.active_account_session + ) + response["edges"] = [{"node": node} for node in nodes] + + return response diff --git a/backend/infrahub/graphql/queries/__init__.py b/backend/infrahub/graphql/queries/__init__.py index 25aa60ea1a..ca05880f36 100644 --- a/backend/infrahub/graphql/queries/__init__.py +++ b/backend/infrahub/graphql/queries/__init__.py @@ -1,4 +1,4 @@ -from .account import AccountToken +from .account import AccountPermissions, AccountToken from .branch import BranchQueryList from .diff.diff import DiffSummary from .internal import InfrahubInfo @@ -10,16 +10,17 @@ from .task import Task __all__ = [ + "AccountPermissions", "AccountToken", "BranchQueryList", "DiffSummary", - "InfrahubInfo", - "InfrahubSearchAnywhere", - "InfrahubStatus", "InfrahubIPAddressGetNextAvailable", "InfrahubIPPrefixGetNextAvailable", + "InfrahubInfo", "InfrahubResourcePoolAllocated", "InfrahubResourcePoolUtilization", + "InfrahubSearchAnywhere", + "InfrahubStatus", "Relationship", "Task", ] diff --git a/backend/infrahub/graphql/queries/account.py b/backend/infrahub/graphql/queries/account.py index 17f9471494..792f868c02 100644 --- a/backend/infrahub/graphql/queries/account.py +++ b/backend/infrahub/graphql/queries/account.py @@ -5,6 +5,7 @@ from graphene import Field, Int, List, ObjectType, String from infrahub_sdk.utils import extract_fields_first_node +from infrahub.core import registry from infrahub.core.manager import NodeManager from infrahub.core.protocols import InternalAccountToken from infrahub.exceptions import PermissionDeniedError @@ -12,7 +13,8 @@ if TYPE_CHECKING: from graphql import GraphQLResolveInfo - from infrahub.graphql import GraphqlContext + from infrahub.graphql.initialization import GraphqlContext + from infrahub.permissions.constants import AssignedPermissions class AccountTokenNode(ObjectType): @@ -64,3 +66,101 @@ async def resolve_account_tokens( AccountToken = Field( AccountTokenEdges, resolver=resolve_account_tokens, limit=Int(required=False), offset=Int(required=False) ) + + +class AccountGlobalPermissionNode(ObjectType): + id = Field(String, required=True) + name = Field(String, required=True) + action = Field(String, required=True) + decision = Field(String, required=True) + identifier = Field(String, required=True) + + +class AccountObjectPermissionNode(ObjectType): + id = Field(String, required=True) + branch = Field(String, required=True) + namespace = Field(String, required=True) + name = Field(String, required=True) + action = Field(String, required=True) + decision = Field(String, required=True) + identifier = Field(String, required=True) + + +class AccountGlobalPermissionEdge(ObjectType): + node = Field(AccountGlobalPermissionNode, required=True) + + +class AccountObjectPermissionEdge(ObjectType): + node = Field(AccountObjectPermissionNode, required=True) + + +class AccountGlobalPermissionEdges(ObjectType): + count = Field(Int, required=True) + edges = Field(List(of_type=AccountGlobalPermissionEdge, required=True), required=True) + + +class AccountObjectPermissionEdges(ObjectType): + count = Field(Int, required=True) + edges = Field(List(of_type=AccountObjectPermissionEdge, required=True), required=True) + + +class AccountPermissionsEdges(ObjectType): + global_permissions = Field(AccountGlobalPermissionEdges, required=False) + object_permissions = Field(AccountObjectPermissionEdges, required=False) + + +async def resolve_account_permissions( + root: dict, # pylint: disable=unused-argument + info: GraphQLResolveInfo, +) -> dict: + context: GraphqlContext = info.context + + if not context.account_session: + raise ValueError("An account_session is mandatory to execute this query") + + fields = await extract_fields_first_node(info) + permissions: AssignedPermissions = {"global_permissions": [], "object_permissions": []} + for permission_backend in registry.permission_backends: + backend_permissions = await permission_backend.load_permissions( + db=context.db, account_id=context.account_session.account_id, branch=context.branch + ) + permissions["global_permissions"].extend(backend_permissions["global_permissions"]) + permissions["object_permissions"].extend(backend_permissions["object_permissions"]) + + response: dict[str, dict[str, Any]] = {} + if "global_permissions" in fields: + global_list = permissions["global_permissions"] + response["global_permissions"] = {"count": len(global_list)} + response["global_permissions"]["edges"] = [ + { + "node": { + "id": obj.id, + "name": obj.name, + "action": obj.action, + "decision": obj.decision, + "identifier": str(obj), + } + } + for obj in global_list + ] + if "object_permissions" in fields: + object_list = permissions["object_permissions"] + response["object_permissions"] = {"count": len(object_list)} + response["object_permissions"]["edges"] = [ + { + "node": { + "id": obj.id, + "branch": obj.branch, + "namespace": obj.namespace, + "name": obj.name, + "action": obj.action, + "decision": obj.decision, + "identifier": str(obj), + } + } + for obj in object_list + ] + return response + + +AccountPermissions = Field(AccountPermissionsEdges, resolver=resolve_account_permissions) diff --git a/backend/infrahub/graphql/queries/diff/diff.py b/backend/infrahub/graphql/queries/diff/diff.py index 92e2a2ac2f..344388a824 100644 --- a/backend/infrahub/graphql/queries/diff/diff.py +++ b/backend/infrahub/graphql/queries/diff/diff.py @@ -16,7 +16,7 @@ if TYPE_CHECKING: from graphql import GraphQLResolveInfo - from infrahub.graphql import GraphqlContext + from infrahub.graphql.initialization import GraphqlContext GrapheneDiffActionEnum = GrapheneEnum.from_enum(DiffAction) diff --git a/backend/infrahub/graphql/queries/diff/tree.py b/backend/infrahub/graphql/queries/diff/tree.py index c5287b3737..1d4d5c0ccc 100644 --- a/backend/infrahub/graphql/queries/diff/tree.py +++ b/backend/infrahub/graphql/queries/diff/tree.py @@ -31,7 +31,7 @@ EnrichedDiffSingleRelationship, ) from infrahub.database import InfrahubDatabase - from infrahub.graphql import GraphqlContext + from infrahub.graphql.initialization import GraphqlContext GrapheneDiffActionEnum = GrapheneEnum.from_enum(DiffAction) GrapheneCardinalityEnum = GrapheneEnum.from_enum(RelationshipCardinality) diff --git a/backend/infrahub/graphql/queries/ipam.py b/backend/infrahub/graphql/queries/ipam.py index c2b8e9a4b7..df27b364f2 100644 --- a/backend/infrahub/graphql/queries/ipam.py +++ b/backend/infrahub/graphql/queries/ipam.py @@ -15,7 +15,7 @@ if TYPE_CHECKING: from graphql import GraphQLResolveInfo - from infrahub.graphql import GraphqlContext + from infrahub.graphql.initialization import GraphqlContext class IPAddressGetNextAvailable(ObjectType): diff --git a/backend/infrahub/graphql/queries/relationship.py b/backend/infrahub/graphql/queries/relationship.py index 6d2312c0f0..40041f42a6 100644 --- a/backend/infrahub/graphql/queries/relationship.py +++ b/backend/infrahub/graphql/queries/relationship.py @@ -11,7 +11,7 @@ if TYPE_CHECKING: from graphql import GraphQLResolveInfo - from infrahub.graphql import GraphqlContext + from infrahub.graphql.initialization import GraphqlContext class Relationships(ObjectType): diff --git a/backend/infrahub/graphql/queries/resource_manager.py b/backend/infrahub/graphql/queries/resource_manager.py index 420734a038..9b75e5f826 100644 --- a/backend/infrahub/graphql/queries/resource_manager.py +++ b/backend/infrahub/graphql/queries/resource_manager.py @@ -24,7 +24,7 @@ from infrahub.core.node import Node from infrahub.core.protocols import CoreNode from infrahub.database import InfrahubDatabase - from infrahub.graphql import GraphqlContext + from infrahub.graphql.initialization import GraphqlContext class IPPoolUtilizationResource(ObjectType): diff --git a/backend/infrahub/graphql/queries/search.py b/backend/infrahub/graphql/queries/search.py index 831ac691b5..36f1c4273a 100644 --- a/backend/infrahub/graphql/queries/search.py +++ b/backend/infrahub/graphql/queries/search.py @@ -12,7 +12,7 @@ from graphql import GraphQLResolveInfo from infrahub.core.protocols import CoreNode - from infrahub.graphql import GraphqlContext + from infrahub.graphql.initialization import GraphqlContext class Node(ObjectType): diff --git a/backend/infrahub/graphql/queries/status.py b/backend/infrahub/graphql/queries/status.py index d1f7c94093..971b6dbf68 100644 --- a/backend/infrahub/graphql/queries/status.py +++ b/backend/infrahub/graphql/queries/status.py @@ -10,7 +10,7 @@ if TYPE_CHECKING: from graphql import GraphQLResolveInfo - from infrahub.graphql import GraphqlContext + from infrahub.graphql.initialization import GraphqlContext class StatusSummary(ObjectType): @@ -46,9 +46,7 @@ async def resolve_status( service = context.service or services.service fields = await extract_fields_first_node(info) response: dict[str, Any] = {} - workers = await service.component.list_workers( - branch=context.branch.id if context.branch.id else context.branch.name, schema_hash=True - ) + workers = await service.component.list_workers(branch=context.branch.id or context.branch.name, schema_hash=True) if summary := fields.get("summary"): response["summary"] = {} diff --git a/backend/infrahub/graphql/queries/task.py b/backend/infrahub/graphql/queries/task.py index f669ddd386..713b9b2540 100644 --- a/backend/infrahub/graphql/queries/task.py +++ b/backend/infrahub/graphql/queries/task.py @@ -11,7 +11,7 @@ if TYPE_CHECKING: from graphql import GraphQLResolveInfo - from infrahub.graphql import GraphqlContext + from infrahub.graphql.initialization import GraphqlContext class Tasks(ObjectType): diff --git a/backend/infrahub/graphql/query.py b/backend/infrahub/graphql/query.py index 0c815079f0..c049f82313 100644 --- a/backend/infrahub/graphql/query.py +++ b/backend/infrahub/graphql/query.py @@ -10,7 +10,7 @@ from infrahub.core.protocols import CoreGraphQLQuery from infrahub.core.registry import registry from infrahub.core.timestamp import Timestamp -from infrahub.graphql import prepare_graphql_params +from infrahub.graphql.initialization import prepare_graphql_params if TYPE_CHECKING: from graphql.execution import ExecutionResult diff --git a/backend/infrahub/graphql/resolver.py b/backend/infrahub/graphql/resolver.py index 99229140bd..820d25f8be 100644 --- a/backend/infrahub/graphql/resolver.py +++ b/backend/infrahub/graphql/resolver.py @@ -4,20 +4,44 @@ from infrahub_sdk.utils import extract_fields -from infrahub.core.constants import BranchSupportType, RelationshipHierarchyDirection +from infrahub.core.constants import BranchSupportType, InfrahubKind, RelationshipHierarchyDirection from infrahub.core.manager import NodeManager from infrahub.core.query.node import NodeGetHierarchyQuery +from infrahub.exceptions import NodeNotFoundError +from .parser import extract_selection +from .permissions import get_permissions from .types import RELATIONS_PROPERTY_MAP, RELATIONS_PROPERTY_MAP_REVERSED if TYPE_CHECKING: from graphql import GraphQLResolveInfo - from infrahub.core.schema import NodeSchema - from infrahub.graphql import GraphqlContext + from infrahub.core.schema import MainSchemaTypes, NodeSchema + from infrahub.graphql.initialization import GraphqlContext -async def default_resolver(*args, **kwargs): +async def account_resolver( + root, # pylint: disable=unused-argument + info: GraphQLResolveInfo, +) -> dict: + fields = await extract_fields(info.field_nodes[0].selection_set) + context: GraphqlContext = info.context + + async with context.db.start_session() as db: + results = await NodeManager.query( + schema=InfrahubKind.GENERICACCOUNT, + filters={"ids": [context.account_session.account_id]}, + fields=fields, + db=db, + ) + if results: + account_profile = await results[0].to_graphql(db=db, fields=fields) + return account_profile + + raise NodeNotFoundError(node_type=InfrahubKind.GENERICACCOUNT, identifier=context.account_session.account_id) + + +async def default_resolver(*args: Any, **kwargs) -> dict | list[dict] | None: """Not sure why but the default resolver returns sometime 4 positional args and sometime 2. When it returns 4, they are organized as follow @@ -94,6 +118,71 @@ async def default_resolver(*args, **kwargs): return await objs[0].to_graphql(db=db, fields=fields, related_node_ids=context.related_node_ids) +async def default_paginated_list_resolver( + root: dict, # pylint: disable=unused-argument + info: GraphQLResolveInfo, + offset: int | None = None, + limit: int | None = None, + partial_match: bool = False, + **kwargs: dict[str, Any], +) -> dict[str, Any]: + schema: MainSchemaTypes = info.return_type.graphene_type._meta.schema + fields = await extract_selection(info.field_nodes[0], schema=schema) + + context: GraphqlContext = info.context + async with context.db.start_session() as db: + response: dict[str, Any] = {"edges": []} + filters = { + key: value for key, value in kwargs.items() if ("__" in key and value is not None) or key in ("ids", "hfid") + } + + edges = fields.get("edges", {}) + node_fields = edges.get("node", {}) + + permissions = fields.get("permissions") + if permissions: + response["permissions"] = await get_permissions(db=db, schema=schema, context=context) + + objs = [] + if edges or "hfid" in filters: + objs = await NodeManager.query( + db=db, + schema=schema, + filters=filters or None, + fields=node_fields, + at=context.at, + branch=context.branch, + limit=limit, + offset=offset, + account=context.account_session, + include_source=True, + include_owner=True, + partial_match=partial_match, + ) + + if "count" in fields: + if filters.get("hfid"): + response["count"] = len(objs) + else: + response["count"] = await NodeManager.count( + db=db, + schema=schema, + filters=filters, + at=context.at, + branch=context.branch, + partial_match=partial_match, + ) + + if objs: + objects = [ + {"node": await obj.to_graphql(db=db, fields=node_fields, related_node_ids=context.related_node_ids)} + for obj in objs + ] + response["edges"] = objects + + return response + + async def single_relationship_resolver(parent: dict, info: GraphQLResolveInfo, **kwargs) -> dict[str, Any]: """Resolver for relationships of cardinality=one for Edged responses diff --git a/backend/infrahub/graphql/schema.py b/backend/infrahub/graphql/schema.py index c56cfac1f7..a43fe5d789 100644 --- a/backend/infrahub/graphql/schema.py +++ b/backend/infrahub/graphql/schema.py @@ -1,42 +1,44 @@ from __future__ import annotations -from typing import TYPE_CHECKING - from graphene import ObjectType -from infrahub_sdk.utils import extract_fields - -from infrahub.core.constants import InfrahubKind -from infrahub.core.manager import NodeManager -from infrahub.exceptions import NodeNotFoundError -from .mutations import ( +from .mutations.account import ( + InfrahubAccountSelfUpdate, + InfrahubAccountTokenCreate, + InfrahubAccountTokenDelete, +) +from .mutations.branch import ( BranchCreate, BranchDelete, BranchMerge, BranchRebase, BranchUpdate, BranchValidate, - DiffUpdateMutation, - InfrahubAccountSelfUpdate, - InfrahubAccountTokenCreate, - InfrahubAccountTokenDelete, - IPAddressPoolGetResource, - IPPrefixPoolGetResource, - ProcessRepository, - ProposedChangeRequestRunCheck, +) +from .mutations.diff import DiffUpdateMutation +from .mutations.diff_conflict import ResolveDiffConflict +from .mutations.proposed_change import ProposedChangeRequestRunCheck +from .mutations.relationship import ( RelationshipAdd, RelationshipRemove, - ResolveDiffConflict, +) +from .mutations.repository import ( + ProcessRepository, + ValidateRepositoryConnectivity, +) +from .mutations.resource_manager import IPAddressPoolGetResource, IPPrefixPoolGetResource +from .mutations.schema import ( SchemaDropdownAdd, SchemaDropdownRemove, SchemaEnumAdd, SchemaEnumRemove, +) +from .mutations.task import ( TaskCreate, TaskUpdate, - ValidateRepositoryConnectivity, ) -from .parser import extract_selection from .queries import ( + AccountPermissions, AccountToken, BranchQueryList, DiffSummary, @@ -52,42 +54,11 @@ ) from .queries.diff.tree import DiffTreeQuery, DiffTreeSummaryQuery -if TYPE_CHECKING: - from graphql import GraphQLResolveInfo - - from . import GraphqlContext - - -# pylint: disable=unused-argument - - -async def default_paginated_list_resolver(root: dict, info: GraphQLResolveInfo, **kwargs): - fields = await extract_selection(info.field_nodes[0], schema=info.return_type.graphene_type._meta.schema) - - return await info.return_type.graphene_type.get_paginated_list(**kwargs, fields=fields, context=info.context) - - -async def account_resolver(root, info: GraphQLResolveInfo): - fields = await extract_fields(info.field_nodes[0].selection_set) - context: GraphqlContext = info.context - - async with context.db.start_session() as db: - results = await NodeManager.query( - schema=InfrahubKind.GENERICACCOUNT, - filters={"ids": [context.account_session.account_id]}, - fields=fields, - db=db, - ) - if results: - account_profile = await results[0].to_graphql(db=db, fields=fields) - return account_profile - - raise NodeNotFoundError(node_type=InfrahubKind.GENERICACCOUNT, identifier=context.account_session.account_id) - class InfrahubBaseQuery(ObjectType): Branch = BranchQueryList InfrahubAccountToken = AccountToken + InfrahubPermissions = AccountPermissions DiffTree = DiffTreeQuery DiffTreeSummary = DiffTreeSummaryQuery diff --git a/backend/infrahub/graphql/subscription/graphql_query.py b/backend/infrahub/graphql/subscription/graphql_query.py index 3eb1d6c536..20f866b4de 100644 --- a/backend/infrahub/graphql/subscription/graphql_query.py +++ b/backend/infrahub/graphql/subscription/graphql_query.py @@ -13,7 +13,7 @@ from infrahub.log import get_logger if TYPE_CHECKING: - from infrahub.graphql import GraphqlContext + from infrahub.graphql.initialization import GraphqlContext log = get_logger(name="infrahub.graphql") diff --git a/backend/infrahub/graphql/types/__init__.py b/backend/infrahub/graphql/types/__init__.py index c7c60dd640..9066c468a2 100644 --- a/backend/infrahub/graphql/types/__init__.py +++ b/backend/infrahub/graphql/types/__init__.py @@ -23,43 +23,43 @@ ) from .branch import BranchType from .interface import InfrahubInterface -from .mixin import GetListMixin from .node import InfrahubObject +from .permission import PaginatedObjectPermission from .relationship import RelationshipNode from .standard_node import InfrahubObjectType from .task import TaskNodes from .task_log import TaskLog, TaskLogEdge, TaskLogNodes __all__ = [ + "AnyAttributeType", "AttributeInterface", "BaseAttribute", + "BoolAttributeType", + "BranchType", + "CheckboxAttributeType", "DropdownFields", "DropdownType", "IPHostType", "IPNetworkType", - "MacAddressType", - "TextAttributeType", - "NumberAttributeType", - "CheckboxAttributeType", - "StrAttributeType", - "IntAttributeType", - "BoolAttributeType", - "ListAttributeType", - "JSONAttributeType", - "AnyAttributeType", - "BranchType", "InfrahubInterface", - "GetListMixin", "InfrahubObject", "InfrahubObjectType", + "IntAttributeType", + "JSONAttributeType", + "ListAttributeType", + "MacAddressType", + "NumberAttributeType", + "PaginatedObjectPermission", "RelatedIPAddressNodeInput", "RelatedNodeInput", "RelatedPrefixNodeInput", "RelationshipNode", + "StrAttributeType", "TaskLog", "TaskLogEdge", "TaskLogNodes", "TaskNodes", + "TextAttributeType", ] diff --git a/backend/infrahub/graphql/types/attribute.py b/backend/infrahub/graphql/types/attribute.py index dcb0b4252d..3dfd2af491 100644 --- a/backend/infrahub/graphql/types/attribute.py +++ b/backend/infrahub/graphql/types/attribute.py @@ -1,5 +1,7 @@ from __future__ import annotations +from typing import Any + from graphene import BigInt, Boolean, DateTime, Field, InputObjectType, Int, List, ObjectType, String from graphene.types.generic import GenericScalar @@ -70,7 +72,7 @@ class BaseAttribute(ObjectType): is_from_profile = Field(Boolean) @classmethod - def __init_subclass__(cls, **kwargs): + def __init_subclass__(cls, **kwargs: dict[str, Any]) -> None: super().__init_subclass__(**kwargs) registry.default_graphql_type[cls.__name__] = cls diff --git a/backend/infrahub/graphql/types/branch.py b/backend/infrahub/graphql/types/branch.py index 296b05c927..9526afde54 100644 --- a/backend/infrahub/graphql/types/branch.py +++ b/backend/infrahub/graphql/types/branch.py @@ -10,7 +10,7 @@ from .standard_node import InfrahubObjectType if TYPE_CHECKING: - from infrahub.graphql import GraphqlContext + from infrahub.graphql.initialization import GraphqlContext class BranchType(InfrahubObjectType): diff --git a/backend/infrahub/graphql/types/enums.py b/backend/infrahub/graphql/types/enums.py index 45e2f06521..df26aab8b9 100644 --- a/backend/infrahub/graphql/types/enums.py +++ b/backend/infrahub/graphql/types/enums.py @@ -5,3 +5,5 @@ CheckType = Enum.from_enum(constants.CheckType) Severity = Enum.from_enum(constants.Severity) + +PermissionDecision = Enum.from_enum(constants.PermissionDecision) diff --git a/backend/infrahub/graphql/types/interface.py b/backend/infrahub/graphql/types/interface.py index 0c95000114..11433ba54f 100644 --- a/backend/infrahub/graphql/types/interface.py +++ b/backend/infrahub/graphql/types/interface.py @@ -5,12 +5,10 @@ from graphene import Interface from graphene.types.interface import InterfaceOptions -from .mixin import GetListMixin - if TYPE_CHECKING: from graphql import GraphQLResolveInfo - from infrahub.graphql import GraphqlContext + from infrahub.graphql.initialization import GraphqlContext from infrahub.graphql.types import InfrahubObject @@ -18,7 +16,7 @@ class InfrahubInterfaceOptions(InterfaceOptions): schema = None -class InfrahubInterface(Interface, GetListMixin): +class InfrahubInterface(Interface): @classmethod def resolve_type(cls, instance: dict[str, Any], info: GraphQLResolveInfo) -> InfrahubObject: context: GraphqlContext = info.context diff --git a/backend/infrahub/graphql/types/mixin.py b/backend/infrahub/graphql/types/mixin.py deleted file mode 100644 index 6138a46b85..0000000000 --- a/backend/infrahub/graphql/types/mixin.py +++ /dev/null @@ -1,91 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Any - -from infrahub.core.manager import NodeManager - -if TYPE_CHECKING: - from infrahub.graphql import GraphqlContext - - -class GetListMixin: - """Mixins to Query the list of nodes using the NodeManager.""" - - @classmethod - async def get_list(cls, fields: dict, context: GraphqlContext, **kwargs): - async with context.db.start_session() as db: - filters = {key: value for key, value in kwargs.items() if ("__" in key and value) or key in ("ids", "hfid")} - - objs = await NodeManager.query( - db=db, - schema=cls._meta.schema, - filters=filters or None, - fields=fields, - at=context.at, - branch=context.branch, - account=context.account_session, - include_source=True, - include_owner=True, - ) - - if not objs: - return [] - return [ - await obj.to_graphql(db=db, fields=fields, related_node_ids=context.related_node_ids) for obj in objs - ] - - @classmethod - async def get_paginated_list(cls, fields: dict, context: GraphqlContext, **kwargs): - partial_match = kwargs.pop("partial_match", False) - - async with context.db.start_session() as db: - response: dict[str, Any] = {"edges": []} - offset = kwargs.pop("offset", None) - limit = kwargs.pop("limit", None) - filters = { - key: value - for key, value in kwargs.items() - if ("__" in key and value is not None) or key in ("ids", "hfid") - } - - edges = fields.get("edges", {}) - node_fields = edges.get("node", {}) - - objs = [] - if edges or "hfid" in filters: - objs = await NodeManager.query( - db=db, - schema=cls._meta.schema, - filters=filters or None, - fields=node_fields, - at=context.at, - branch=context.branch, - limit=limit, - offset=offset, - account=context.account_session, - include_source=True, - include_owner=True, - partial_match=partial_match, - ) - - if "count" in fields: - if filters.get("hfid"): - response["count"] = len(objs) - else: - response["count"] = await NodeManager.count( - db=db, - schema=cls._meta.schema, - filters=filters, - at=context.at, - branch=context.branch, - partial_match=partial_match, - ) - - if objs: - objects = [ - {"node": await obj.to_graphql(db=db, fields=node_fields, related_node_ids=context.related_node_ids)} - for obj in objs - ] - response["edges"] = objects - - return response diff --git a/backend/infrahub/graphql/types/node.py b/backend/infrahub/graphql/types/node.py index c4dda8eb40..f476692f73 100644 --- a/backend/infrahub/graphql/types/node.py +++ b/backend/infrahub/graphql/types/node.py @@ -7,14 +7,12 @@ from infrahub.core.schema import GenericSchema, MainSchemaTypes, NodeSchema, ProfileSchema -from .mixin import GetListMixin - class InfrahubObjectOptions(ObjectTypeOptions): - schema: Optional[MainSchemaTypes] = None + schema: MainSchemaTypes -class InfrahubObject(ObjectType, GetListMixin): +class InfrahubObject(ObjectType): @classmethod def __init_subclass_with_meta__( # pylint: disable=arguments-differ cls, diff --git a/backend/infrahub/graphql/types/permission.py b/backend/infrahub/graphql/types/permission.py new file mode 100644 index 0000000000..2e92a1a12d --- /dev/null +++ b/backend/infrahub/graphql/types/permission.py @@ -0,0 +1,26 @@ +from __future__ import annotations + +from graphene import Field, Int, List, ObjectType, String + +from .enums import PermissionDecision + + +class ObjectPermission(ObjectType): + kind = Field(String, required=True, description="The kind this permission refers to.") + view = Field(PermissionDecision, required=True, description="Indicates if the account has the read permission") + create = Field(PermissionDecision, required=True, description="Indicates if the account has the create permission") + update = Field(PermissionDecision, required=True, description="Indicates if the account has the update permission") + delete = Field(PermissionDecision, required=True, description="Indicates if the account has the delete permission") + + +class ObjectPermissionNode(ObjectType): + node = Field(ObjectPermission, required=True) + + +class PaginatedObjectPermission(ObjectType): + count = Field( + Int, + required=True, + description="The number of permissions applicable, will be 1 for normal nodes or possibly more for generics", + ) + edges = Field(List(of_type=ObjectPermissionNode, required=True), required=True) diff --git a/backend/infrahub/graphql/types/standard_node.py b/backend/infrahub/graphql/types/standard_node.py index 19ac68267a..28b25e0eea 100644 --- a/backend/infrahub/graphql/types/standard_node.py +++ b/backend/infrahub/graphql/types/standard_node.py @@ -8,7 +8,7 @@ from infrahub import config if TYPE_CHECKING: - from infrahub.graphql import GraphqlContext + from infrahub.graphql.initialization import GraphqlContext class InfrahubObjectTypeOptions(ObjectTypeOptions): @@ -19,7 +19,7 @@ class InfrahubObjectType(ObjectType): @classmethod def __init_subclass_with_meta__( # pylint: disable=arguments-differ cls, model=None, interfaces=(), _meta=None, **options - ): + ) -> None: if not _meta: _meta = InfrahubObjectTypeOptions(cls) @@ -28,7 +28,7 @@ def __init_subclass_with_meta__( # pylint: disable=arguments-differ super().__init_subclass_with_meta__(_meta=_meta, interfaces=interfaces, **options) @classmethod - async def get_list(cls, fields: dict[str, Any], context: GraphqlContext, **kwargs): + async def get_list(cls, fields: dict[str, Any], context: GraphqlContext, **kwargs) -> list[dict[str, Any]]: async with context.db.session(database=config.SETTINGS.database.database_name) as db: filters = {key: value for key, value in kwargs.items() if "__" in key and value} diff --git a/backend/infrahub/graphql/utils.py b/backend/infrahub/graphql/utils.py index e956fdd585..48b15de931 100644 --- a/backend/infrahub/graphql/utils.py +++ b/backend/infrahub/graphql/utils.py @@ -69,7 +69,7 @@ def selected_field_names_fast( return selected_field_names(selection_set, context, runtime_type) -def selected_field_names_naive(selection_set: SelectionSetNode): +def selected_field_names_naive(selection_set: SelectionSetNode) -> list: """Get the list of field names that are selected at the current level. Does not include nested names. Limitations: @@ -179,7 +179,7 @@ def selected_field_names_from_context( return (field.name.value for fields_list in fields_map.values() for field in fields_list) -def print_query(info: GraphQLResolveInfo): +def print_query(info: GraphQLResolveInfo) -> None: """Traverse the query""" initial_selection_set = info.field_nodes[0].selection_set print_selection_set(initial_selection_set, 1) diff --git a/backend/infrahub/lock.py b/backend/infrahub/lock.py index 787a867920..d8a37d96c2 100644 --- a/backend/infrahub/lock.py +++ b/backend/infrahub/lock.py @@ -45,7 +45,7 @@ class InfrahubMultiLock: """Context manager to allow multiple locks to be reserved together""" - def __init__(self, _registry: InfrahubLockRegistry, locks: Optional[list[str]] = None): + def __init__(self, _registry: InfrahubLockRegistry, locks: Optional[list[str]] = None) -> None: self.registry = _registry self.locks = locks or [] @@ -72,7 +72,7 @@ async def release(self) -> None: class NATSLock: """Context manager to lock using NATS""" - def __init__(self, service: InfrahubServices, name: str): + def __init__(self, service: InfrahubServices, name: str) -> None: self.name = name self.token = None self.service = service @@ -118,7 +118,7 @@ def __init__( connection: Optional[Union[redis.Redis, InfrahubServices]] = None, local: Optional[bool] = None, in_multi: bool = False, - ): + ) -> None: self.use_local: bool = local self.local: LocalLock = None self.remote: GlobalLock = None @@ -175,7 +175,7 @@ async def locked(self) -> bool: class InfrahubLockRegistry: def __init__( self, token: Optional[str] = None, local_only: bool = False, service: Optional[InfrahubServices] = None - ): + ) -> None: if config.SETTINGS.cache.enable and not local_only: if config.SETTINGS.cache.driver == config.CacheDriver.Redis: self.connection = redis.Redis( @@ -256,6 +256,6 @@ async def wait_until_available(self, name: str) -> None: await sleep(0.1) -def initialize_lock(local_only: bool = False, service: Optional[InfrahubServices] = None): +def initialize_lock(local_only: bool = False, service: Optional[InfrahubServices] = None) -> None: global registry # pylint: disable=global-statement registry = InfrahubLockRegistry(local_only=local_only, service=service) diff --git a/sync/examples/ipfabric_to_infrahub/infrahub/__init__.py b/backend/infrahub/menu/__init__.py similarity index 100% rename from sync/examples/ipfabric_to_infrahub/infrahub/__init__.py rename to backend/infrahub/menu/__init__.py diff --git a/backend/infrahub/menu/constants.py b/backend/infrahub/menu/constants.py new file mode 100644 index 0000000000..e0040eb425 --- /dev/null +++ b/backend/infrahub/menu/constants.py @@ -0,0 +1,10 @@ +from infrahub.utils import InfrahubStringEnum + + +class MenuSection(InfrahubStringEnum): + OBJECT = "object" + INTERNAL = "internal" + + +DEFAULT_MENU = "Other" +FULL_DEFAULT_MENU = "Builtin:Other" diff --git a/backend/infrahub/menu/generator.py b/backend/infrahub/menu/generator.py new file mode 100644 index 0000000000..485d3dbf52 --- /dev/null +++ b/backend/infrahub/menu/generator.py @@ -0,0 +1,101 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from infrahub.core import registry +from infrahub.core.branch import Branch # noqa: TCH001 +from infrahub.core.protocols import CoreMenuItem +from infrahub.log import get_logger + +from .constants import FULL_DEFAULT_MENU +from .models import MenuDict, MenuItemDict + +if TYPE_CHECKING: + from infrahub.auth import AccountSession + from infrahub.database import InfrahubDatabase + +log = get_logger() + + +def get_full_name(obj: CoreMenuItem) -> str: + return f"{obj.namespace.value}:{obj.name.value}" + + +# pylint: disable=too-many-branches +async def generate_menu( + db: InfrahubDatabase, branch: Branch, menu_items: list[CoreMenuItem], account: AccountSession | None = None +) -> MenuDict: + # FIXME temp hack to avoid pylint to complain + account = account # noqa: PLW0127 + + structure = MenuDict() + full_schema = registry.schema.get_full(branch=branch, duplicate=False) + + already_processed = [] + havent_been_processed = [] + + # Process the parent first + for item in menu_items: + full_name = get_full_name(item) + parent = await item.parent.get_peer(db=db, peer_type=CoreMenuItem) + if parent: + havent_been_processed.append(full_name) + continue + structure.data[full_name] = MenuItemDict.from_node(obj=item) + already_processed.append(full_name) + + # Process the children + havent_been_processed = [] + for item in menu_items: + full_name = get_full_name(item) + if full_name in already_processed: + continue + + parent = await item.parent.get_peer(db=db, peer_type=CoreMenuItem) + if not parent: + havent_been_processed.append(full_name) + continue + + parent_full_name = get_full_name(parent) + menu_item = structure.find_item(name=parent_full_name) + if menu_item: + child_item = MenuItemDict.from_node(obj=item) + menu_item.children[child_item.identifier] = child_item + else: + log.warning( + "new_menu_request: unable to find the parent menu item", + branch=branch.name, + menu_item=item.name.value, + parent_item=parent.name.value, + ) + + default_menu = structure.find_item(name=FULL_DEFAULT_MENU) + if not default_menu: + raise ValueError("Unable to locate the default menu item") + + for schema in full_schema.values(): + if schema.include_in_menu is False: + continue + + menu_item = MenuItemDict.from_schema(model=schema) + already_in_schema = bool(structure.find_item(name=menu_item.identifier)) + if already_in_schema: + continue + + if schema.menu_placement: + menu_placement = structure.find_item(name=schema.menu_placement) + + if menu_placement: + menu_placement.children[menu_item.identifier] = menu_item + continue + + log.warning( + "new_menu_request: unable to find the menu_placement defined in the schema", + branch=branch.name, + item=schema.kind, + menu_placement=schema.menu_placement, + ) + + default_menu.children[menu_item.identifier] = menu_item + + return structure diff --git a/backend/infrahub/menu/menu.py b/backend/infrahub/menu/menu.py new file mode 100644 index 0000000000..ba7d554aa8 --- /dev/null +++ b/backend/infrahub/menu/menu.py @@ -0,0 +1,255 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from infrahub.core.constants import infrahubkind as InfrahubKind +from infrahub.core.schema import SchemaRoot, core_models + +from .constants import DEFAULT_MENU, MenuSection +from .models import MenuItemDefinition + +if TYPE_CHECKING: + from infrahub.core.schema import MainSchemaTypes + + +infrahub_schema = SchemaRoot(**core_models) + + +def _extract_node_icon(model: MainSchemaTypes) -> str: + if not model.icon: + return "" + return model.icon + + +default_menu = [ + MenuItemDefinition( + namespace="Builtin", + name=DEFAULT_MENU, + label=DEFAULT_MENU.title(), + protected=True, + section=MenuSection.OBJECT, + ), + MenuItemDefinition( + namespace="Builtin", + name="ObjectManagement", + label="Object Management", + protected=True, + section=MenuSection.INTERNAL, + order_weight=1000, + children=[ + MenuItemDefinition( + namespace="Builtin", + name="Groups", + label="Groups", + kind=InfrahubKind.GENERICGROUP, + icon=_extract_node_icon(infrahub_schema.get(InfrahubKind.GENERICGROUP)), + protected=True, + section=MenuSection.INTERNAL, + order_weight=1000, + ), + MenuItemDefinition( + namespace="Builtin", + name="Profiles", + label="Profiles", + kind=InfrahubKind.PROFILE, + icon=_extract_node_icon(infrahub_schema.get(InfrahubKind.PROFILE)), + protected=True, + section=MenuSection.INTERNAL, + order_weight=2000, + ), + MenuItemDefinition( + namespace="Builtin", + name="ResourceManager", + label="Resource Manager", + path="/resource-manager", + icon=_extract_node_icon(infrahub_schema.get(InfrahubKind.RESOURCEPOOL)), + protected=True, + section=MenuSection.INTERNAL, + order_weight=3000, + ), + ], + ), + MenuItemDefinition( + namespace="Builtin", + name="ChangeControl", + label="Change Control", + protected=True, + section=MenuSection.INTERNAL, + order_weight=2000, + children=[ + MenuItemDefinition( + namespace="Builtin", + name="Branches", + label="Branches", + path="/branche", + icon="mdi:layers-triple", + protected=True, + section=MenuSection.INTERNAL, + order_weight=1000, + ), + MenuItemDefinition( + namespace="Builtin", + name="ProposedChanges", + label="Proposed Changes", + path="/proposed-changes", + icon=_extract_node_icon(infrahub_schema.get(InfrahubKind.PROPOSEDCHANGE)), + protected=True, + section=MenuSection.INTERNAL, + order_weight=2000, + ), + MenuItemDefinition( + namespace="Builtin", + name="CheckDefinition", + label="Check Definition", + kind=InfrahubKind.CHECKDEFINITION, + icon=_extract_node_icon(infrahub_schema.get(InfrahubKind.CHECKDEFINITION)), + protected=True, + section=MenuSection.INTERNAL, + order_weight=3000, + ), + MenuItemDefinition( + namespace="Builtin", + name="Tasks", + label="Tasks", + path="/tasks", + icon="mdi:shield-check", + protected=True, + section=MenuSection.INTERNAL, + order_weight=3000, + ), + ], + ), + MenuItemDefinition( + namespace="Builtin", + name="UnifiedStorage", + label="Unified Storage", + protected=True, + section=MenuSection.INTERNAL, + order_weight=3000, + children=[ + MenuItemDefinition( + namespace="Builtin", + name="Schema", + label="Schema", + path="/schema", + icon="mdi:file-code", + protected=True, + section=MenuSection.INTERNAL, + order_weight=1000, + ), + MenuItemDefinition( + namespace="Builtin", + name="Repository", + label="Repository", + kind=InfrahubKind.GENERICREPOSITORY, + icon=_extract_node_icon(infrahub_schema.get(InfrahubKind.GENERICREPOSITORY)), + protected=True, + section=MenuSection.INTERNAL, + order_weight=2000, + ), + MenuItemDefinition( + namespace="Builtin", + name="GraphqlQuery", + label="GraphQL Query", + kind=InfrahubKind.GRAPHQLQUERY, + icon=_extract_node_icon(infrahub_schema.get(InfrahubKind.GRAPHQLQUERY)), + protected=True, + section=MenuSection.INTERNAL, + order_weight=3000, + ), + ], + ), + MenuItemDefinition( + namespace="Builtin", + name="Admin", + label="Admin", + protected=True, + section=MenuSection.INTERNAL, + order_weight=3000, + children=[ + MenuItemDefinition( + namespace="Builtin", + name="RoleManagement", + label="Role Management", + path="/role-management", + icon=_extract_node_icon(infrahub_schema.get(InfrahubKind.BASEPERMISSION)), + protected=True, + section=MenuSection.INTERNAL, + order_weight=1000, + ), + MenuItemDefinition( + namespace="Builtin", + name="Credentials", + label="Credentials", + kind=InfrahubKind.CREDENTIAL, + icon=_extract_node_icon(infrahub_schema.get(InfrahubKind.CREDENTIAL)), + protected=True, + section=MenuSection.INTERNAL, + order_weight=2000, + ), + MenuItemDefinition( + namespace="Builtin", + name="Webhooks", + label="Webhooks", + icon=_extract_node_icon(infrahub_schema.get(InfrahubKind.CUSTOMWEBHOOK)), + protected=True, + section=MenuSection.INTERNAL, + order_weight=3000, + children=[ + MenuItemDefinition( + namespace="Builtin", + name="WebhookStandard", + label="Webhook", + kind=InfrahubKind.STANDARDWEBHOOK, + icon=_extract_node_icon(infrahub_schema.get(InfrahubKind.STANDARDWEBHOOK)), + protected=True, + section=MenuSection.INTERNAL, + order_weight=1000, + ), + MenuItemDefinition( + namespace="Builtin", + name="WebhookCustom", + label="Custom Webhook", + kind=InfrahubKind.CUSTOMWEBHOOK, + icon=_extract_node_icon(infrahub_schema.get(InfrahubKind.CUSTOMWEBHOOK)), + protected=True, + section=MenuSection.INTERNAL, + order_weight=2000, + ), + ], + ), + ], + ), +] + + +# deployment = InterfaceMenu( +# title="Deployment", +# children=[ +# InterfaceMenu( +# title="Artifact", +# kind=InfrahubKind.ARTIFACT}", +# icon=_extract_node_icon(full_schema[InfrahubKind.ARTIFACT]), +# ), +# InterfaceMenu( +# title="Artifact Definition", +# kind=InfrahubKind.ARTIFACTDEFINITION}", +# icon=_extract_node_icon(full_schema[InfrahubKind.ARTIFACTDEFINITION]), +# ), +# InterfaceMenu( +# title="Generator Definition", +# kind=InfrahubKind.GENERATORDEFINITION}", +# icon=_extract_node_icon(full_schema[InfrahubKind.GENERATORDEFINITION]), +# ), +# InterfaceMenu( +# title="Generator Instance", +# kind=InfrahubKind.GENERATORINSTANCE}", +# icon=_extract_node_icon(full_schema[InfrahubKind.GENERATORINSTANCE]), +# ), +# InterfaceMenu( +# title="Transformation", +# kind=InfrahubKind.TRANSFORM}", +# icon=_extract_node_icon(full_schema[InfrahubKind.TRANSFORM]), +# ), +# ], +# ) diff --git a/backend/infrahub/menu/models.py b/backend/infrahub/menu/models.py new file mode 100644 index 0000000000..9d68cd859b --- /dev/null +++ b/backend/infrahub/menu/models.py @@ -0,0 +1,159 @@ +from __future__ import annotations + +from dataclasses import dataclass, field +from typing import TYPE_CHECKING, Self + +from pydantic import BaseModel, Field + +from infrahub.core.node import Node +from infrahub.core.protocols import CoreMenuItem +from infrahub.core.schema import GenericSchema, MainSchemaTypes, NodeSchema, ProfileSchema + +from .constants import MenuSection + +if TYPE_CHECKING: + from infrahub.database import InfrahubDatabase + + +def get_full_name(obj: CoreMenuItem | NodeSchema | GenericSchema | ProfileSchema) -> str: + if isinstance(obj, (NodeSchema, GenericSchema, ProfileSchema)): + return _get_full_name_schema(obj) + return _get_full_name_node(obj) + + +def _get_full_name_node(obj: CoreMenuItem) -> str: + return f"{obj.namespace.value}:{obj.name.value}" + + +def _get_full_name_schema(node: MainSchemaTypes) -> str: + return f"{node.namespace}:{node.name}" + + +@dataclass +class MenuDict: + data: dict[str, MenuItemDict] = field(default_factory=dict) + + def find_item(self, name: str) -> MenuItemDict | None: + return self._find_child_item(name=name, children=self.data) + + @classmethod + def _find_child_item(cls, name: str, children: dict[str, MenuItemDict]) -> MenuItemDict | None: + if name in children.keys(): + return children[name] + + for child in children.values(): + if not child.children: + continue + found = cls._find_child_item(name=name, children=child.children) + if found: + return found + return None + + def to_rest(self) -> Menu: + data: dict[str, list[MenuItemList]] = {} + + for section in [MenuSection.INTERNAL, MenuSection.OBJECT]: + item_per_section = [value.to_list() for key, value in self.data.items() if value.section == section] + data[section.value] = sorted(item_per_section, key=lambda d: d.order_weight) + + return Menu(sections=data) + + # @staticmethod + # def _sort_menu_items(items: dict[str, MenuItem]) -> dict[str, MenuItem]: + # sorted_dict = dict(sorted(items.items(), key=lambda x: (x[1].order_weight, x[0]), reverse=False)) + # return sorted_dict + + +@dataclass +class Menu: + sections: dict[str, list[MenuItemList]] = field(default_factory=dict) + + +class MenuItem(BaseModel): + identifier: str = Field(..., description="Unique identifier for this menu item") + title: str = Field(..., description="Title of the menu item") + path: str = Field(default="", description="URL endpoint if applicable") + icon: str = Field(default="", description="The icon to show for the current view") + kind: str = Field(default="", description="Kind of the model associated with this menuitem if applicable") + order_weight: int = 5000 + section: MenuSection = MenuSection.OBJECT + + @classmethod + def from_node(cls, obj: CoreMenuItem) -> Self: + return cls( + identifier=get_full_name(obj), + title=obj.label.value or "", + icon=obj.icon.value or "", + order_weight=obj.order_weight.value, + path=obj.path.value or "", + kind=obj.get_kind(), + section=obj.section.value, + ) + + @classmethod + def from_schema(cls, model: NodeSchema | GenericSchema | ProfileSchema) -> Self: + return cls( + identifier=get_full_name(model), + title=model.label or model.kind, + path=f"/objects/{model.kind}", + icon=model.icon or "", + kind=model.kind, + ) + + +class MenuItemDict(MenuItem): + children: dict[str, MenuItemDict] = Field(default_factory=dict, description="Child objects") + + def to_list(self) -> MenuItemList: + data = self.model_dump(exclude={"children"}) + unsorted_children = [child.to_list() for child in self.children.values()] + data["children"] = sorted(unsorted_children, key=lambda d: d.order_weight) + return MenuItemList(**data) + + +class MenuItemList(MenuItem): + children: list[MenuItemList] = Field(default_factory=list, description="Child objects") + + +class MenuItemDefinition(BaseModel): + namespace: str + name: str + label: str + description: str = "" + icon: str = "" + protected: bool = False + path: str = "" + kind: str = "" + section: MenuSection = MenuSection.OBJECT + order_weight: int = 2000 + children: list[MenuItemDefinition] = Field(default_factory=list) + + async def to_node(self, db: InfrahubDatabase, parent: CoreMenuItem | None = None) -> CoreMenuItem: + obj = await Node.init(db=db, schema=CoreMenuItem) + await obj.new( + db=db, + namespace=self.namespace, + name=self.name, + label=self.label, + path=self.get_path(), + description=self.description or None, + icon=self.icon or None, + protected=self.protected, + section=self.section.value, + order_weight=self.order_weight, + parent=parent.id if parent else None, + ) + return obj + + def get_path(self) -> str | None: + if self.path: + return self.path + + if self.kind: + return f"/objects/{self.kind}" + + return None + + @property + def full_name(self) -> str: + return f"{self.namespace}:{self.name}" diff --git a/backend/infrahub/message_bus/messages/schema_migration_path.py b/backend/infrahub/message_bus/messages/schema_migration_path.py index 364cc44e2e..c477b0346b 100644 --- a/backend/infrahub/message_bus/messages/schema_migration_path.py +++ b/backend/infrahub/message_bus/messages/schema_migration_path.py @@ -2,7 +2,7 @@ from typing import Optional -from pydantic import Field +from pydantic import BaseModel, Field from infrahub.core.branch import Branch # noqa: TCH001 from infrahub.core.path import SchemaPath # noqa: TCH001 @@ -12,7 +12,7 @@ ROUTING_KEY = "schema.migration.path" -class SchemaMigrationPath(InfrahubMessage): +class SchemaMigrationPathData(BaseModel): branch: Branch = Field(..., description="The name of the branch to target") migration_name: str = Field(..., description="The name of the migration to run") new_node_schema: Optional[MainSchemaTypes] = Field(None, description="new Schema of Node or Generic to process") @@ -22,6 +22,10 @@ class SchemaMigrationPath(InfrahubMessage): schema_path: SchemaPath = Field(..., description="SchemaPath to the element of the schema to migrate") +class SchemaMigrationPath(SchemaMigrationPathData, InfrahubMessage): + pass + + class SchemaMigrationPathResponseData(InfrahubResponseData): errors: list[str] = Field(default_factory=list) migration_name: Optional[str] = None diff --git a/backend/infrahub/message_bus/messages/schema_validator_path.py b/backend/infrahub/message_bus/messages/schema_validator_path.py index 277c781f36..07215e601d 100644 --- a/backend/infrahub/message_bus/messages/schema_validator_path.py +++ b/backend/infrahub/message_bus/messages/schema_validator_path.py @@ -1,6 +1,6 @@ from __future__ import annotations -from pydantic import Field +from pydantic import BaseModel, Field from infrahub.core.branch import Branch # noqa: TCH001 from infrahub.core.path import SchemaPath # noqa: TCH001 @@ -11,13 +11,17 @@ ROUTING_KEY = "schema.validator.path" -class SchemaValidatorPath(InfrahubMessage): +class SchemaValidatorPathData(BaseModel): branch: Branch = Field(..., description="The name of the branch to target") constraint_name: str = Field(..., description="The name of the constraint to validate") node_schema: MainSchemaTypes = Field(..., description="Schema of Node or Generic to validate") schema_path: SchemaPath = Field(..., description="SchemaPath to the element of the schema to validate") +class SchemaValidatorPath(SchemaValidatorPathData, InfrahubMessage): + pass + + class SchemaValidatorPathResponseData(InfrahubResponseData): violations: list[SchemaViolation] = Field(default_factory=list) constraint_name: str diff --git a/backend/infrahub/message_bus/messages/send_webhook_event.py b/backend/infrahub/message_bus/messages/send_webhook_event.py index 9ee4284ae0..d3eda2f61d 100644 --- a/backend/infrahub/message_bus/messages/send_webhook_event.py +++ b/backend/infrahub/message_bus/messages/send_webhook_event.py @@ -1,11 +1,15 @@ -from pydantic import Field +from pydantic import BaseModel, Field from infrahub.message_bus import InfrahubMessage -class SendWebhookEvent(InfrahubMessage): +class SendWebhookData(BaseModel): """Sent a webhook to an external source.""" webhook_id: str = Field(..., description="The unique ID of the webhook") event_type: str = Field(..., description="The event type") event_data: dict = Field(..., description="The data tied to the event") + + +class SendWebhookEvent(SendWebhookData, InfrahubMessage): + """Sent a webhook to an external source.""" diff --git a/backend/infrahub/message_bus/messages/transform_jinja_template.py b/backend/infrahub/message_bus/messages/transform_jinja_template.py index 96cea8da17..00e6816fcc 100644 --- a/backend/infrahub/message_bus/messages/transform_jinja_template.py +++ b/backend/infrahub/message_bus/messages/transform_jinja_template.py @@ -1,13 +1,13 @@ from typing import Optional -from pydantic import Field +from pydantic import BaseModel, Field from infrahub.message_bus import InfrahubMessage, InfrahubResponse, InfrahubResponseData ROUTING_KEY = "transform.jinja.template" -class TransformJinjaTemplate(InfrahubMessage): +class TransformJinjaTemplateData(BaseModel): """Sent to trigger the checks for a repository to be executed.""" repository_id: str = Field(..., description="The unique ID of the Repository") @@ -19,6 +19,10 @@ class TransformJinjaTemplate(InfrahubMessage): commit: str = Field(..., description="The commit id to use when rendering the template") +class TransformJinjaTemplate(TransformJinjaTemplateData, InfrahubMessage): + """Sent to trigger the checks for a repository to be executed.""" + + class TransformJinjaTemplateResponseData(InfrahubResponseData): rendered_template: Optional[str] = Field(None, description="Rendered template in string format") diff --git a/backend/infrahub/message_bus/operations/__init__.py b/backend/infrahub/message_bus/operations/__init__.py index 91a159879d..7ab85d987c 100644 --- a/backend/infrahub/message_bus/operations/__init__.py +++ b/backend/infrahub/message_bus/operations/__init__.py @@ -1,6 +1,7 @@ from typing import Optional import ujson +from prefect import Flow from infrahub.message_bus import RPCErrorResponse, messages from infrahub.message_bus.operations import ( @@ -81,12 +82,17 @@ } -async def execute_message(routing_key: str, message_body: bytes, service: InfrahubServices) -> Optional[MessageTTL]: +async def execute_message( + routing_key: str, message_body: bytes, service: InfrahubServices, skip_flow: bool = False +) -> Optional[MessageTTL]: message_data = ujson.loads(message_body) message = messages.MESSAGE_MAP[routing_key](**message_data) message.set_log_data(routing_key=routing_key) try: - await COMMAND_MAP[routing_key](message=message, service=service) + func = COMMAND_MAP[routing_key] + if skip_flow and isinstance(func, Flow): + func = func.fn + await func(message=message, service=service) except Exception as exc: # pylint: disable=broad-except if message.reply_requested: response = RPCErrorResponse(errors=[str(exc)], initial_message=message.model_dump()) diff --git a/backend/infrahub/message_bus/operations/check/artifact.py b/backend/infrahub/message_bus/operations/check/artifact.py index cd579f186d..191d6db6ca 100644 --- a/backend/infrahub/message_bus/operations/check/artifact.py +++ b/backend/infrahub/message_bus/operations/check/artifact.py @@ -1,5 +1,7 @@ from typing import Union +from prefect import flow + from infrahub.core.constants import InfrahubKind, ValidatorConclusion from infrahub.core.timestamp import Timestamp from infrahub.git.repository import InfrahubReadOnlyRepository, InfrahubRepository @@ -12,7 +14,8 @@ log = get_logger() -async def create(message: messages.CheckArtifactCreate, service: InfrahubServices): +@flow(name="git-repository-check-artifact-create") +async def create(message: messages.CheckArtifactCreate, service: InfrahubServices) -> None: log.debug("Creating artifact", message=message) validator = await service.client.get( kind=InfrahubKind.ARTIFACTVALIDATOR, id=message.validator_id, include=["checks"] diff --git a/backend/infrahub/message_bus/operations/check/generator.py b/backend/infrahub/message_bus/operations/check/generator.py index 4b2cb7b41f..5ffcffa05f 100644 --- a/backend/infrahub/message_bus/operations/check/generator.py +++ b/backend/infrahub/message_bus/operations/check/generator.py @@ -3,6 +3,7 @@ from infrahub_sdk import InfrahubNode from infrahub_sdk.exceptions import ModuleImportError from infrahub_sdk.schema import InfrahubGeneratorDefinitionConfig +from prefect import flow from infrahub import lock from infrahub.core.constants import GeneratorInstanceStatus, InfrahubKind, ValidatorConclusion @@ -16,7 +17,8 @@ # pylint: disable=duplicate-code -async def run(message: messages.CheckGeneratorRun, service: InfrahubServices): +@flow(name="git-repository-check-generator-run") +async def run(message: messages.CheckGeneratorRun, service: InfrahubServices) -> None: repository = await get_initialized_repo( repository_id=message.repository_id, name=message.repository_name, diff --git a/backend/infrahub/message_bus/operations/check/repository.py b/backend/infrahub/message_bus/operations/check/repository.py index c361f28e68..9809c6326f 100644 --- a/backend/infrahub/message_bus/operations/check/repository.py +++ b/backend/infrahub/message_bus/operations/check/repository.py @@ -1,6 +1,7 @@ from typing import List from infrahub_sdk import UUIDT +from prefect import flow from infrahub import lock from infrahub.core.constants import InfrahubKind @@ -16,7 +17,8 @@ log = get_logger() -async def check_definition(message: messages.CheckRepositoryCheckDefinition, service: InfrahubServices): +@flow(name="git-repository-check-definition") +async def check_definition(message: messages.CheckRepositoryCheckDefinition, service: InfrahubServices) -> None: definition = await service.client.get( kind=InfrahubKind.CHECKDEFINITION, id=message.check_definition_id, branch=message.branch_name ) @@ -141,7 +143,8 @@ async def check_definition(message: messages.CheckRepositoryCheckDefinition, ser await service.send(message=event) -async def merge_conflicts(message: messages.CheckRepositoryMergeConflicts, service: InfrahubServices): +@flow(name="git-repository-check-merge-conflict") +async def merge_conflicts(message: messages.CheckRepositoryMergeConflicts, service: InfrahubServices) -> None: """Runs a check to see if there are merge conflicts between two branches.""" log.info( "Checking for merge conflicts", @@ -222,7 +225,8 @@ async def merge_conflicts(message: messages.CheckRepositoryMergeConflicts, servi ) -async def user_check(message: messages.CheckRepositoryUserCheck, service: InfrahubServices): +@flow(name="git-repository-user-check") +async def user_check(message: messages.CheckRepositoryUserCheck, service: InfrahubServices) -> None: validator = await service.client.get(kind=InfrahubKind.USERVALIDATOR, id=message.validator_id) await validator.checks.fetch() diff --git a/backend/infrahub/message_bus/operations/event/branch.py b/backend/infrahub/message_bus/operations/event/branch.py index e452df653e..b683b7c3bf 100644 --- a/backend/infrahub/message_bus/operations/event/branch.py +++ b/backend/infrahub/message_bus/operations/event/branch.py @@ -1,5 +1,7 @@ from typing import List +from prefect import flow + from infrahub.core import registry from infrahub.core.diff.model.path import BranchTrackingId from infrahub.core.diff.repository.repository import DiffRepository @@ -11,6 +13,7 @@ log = get_logger() +@flow(name="event-branch-create") async def create(message: messages.EventBranchCreate, service: InfrahubServices) -> None: log.info("run_message", branch=message.branch) @@ -23,6 +26,7 @@ async def create(message: messages.EventBranchCreate, service: InfrahubServices) await service.send(message=event) +@flow(name="event-branch-delete") async def delete(message: messages.EventBranchDelete, service: InfrahubServices) -> None: log.info("Branch was deleted", branch=message.branch) @@ -36,6 +40,7 @@ async def delete(message: messages.EventBranchDelete, service: InfrahubServices) await service.send(message=event) +@flow(name="branch-event-merge") async def merge(message: messages.EventBranchMerge, service: InfrahubServices) -> None: log.info("Branch merged", source_branch=message.source_branch, target_branch=message.target_branch) @@ -64,6 +69,7 @@ async def merge(message: messages.EventBranchMerge, service: InfrahubServices) - await service.send(message=event) +@flow(name="event-branch-rebased") async def rebased(message: messages.EventBranchRebased, service: InfrahubServices) -> None: log.info("Branch rebased", branch=message.branch) diff --git a/backend/infrahub/message_bus/operations/event/node.py b/backend/infrahub/message_bus/operations/event/node.py index d123fb33cd..8f9bbc51a6 100644 --- a/backend/infrahub/message_bus/operations/event/node.py +++ b/backend/infrahub/message_bus/operations/event/node.py @@ -1,5 +1,7 @@ from typing import List +from prefect import flow + from infrahub.core.constants import InfrahubKind from infrahub.log import get_logger from infrahub.message_bus import InfrahubMessage, messages @@ -8,6 +10,7 @@ log = get_logger() +@flow(name="event-node-mutated") async def mutated( message: messages.EventNodeMutated, service: InfrahubServices, diff --git a/backend/infrahub/message_bus/operations/event/schema.py b/backend/infrahub/message_bus/operations/event/schema.py index f84918d096..4bf27075a7 100644 --- a/backend/infrahub/message_bus/operations/event/schema.py +++ b/backend/infrahub/message_bus/operations/event/schema.py @@ -1,3 +1,5 @@ +from prefect import flow + from infrahub.log import get_logger from infrahub.message_bus import messages from infrahub.services import InfrahubServices @@ -5,6 +7,7 @@ log = get_logger() +@flow(name="event-schema-update") async def update(message: messages.EventSchemaUpdate, service: InfrahubServices) -> None: log.info("run_message", branch=message.branch) diff --git a/backend/infrahub/message_bus/operations/event/worker.py b/backend/infrahub/message_bus/operations/event/worker.py index a47a22b1cd..c2f163882c 100644 --- a/backend/infrahub/message_bus/operations/event/worker.py +++ b/backend/infrahub/message_bus/operations/event/worker.py @@ -1,7 +1,10 @@ +from prefect import flow + from infrahub.message_bus import messages from infrahub.services import InfrahubServices +@flow(name="event-worker-newprimary-api") async def new_primary_api(message: messages.EventWorkerNewPrimaryAPI, service: InfrahubServices) -> None: service.log.info("api_worker promoted to primary", worker_id=message.worker_id) diff --git a/backend/infrahub/message_bus/operations/finalize/validator.py b/backend/infrahub/message_bus/operations/finalize/validator.py index 50a206fed1..5fe25f3d00 100644 --- a/backend/infrahub/message_bus/operations/finalize/validator.py +++ b/backend/infrahub/message_bus/operations/finalize/validator.py @@ -1,3 +1,5 @@ +from prefect import flow + from infrahub import config from infrahub.core.timestamp import Timestamp from infrahub.log import get_logger @@ -8,6 +10,7 @@ log = get_logger() +@flow(name="validator-finalize-execution") async def execution(message: messages.FinalizeValidatorExecution, service: InfrahubServices) -> None: """Monitors the status of checks associated with a validator and finalizes the conclusion of the validator diff --git a/backend/infrahub/message_bus/operations/git/branch.py b/backend/infrahub/message_bus/operations/git/branch.py index 2c2fc76afa..931241096d 100644 --- a/backend/infrahub/message_bus/operations/git/branch.py +++ b/backend/infrahub/message_bus/operations/git/branch.py @@ -1,3 +1,5 @@ +from prefect import flow + from infrahub import lock from infrahub.git.repository import InfrahubRepository from infrahub.log import get_logger @@ -7,6 +9,7 @@ log = get_logger() +@flow(name="git-repository-branch-create") async def create(message: messages.GitBranchCreate, service: InfrahubServices) -> None: log.info("creating branch in repository", branch=message.branch, repository=message.repository_name) repo = await InfrahubRepository.init(id=message.repository_id, name=message.repository_name, client=service.client) diff --git a/backend/infrahub/message_bus/operations/git/diff.py b/backend/infrahub/message_bus/operations/git/diff.py index 2b7d4678bb..8fada660ae 100644 --- a/backend/infrahub/message_bus/operations/git/diff.py +++ b/backend/infrahub/message_bus/operations/git/diff.py @@ -1,3 +1,5 @@ +from prefect import flow + from infrahub.git.repository import get_initialized_repo from infrahub.log import get_logger from infrahub.message_bus import messages @@ -7,6 +9,7 @@ log = get_logger() +@flow(name="git-repository-diff-files-names-only") async def names_only(message: messages.GitDiffNamesOnly, service: InfrahubServices) -> None: log.info( "Collecting modifications between commits", diff --git a/backend/infrahub/message_bus/operations/git/file.py b/backend/infrahub/message_bus/operations/git/file.py index 9594aa3e1d..4176b1cb8e 100644 --- a/backend/infrahub/message_bus/operations/git/file.py +++ b/backend/infrahub/message_bus/operations/git/file.py @@ -1,3 +1,5 @@ +from prefect import flow + from infrahub.git.repository import get_initialized_repo from infrahub.log import get_logger from infrahub.message_bus import messages @@ -7,6 +9,7 @@ log = get_logger() +@flow(name="git-repository-get-file") async def get(message: messages.GitFileGet, service: InfrahubServices) -> None: log.info("Collecting file from repository", repository=message.repository_name, file=message.file) diff --git a/backend/infrahub/message_bus/operations/git/repository.py b/backend/infrahub/message_bus/operations/git/repository.py index ed8b36c561..b888b1087a 100644 --- a/backend/infrahub/message_bus/operations/git/repository.py +++ b/backend/infrahub/message_bus/operations/git/repository.py @@ -1,3 +1,5 @@ +from prefect import flow + from infrahub import lock from infrahub.core.constants import InfrahubKind, RepositoryInternalStatus from infrahub.exceptions import RepositoryError @@ -13,6 +15,7 @@ log = get_logger() +@flow(name="git-repository-add-read-write") async def add(message: messages.GitRepositoryAdd, service: InfrahubServices) -> None: log.info( "Cloning and importing repository", @@ -43,6 +46,7 @@ async def add(message: messages.GitRepositoryAdd, service: InfrahubServices) -> await repo.sync() +@flow(name="git-repository-add-read-only") async def add_read_only(message: messages.GitRepositoryAddReadOnly, service: InfrahubServices) -> None: log.info( "Cloning and importing read-only repository", repository=message.repository_name, location=message.location @@ -67,6 +71,7 @@ async def add_read_only(message: messages.GitRepositoryAddReadOnly, service: Inf await repo.sync_from_remote() +@flow(name="git-repository-check-connectivity") async def connectivity(message: messages.GitRepositoryConnectivity, service: InfrahubServices) -> None: response_data = GitRepositoryConnectivityResponseData(message="Successfully accessed repository", success=True) @@ -83,6 +88,7 @@ async def connectivity(message: messages.GitRepositoryConnectivity, service: Inf await service.reply(message=response, initiator=message) +@flow(name="git-repository-import-object") async def import_objects(message: messages.GitRepositoryImportObjects, service: InfrahubServices) -> None: async with service.git_report( related_node=message.repository_id, @@ -98,6 +104,7 @@ async def import_objects(message: messages.GitRepositoryImportObjects, service: await repo.import_objects_from_files(infrahub_branch_name=message.infrahub_branch_name, commit=message.commit) +@flow(name="git-repository-pull-read-only") async def pull_read_only(message: messages.GitRepositoryPullReadOnly, service: InfrahubServices) -> None: if not message.ref and not message.commit: log.warning( @@ -148,6 +155,7 @@ async def pull_read_only(message: messages.GitRepositoryPullReadOnly, service: I await repo.sync_from_remote(commit=message.commit) +@flow(name="git-repository-merge") async def merge(message: messages.GitRepositoryMerge, service: InfrahubServices) -> None: log.info( "Merging repository branch", diff --git a/backend/infrahub/message_bus/operations/refresh/webhook.py b/backend/infrahub/message_bus/operations/refresh/webhook.py index 8d07a6e79d..abc8fe6f44 100644 --- a/backend/infrahub/message_bus/operations/refresh/webhook.py +++ b/backend/infrahub/message_bus/operations/refresh/webhook.py @@ -1,10 +1,12 @@ import ujson +from prefect import flow from infrahub.core.constants import InfrahubKind from infrahub.message_bus import messages from infrahub.services import InfrahubServices +@flow(name="registry-webhook-config-refresh") async def configuration( message: messages.RefreshWebhookConfiguration, # pylint: disable=unused-argument service: InfrahubServices, diff --git a/backend/infrahub/message_bus/operations/requests/artifact.py b/backend/infrahub/message_bus/operations/requests/artifact.py index 6764978b76..eb04019d0b 100644 --- a/backend/infrahub/message_bus/operations/requests/artifact.py +++ b/backend/infrahub/message_bus/operations/requests/artifact.py @@ -1,3 +1,5 @@ +from prefect import flow + from infrahub.git.repository import get_initialized_repo from infrahub.log import get_logger from infrahub.message_bus import messages @@ -7,7 +9,8 @@ log = get_logger() -async def generate(message: messages.RequestArtifactGenerate, service: InfrahubServices): +@flow(name="artifact-generate") +async def generate(message: messages.RequestArtifactGenerate, service: InfrahubServices) -> None: log.debug("Generating artifact", message=message) repo = await get_initialized_repo( diff --git a/backend/infrahub/message_bus/operations/requests/artifact_definition.py b/backend/infrahub/message_bus/operations/requests/artifact_definition.py index 2020d6f9d1..33d1552855 100644 --- a/backend/infrahub/message_bus/operations/requests/artifact_definition.py +++ b/backend/infrahub/message_bus/operations/requests/artifact_definition.py @@ -1,6 +1,7 @@ from typing import Optional from infrahub_sdk import UUIDT +from prefect import flow from infrahub.core.constants import InfrahubKind, ValidatorConclusion, ValidatorState from infrahub.core.timestamp import Timestamp @@ -12,6 +13,7 @@ log = get_logger() +@flow(name="artifact-definition-check") async def check(message: messages.RequestArtifactDefinitionCheck, service: InfrahubServices) -> None: async with service.task_report( title=f"Artifact Definition: {message.artifact_definition.definition_name}", @@ -134,6 +136,7 @@ async def check(message: messages.RequestArtifactDefinitionCheck, service: Infra await service.send(message=event) +@flow(name="artifact-definition-generate") async def generate(message: messages.RequestArtifactDefinitionGenerate, service: InfrahubServices) -> None: log.info( "Received request to generate artifacts for an artifact_definition", diff --git a/backend/infrahub/message_bus/operations/requests/diff.py b/backend/infrahub/message_bus/operations/requests/diff.py index 479af29511..f15f016eb2 100644 --- a/backend/infrahub/message_bus/operations/requests/diff.py +++ b/backend/infrahub/message_bus/operations/requests/diff.py @@ -1,3 +1,5 @@ +from prefect import flow + from infrahub.core import registry from infrahub.core.diff.coordinator import DiffCoordinator from infrahub.dependencies.registry import get_component_registry @@ -8,6 +10,7 @@ log = get_logger() +@flow(name="diff-update") async def update(message: messages.RequestDiffUpdate, service: InfrahubServices) -> None: component_registry = get_component_registry() base_branch = await registry.get_branch(db=service.database, branch=registry.default_branch) @@ -24,6 +27,7 @@ async def update(message: messages.RequestDiffUpdate, service: InfrahubServices) ) +@flow(name="diff-refresh") async def refresh(message: messages.RequestDiffRefresh, service: InfrahubServices) -> None: component_registry = get_component_registry() base_branch = await registry.get_branch(db=service.database, branch=registry.default_branch) diff --git a/backend/infrahub/message_bus/operations/requests/generator.py b/backend/infrahub/message_bus/operations/requests/generator.py index 1b62104332..130412f3ce 100644 --- a/backend/infrahub/message_bus/operations/requests/generator.py +++ b/backend/infrahub/message_bus/operations/requests/generator.py @@ -4,15 +4,17 @@ from infrahub_sdk.exceptions import ModuleImportError from infrahub_sdk.protocols import CoreGeneratorInstance from infrahub_sdk.schema import InfrahubGeneratorDefinitionConfig +from prefect import flow from infrahub import lock -from infrahub.core.constants import GeneratorInstanceStatus, InfrahubKind +from infrahub.core.constants import GeneratorInstanceStatus from infrahub.git.base import extract_repo_file_information from infrahub.git.repository import get_initialized_repo from infrahub.message_bus import messages from infrahub.services import InfrahubServices +@flow(name="generator-run") async def run(message: messages.RequestGeneratorRun, service: InfrahubServices) -> None: repository = await get_initialized_repo( repository_id=message.repository_id, @@ -65,8 +67,8 @@ async def run(message: messages.RequestGeneratorRun, service: InfrahubServices) async def _define_instance(message: messages.RequestGeneratorRun, service: InfrahubServices) -> CoreGeneratorInstance: if message.generator_instance: - instance: CoreGeneratorInstance = await service.client.get( - kind=InfrahubKind.GENERATORINSTANCE, id=message.generator_instance, branch=message.branch_name + instance = await service.client.get( + kind=CoreGeneratorInstance, id=message.generator_instance, branch=message.branch_name ) instance.status.value = GeneratorInstanceStatus.PENDING.value await instance.update(do_full_update=True) @@ -75,8 +77,8 @@ async def _define_instance(message: messages.RequestGeneratorRun, service: Infra async with lock.registry.get( f"{message.target_id}-{message.generator_definition.definition_id}", namespace="generator" ): - instances: list[CoreGeneratorInstance] = await service.client.filters( - kind=InfrahubKind.GENERATORINSTANCE, + instances = await service.client.filters( + kind=CoreGeneratorInstance, definition__ids=[message.generator_definition.definition_id], object__ids=[message.target_id], branch=message.branch_name, @@ -87,7 +89,7 @@ async def _define_instance(message: messages.RequestGeneratorRun, service: Infra await instance.update(do_full_update=True) else: instance = await service.client.create( - kind=InfrahubKind.GENERATORINSTANCE, + kind=CoreGeneratorInstance, branch=message.branch_name, data={ "name": f"{message.generator_definition.definition_name}: {message.target_name}", diff --git a/backend/infrahub/message_bus/operations/requests/generator_definition.py b/backend/infrahub/message_bus/operations/requests/generator_definition.py index 92c03e839e..7792bcaa48 100644 --- a/backend/infrahub/message_bus/operations/requests/generator_definition.py +++ b/backend/infrahub/message_bus/operations/requests/generator_definition.py @@ -1,6 +1,7 @@ from typing import Optional from infrahub_sdk import UUIDT +from prefect import flow from infrahub.core.constants import InfrahubKind, ValidatorConclusion, ValidatorState from infrahub.core.timestamp import Timestamp @@ -9,6 +10,7 @@ from infrahub.services import InfrahubServices +@flow(name="generator-definition-check") async def check(message: messages.RequestGeneratorDefinitionCheck, service: InfrahubServices) -> None: async with service.task_report( title=f"Generator Definition: {message.generator_definition.definition_name}", @@ -127,6 +129,7 @@ async def check(message: messages.RequestGeneratorDefinitionCheck, service: Infr await service.send(message=event) +@flow(name="generator-definition-run") async def run(message: messages.RequestGeneratorDefinitionRun, service: InfrahubServices) -> None: async with service.task_report( title="Executing Generator", diff --git a/backend/infrahub/message_bus/operations/requests/git.py b/backend/infrahub/message_bus/operations/requests/git.py index f388bf09d5..d1d20bf86b 100644 --- a/backend/infrahub/message_bus/operations/requests/git.py +++ b/backend/infrahub/message_bus/operations/requests/git.py @@ -1,5 +1,7 @@ from typing import List +from prefect import flow + from infrahub.core.constants import InfrahubKind from infrahub.git.actions import sync_remote_repositories from infrahub.log import get_logger @@ -9,6 +11,7 @@ log = get_logger() +@flow(name="git-repository-branch-create") async def create_branch(message: messages.RequestGitCreateBranch, service: InfrahubServices) -> None: """Request to the creation of git branches in available repositories.""" log.info("Querying repositories for branch creation") @@ -28,6 +31,7 @@ async def create_branch(message: messages.RequestGitCreateBranch, service: Infra await service.send(message=event) +@flow(name="git-repository-sync") async def sync( message: messages.RequestGitSync, # pylint: disable=unused-argument service: InfrahubServices, diff --git a/backend/infrahub/message_bus/operations/requests/graphql_query_group.py b/backend/infrahub/message_bus/operations/requests/graphql_query_group.py index 3417c549c0..da8a906572 100644 --- a/backend/infrahub/message_bus/operations/requests/graphql_query_group.py +++ b/backend/infrahub/message_bus/operations/requests/graphql_query_group.py @@ -2,6 +2,7 @@ from infrahub_sdk import InfrahubClient, InfrahubNode from infrahub_sdk.utils import dict_hash +from prefect import flow from infrahub.core.constants import InfrahubKind from infrahub.log import get_logger @@ -11,7 +12,9 @@ log = get_logger() -async def group_add_subscriber(client: InfrahubClient, group: InfrahubNode, subscribers: List[str], branch: str): +async def group_add_subscriber( + client: InfrahubClient, group: InfrahubNode, subscribers: List[str], branch: str +) -> dict: subscribers_str = ["{ id: " + f'"{subscriber}"' + " }" for subscriber in subscribers] query = """ mutation { @@ -33,6 +36,7 @@ async def group_add_subscriber(client: InfrahubClient, group: InfrahubNode, subs return await client.execute_graphql(query=query, branch_name=branch, tracker="mutation-relationshipadd") +@flow(name="graphql-query-update") async def update(message: messages.RequestGraphQLQueryGroupUpdate, service: InfrahubServices) -> None: """Create or Update a GraphQLQueryGroup.""" diff --git a/backend/infrahub/message_bus/operations/requests/proposed_change.py b/backend/infrahub/message_bus/operations/requests/proposed_change.py index 22158775a2..ddff626ced 100644 --- a/backend/infrahub/message_bus/operations/requests/proposed_change.py +++ b/backend/infrahub/message_bus/operations/requests/proposed_change.py @@ -8,6 +8,8 @@ from typing import TYPE_CHECKING, Union import pytest +from infrahub_sdk.protocols import CoreGeneratorDefinition, CoreProposedChange +from prefect import flow from pydantic import BaseModel from infrahub import config, lock @@ -30,14 +32,13 @@ ProposedChangeSubscriber, ) from infrahub.pytest_plugin import InfrahubBackendPlugin +from infrahub.services import InfrahubServices # noqa: TCH001 if TYPE_CHECKING: from infrahub_sdk.node import InfrahubNode - from infrahub_sdk.protocols import CoreGeneratorDefinition, CoreProposedChange from infrahub.core.models import SchemaUpdateConstraintInfo - from infrahub.core.schema_manager import SchemaBranch - from infrahub.services import InfrahubServices + from infrahub.core.schema.schema_branch import SchemaBranch log = get_logger() @@ -69,6 +70,7 @@ def log_line(self) -> str: return "Doesn't require changes due to no relevant modified kinds or file changes in Git" +@flow(name="proposed-changed-cancel") async def cancel(message: messages.RequestProposedChangeCancel, service: InfrahubServices) -> None: """Cancel a proposed change.""" async with service.task_report( @@ -76,13 +78,12 @@ async def cancel(message: messages.RequestProposedChangeCancel, service: Infrahu title="Canceling proposed change", ) as task_report: await task_report.info("Canceling proposed change as the source branch was deleted", id=message.proposed_change) - proposed_change: CoreProposedChange = await service.client.get( - kind=InfrahubKind.PROPOSEDCHANGE, id=message.proposed_change - ) + proposed_change = await service.client.get(kind=CoreProposedChange, id=message.proposed_change) proposed_change.state.value = ProposedChangeState.CANCELED.value await proposed_change.save() +@flow(name="proposed-changed-data-integrity") async def data_integrity(message: messages.RequestProposedChangeDataIntegrity, service: InfrahubServices) -> None: """Triggers a data integrity validation check on the provided proposed change to start.""" async with service.task_report( @@ -99,6 +100,7 @@ async def data_integrity(message: messages.RequestProposedChangeDataIntegrity, s await diff_coordinator.update_branch_diff(base_branch=destination_branch, diff_branch=source_branch) +@flow(name="proposed-changed-pipeline") async def pipeline(message: messages.RequestProposedChangePipeline, service: InfrahubServices) -> None: async with service.task_report( related_node=message.proposed_change, @@ -221,6 +223,7 @@ async def pipeline(message: messages.RequestProposedChangePipeline, service: Inf await service.send(message=event) +@flow(name="proposed-changed-schema-integrity") async def schema_integrity( message: messages.RequestProposedChangeSchemaIntegrity, service: InfrahubServices, # pylint: disable=unused-argument @@ -288,6 +291,7 @@ async def schema_integrity( ) +@flow(name="proposed-changed-repository-check") async def repository_checks(message: messages.RequestProposedChangeRepositoryChecks, service: InfrahubServices) -> None: async with service.task_report( related_node=message.proposed_change, @@ -328,6 +332,7 @@ async def repository_checks(message: messages.RequestProposedChangeRepositoryChe await service.send(message=event) +@flow(name="proposed-changed-refresh-artifact") async def refresh_artifacts(message: messages.RequestProposedChangeRefreshArtifacts, service: InfrahubServices) -> None: async with service.task_report( related_node=message.proposed_change, @@ -391,6 +396,7 @@ async def refresh_artifacts(message: messages.RequestProposedChangeRefreshArtifa await service.send(message=msg) +@flow(name="proposed-changed-run-generator") async def run_generators(message: messages.RequestProposedChangeRunGenerators, service: InfrahubServices) -> None: async with service.task_report( related_node=message.proposed_change, @@ -557,6 +563,7 @@ async def run_generators(message: messages.RequestProposedChangeRunGenerators, s """ +@flow(name="proposed-changed-run-tests") async def run_tests(message: messages.RequestProposedChangeRunTests, service: InfrahubServices) -> None: async with service.task_report( related_node=message.proposed_change, diff --git a/backend/infrahub/message_bus/operations/requests/repository.py b/backend/infrahub/message_bus/operations/requests/repository.py index 872196a2f8..1646edf0e0 100644 --- a/backend/infrahub/message_bus/operations/requests/repository.py +++ b/backend/infrahub/message_bus/operations/requests/repository.py @@ -1,6 +1,7 @@ from typing import List from infrahub_sdk import UUIDT +from prefect import flow from infrahub.core.constants import InfrahubKind from infrahub.core.timestamp import Timestamp @@ -12,7 +13,8 @@ log = get_logger() -async def checks(message: messages.RequestRepositoryChecks, service: InfrahubServices): +@flow(name="repository-check") +async def checks(message: messages.RequestRepositoryChecks, service: InfrahubServices) -> None: """Request to start validation checks on a specific repository.""" log.info("Running repository checks", repository_id=message.repository, proposed_change_id=message.proposed_change) @@ -94,7 +96,8 @@ async def checks(message: messages.RequestRepositoryChecks, service: InfrahubSer await service.send(message=event) -async def user_checks(message: messages.RequestRepositoryUserChecks, service: InfrahubServices): +@flow(name="repository-users-check") +async def user_checks(message: messages.RequestRepositoryUserChecks, service: InfrahubServices) -> None: """Request to start validation checks on a specific repository for User-defined checks.""" async with service.task_report( related_node=message.proposed_change, diff --git a/backend/infrahub/message_bus/operations/schema/migration.py b/backend/infrahub/message_bus/operations/schema/migration.py index 9de2f67424..9adf96b324 100644 --- a/backend/infrahub/message_bus/operations/schema/migration.py +++ b/backend/infrahub/message_bus/operations/schema/migration.py @@ -1,15 +1,21 @@ +from prefect import flow, task +from prefect.logging import get_run_logger +from prefect.runtime import task_run + from infrahub.core.migrations import MIGRATION_MAP from infrahub.log import get_logger from infrahub.message_bus.messages.schema_migration_path import ( SchemaMigrationPath, + SchemaMigrationPathData, SchemaMigrationPathResponse, SchemaMigrationPathResponseData, ) -from infrahub.services import InfrahubServices +from infrahub.services import InfrahubServices, services log = get_logger() +@flow(name="schema-migration-path") async def path(message: SchemaMigrationPath, service: InfrahubServices) -> None: async with service.database.start_session() as db: node_kind = None @@ -52,3 +58,60 @@ async def path(message: SchemaMigrationPath, service: InfrahubServices) -> None: ) ) await service.reply(message=response, initiator=message) + + +def generate_task_name() -> str: + task_name = task_run.task_name + message: SchemaMigrationPathData = task_run.parameters["message"] + return f"{task_name}-{message.branch.name}-{message.migration_name}" + + +@task( + task_run_name=generate_task_name, + description="Apply a given migration to the database", + retries=3, +) +async def schema_path_migrate(message: SchemaMigrationPathData) -> SchemaMigrationPathResponseData: + service = services.service + logger = get_run_logger() + + async with service.database.start_session() as db: + node_kind = None + if message.new_node_schema: + node_kind = message.new_node_schema.kind + elif message.previous_node_schema: + node_kind = message.previous_node_schema.kind + + service.log.info( + "schema.migration.path - received", + migration=message.migration_name, + node_kind=node_kind, + path=message.schema_path.get_path(), + ) + migration_class = MIGRATION_MAP.get(message.migration_name) + if not migration_class: + raise ValueError(f"Unable to find the migration class for {message.migration_name}") + + migration = migration_class( + new_node_schema=message.new_node_schema, + previous_node_schema=message.previous_node_schema, + schema_path=message.schema_path, + ) + execution_result = await migration.execute(db=db, branch=message.branch) + + logger.info(f"Migration completed for {message.migration_name}") + + service.log.info( + "schema.migration.path - completed", + migration=message.migration_name, + node_kind=node_kind, + path=message.schema_path.get_path(), + result=execution_result, + ) + + return SchemaMigrationPathResponseData( + migration_name=message.migration_name, + schema_path=message.schema_path, + errors=execution_result.errors, + nbr_migrations_executed=execution_result.nbr_migrations_executed, + ) diff --git a/backend/infrahub/message_bus/operations/schema/validator.py b/backend/infrahub/message_bus/operations/schema/validator.py index 3e2ed75b2d..02ee3983be 100644 --- a/backend/infrahub/message_bus/operations/schema/validator.py +++ b/backend/infrahub/message_bus/operations/schema/validator.py @@ -1,17 +1,22 @@ +from prefect import flow, task +from prefect.runtime import task_run + from infrahub.core.validators.aggregated_checker import AggregatedConstraintChecker from infrahub.core.validators.model import SchemaConstraintValidatorRequest from infrahub.dependencies.registry import get_component_registry from infrahub.log import get_logger from infrahub.message_bus.messages.schema_validator_path import ( SchemaValidatorPath, + SchemaValidatorPathData, SchemaValidatorPathResponse, SchemaValidatorPathResponseData, ) -from infrahub.services import InfrahubServices +from infrahub.services import InfrahubServices, services log = get_logger() +@flow(name="schema-validator-path") async def path(message: SchemaValidatorPath, service: InfrahubServices) -> None: async with service.database.start_session() as db: log.info( @@ -41,3 +46,43 @@ async def path(message: SchemaValidatorPath, service: InfrahubServices) -> None: ) ) await service.reply(message=response, initiator=message) + + +def generate_task_name() -> str: + task_name = task_run.task_name + message: SchemaValidatorPathData = task_run.parameters["message"] + return f"{task_name}-{message.branch.name}-{message.constraint_name}" + + +@task( + task_run_name=generate_task_name, + description="Validate if a given migration is compatible with the existing data", + retries=3, +) +async def schema_path_validate(message: SchemaValidatorPathData) -> SchemaValidatorPathResponseData: + service = services.service + + async with service.database.start_session() as db: + log.info( + "schema.validator.path", + constraint=message.constraint_name, + node_kind=message.node_schema.kind, + path=message.schema_path.get_path(), + ) + + constraint_request = SchemaConstraintValidatorRequest( + branch=message.branch, + constraint_name=message.constraint_name, + node_schema=message.node_schema, + schema_path=message.schema_path, + ) + + component_registry = get_component_registry() + aggregated_constraint_checker = await component_registry.get_component( + AggregatedConstraintChecker, db=db, branch=message.branch + ) + violations = await aggregated_constraint_checker.run_constraints(constraint_request) + + return SchemaValidatorPathResponseData( + violations=violations, constraint_name=message.constraint_name, schema_path=message.schema_path + ) diff --git a/backend/infrahub/message_bus/operations/send/echo.py b/backend/infrahub/message_bus/operations/send/echo.py index f6c3629fc6..e0dce262c3 100644 --- a/backend/infrahub/message_bus/operations/send/echo.py +++ b/backend/infrahub/message_bus/operations/send/echo.py @@ -1,8 +1,11 @@ +from prefect import flow + from infrahub.message_bus import messages from infrahub.message_bus.messages.send_echo_request import SendEchoRequestResponse, SendEchoRequestResponseData from infrahub.services import InfrahubServices +@flow(name="echo-request") async def request(message: messages.SendEchoRequest, service: InfrahubServices) -> None: service.log.info(f"Received message: {message.message}") if message.reply_requested: diff --git a/backend/infrahub/message_bus/operations/send/telemetry.py b/backend/infrahub/message_bus/operations/send/telemetry.py index ad4ccc9997..4e6a36fa48 100644 --- a/backend/infrahub/message_bus/operations/send/telemetry.py +++ b/backend/infrahub/message_bus/operations/send/telemetry.py @@ -2,23 +2,27 @@ import json import platform import time +from typing import Any -import httpx +from prefect import flow, task +from prefect.logging import get_run_logger from infrahub import __version__, config from infrahub.core import registry, utils from infrahub.core.branch import Branch from infrahub.core.constants import InfrahubKind from infrahub.core.graph.schema import GRAPH_SCHEMA +from infrahub.exceptions import HTTPServerError from infrahub.message_bus import messages -from infrahub.services import InfrahubServices +from infrahub.services import InfrahubServices, services TELEMETRY_KIND: str = "community" TELEMETRY_VERSION: str = "20240524" +@task async def gather_database_information(service: InfrahubServices, branch: Branch) -> dict: # pylint: disable=unused-argument - data = { + data: dict[str, Any] = { "database_type": service.database.db_type.value, "relationship_count": {"total": await utils.count_relationships(db=service.database)}, "node_count": {"total": await utils.count_nodes(db=service.database)}, @@ -33,8 +37,9 @@ async def gather_database_information(service: InfrahubServices, branch: Branch) return data +@task async def gather_schema_information(service: InfrahubServices, branch: Branch) -> dict: # pylint: disable=unused-argument - data = {} + data: dict[str, Any] = {} main_schema = registry.schema.get_schema_branch(name=branch.name) data["node_count"] = len(main_schema.node_names) data["generic_count"] = len(main_schema.generic_names) @@ -43,6 +48,7 @@ async def gather_schema_information(service: InfrahubServices, branch: Branch) - return data +@task async def gather_feature_information(service: InfrahubServices, branch: Branch) -> dict: # pylint: disable=unused-argument data = {} features_to_count = [ @@ -60,13 +66,14 @@ async def gather_feature_information(service: InfrahubServices, branch: Branch) return data +@task async def gather_anonymous_telemetry_data(service: InfrahubServices) -> dict: start_time = time.time() default_branch = registry.get_branch_from_registry() workers = await service.component.list_workers(branch=default_branch.name, schema_hash=False) - data = { + data: dict[str, Any] = { "deployment_id": registry.id, "execution_time": None, "infrahub_version": __version__, @@ -89,6 +96,7 @@ async def gather_anonymous_telemetry_data(service: InfrahubServices) -> dict: return data +@flow(name="telemetry-push-legacy") async def push( message: messages.SendTelemetryPush, # pylint: disable=unused-argument service: InfrahubServices, @@ -98,6 +106,37 @@ async def push( data = await gather_anonymous_telemetry_data(service=service) service.log.debug(f"Anonymous usage telemetry gathered in {data['execution_time']} seconds.") + payload = { + "kind": TELEMETRY_KIND, + "payload_format": TELEMETRY_VERSION, + "data": data, + "checksum": hashlib.sha256(json.dumps(data).encode()).hexdigest(), + } + try: + response = await service.http.post(url=config.SETTINGS.main.telemetry_endpoint, json=payload) + except HTTPServerError as exc: + service.log.debug(f"HTTP exception while pushing anonymous telemetry: {exc}") + if not response.is_success: + service.log.debug("HTTP exception while pushing anonymous telemetry", status_code=response.status_code) + + +@task(retries=5) +async def post_telemetry_data(service: InfrahubServices, url: str, payload: dict[str, Any]) -> None: + """Send the telemetry data to the specified URL, using HTTP POST.""" + response = await service.http.post(url=url, json=payload) + response.raise_for_status() + + +@flow +async def send_telemetry_push() -> None: + service = services.service + + log = get_run_logger() + log.info(f"Pushing anonymous telemetry data to {config.SETTINGS.main.telemetry_endpoint}...") + + data = await gather_anonymous_telemetry_data(service=service) + log.info(f"Anonymous usage telemetry gathered in {data['execution_time']} seconds. | {data}") + payload = { "kind": TELEMETRY_KIND, "payload_format": TELEMETRY_VERSION, @@ -105,9 +144,4 @@ async def push( "checksum": hashlib.sha256(json.dumps(data).encode()).hexdigest(), } - async with httpx.AsyncClient(timeout=5.0) as client: - try: - response = await client.post(config.SETTINGS.main.telemetry_endpoint, json=payload) - response.raise_for_status() - except httpx.HTTPError as exc: - service.log.debug(f"HTTP exception while pushing anonymous telemetry: {exc}") + await post_telemetry_data(service=service, url=config.SETTINGS.main.telemetry_endpoint, payload=payload) diff --git a/backend/infrahub/message_bus/operations/send/webhook.py b/backend/infrahub/message_bus/operations/send/webhook.py index 5b1df4cc76..4110d78fac 100644 --- a/backend/infrahub/message_bus/operations/send/webhook.py +++ b/backend/infrahub/message_bus/operations/send/webhook.py @@ -1,11 +1,17 @@ +from typing import Any + import ujson +from prefect import flow +from prefect.logging import get_run_logger from infrahub.exceptions import NodeNotFoundError from infrahub.message_bus import messages -from infrahub.services import InfrahubServices +from infrahub.message_bus.messages.send_webhook_event import SendWebhookData +from infrahub.services import InfrahubServices, services from infrahub.webhook import CustomWebhook, StandardWebhook, TransformWebhook, Webhook +@flow(name="event-send-webhook") async def event(message: messages.SendWebhookEvent, service: InfrahubServices) -> None: async with service.task_report( related_node=message.webhook_id, @@ -19,7 +25,7 @@ async def event(message: messages.SendWebhookEvent, service: InfrahubServices) - ) webhook_data = ujson.loads(webhook_definition) - payload = {"event_type": message.event_type, "data": message.event_data, "service": service} + payload: dict[str, Any] = {"event_type": message.event_type, "data": message.event_data, "service": service} webhook_map: dict[str, type[Webhook]] = { "standard": StandardWebhook, "custom": CustomWebhook, @@ -33,3 +39,30 @@ async def event(message: messages.SendWebhookEvent, service: InfrahubServices) - title=webhook.webhook_type, logs={"message": "Successfully sent webhook", "severity": "INFO"}, ) + + +@flow +async def send_webhook(message: SendWebhookData) -> None: + service = services.service + log = get_run_logger() + + webhook_definition = await service.cache.get(key=f"webhook:active:{message.webhook_id}") + if not webhook_definition: + log.warning("Webhook not found") + raise NodeNotFoundError( + node_type="Webhook", identifier=message.webhook_id, message="The requested Webhook was not found" + ) + + webhook_data = ujson.loads(webhook_definition) + payload: dict[str, Any] = {"event_type": message.event_type, "data": message.event_data, "service": service} + webhook_map: dict[str, type[Webhook]] = { + "standard": StandardWebhook, + "custom": CustomWebhook, + "transform": TransformWebhook, + } + webhook_class = webhook_map[webhook_data["webhook_type"]] + payload.update(webhook_data["webhook_configuration"]) + webhook = webhook_class(**payload) + await webhook.send() + + log.info("Successfully sent webhook") diff --git a/backend/infrahub/message_bus/operations/transform/jinja.py b/backend/infrahub/message_bus/operations/transform/jinja.py index a861ea5946..6661474484 100644 --- a/backend/infrahub/message_bus/operations/transform/jinja.py +++ b/backend/infrahub/message_bus/operations/transform/jinja.py @@ -1,15 +1,19 @@ +from prefect import flow + from infrahub.git.repository import get_initialized_repo from infrahub.log import get_logger from infrahub.message_bus.messages.transform_jinja_template import ( TransformJinjaTemplate, + TransformJinjaTemplateData, TransformJinjaTemplateResponse, TransformJinjaTemplateResponseData, ) -from infrahub.services import InfrahubServices +from infrahub.services import InfrahubServices, services log = get_logger() +@flow(name="transform-render-jinja2-legacy") async def template(message: TransformJinjaTemplate, service: InfrahubServices) -> None: log.info(f"Received request to render a Jinja template on branch={message.branch}") @@ -28,3 +32,20 @@ async def template(message: TransformJinjaTemplate, service: InfrahubServices) - data=TransformJinjaTemplateResponseData(rendered_template=rendered_template), ) await service.reply(message=response, initiator=message) + + +@flow(persist_result=True) +async def transform_render_jinja2_template(message: TransformJinjaTemplateData) -> str: + service = services.service + repo = await get_initialized_repo( + repository_id=message.repository_id, + name=message.repository_name, + service=service, + repository_kind=message.repository_kind, + ) + + rendered_template = await repo.render_jinja2_template( + commit=message.commit, location=message.template_location, data={"data": message.data} + ) + + return rendered_template diff --git a/backend/infrahub/message_bus/operations/transform/python.py b/backend/infrahub/message_bus/operations/transform/python.py index a93a33c1f9..8023267cc9 100644 --- a/backend/infrahub/message_bus/operations/transform/python.py +++ b/backend/infrahub/message_bus/operations/transform/python.py @@ -1,3 +1,5 @@ +from prefect import flow + from infrahub.git.repository import get_initialized_repo from infrahub.log import get_logger from infrahub.message_bus import messages @@ -10,6 +12,7 @@ log = get_logger() +@flow(name="transform-render-python-legacy") async def data(message: messages.TransformPythonData, service: InfrahubServices) -> None: log.info( "Received request to transform Python data", diff --git a/backend/infrahub/message_bus/operations/trigger/artifact_definition.py b/backend/infrahub/message_bus/operations/trigger/artifact_definition.py index 27013e25ff..1b614f287c 100644 --- a/backend/infrahub/message_bus/operations/trigger/artifact_definition.py +++ b/backend/infrahub/message_bus/operations/trigger/artifact_definition.py @@ -1,3 +1,5 @@ +from prefect import flow + from infrahub.core.constants import InfrahubKind from infrahub.log import get_logger from infrahub.message_bus import messages @@ -6,6 +8,7 @@ log = get_logger() +@flow(name="artifact-definition-generate") async def generate(message: messages.TriggerArtifactDefinitionGenerate, service: InfrahubServices) -> None: artifact_definitions = await service.client.all( kind=InfrahubKind.ARTIFACTDEFINITION, branch=message.branch, include=["id"] diff --git a/backend/infrahub/message_bus/operations/trigger/generator_definition.py b/backend/infrahub/message_bus/operations/trigger/generator_definition.py index c9825add03..b9044806ab 100644 --- a/backend/infrahub/message_bus/operations/trigger/generator_definition.py +++ b/backend/infrahub/message_bus/operations/trigger/generator_definition.py @@ -1,9 +1,12 @@ +from prefect import flow + from infrahub.core.constants import InfrahubKind from infrahub.message_bus import messages from infrahub.message_bus.types import ProposedChangeGeneratorDefinition from infrahub.services import InfrahubServices +@flow(name="generator-definition-run") async def run(message: messages.TriggerGeneratorDefinitionRun, service: InfrahubServices) -> None: generators = await service.client.filters( kind=InfrahubKind.GENERATORDEFINITION, prefetch_relationships=True, populate_store=True, branch=message.branch diff --git a/backend/infrahub/message_bus/operations/trigger/ipam.py b/backend/infrahub/message_bus/operations/trigger/ipam.py index 49dba8bf78..79b61b34fc 100644 --- a/backend/infrahub/message_bus/operations/trigger/ipam.py +++ b/backend/infrahub/message_bus/operations/trigger/ipam.py @@ -1,4 +1,7 @@ import ipaddress +from typing import TYPE_CHECKING + +from prefect import flow from infrahub.core import registry from infrahub.core.ipam.reconciler import IpamReconciler @@ -6,16 +9,20 @@ from infrahub.message_bus import messages from infrahub.services import InfrahubServices +if TYPE_CHECKING: + from infrahub.core.ipam.constants import AllIPTypes + log = get_logger() +@flow(name="ipam-reconciliation") async def reconciliation(message: messages.TriggerIpamReconciliation, service: InfrahubServices) -> None: branch = await registry.get_branch(db=service.database, branch=message.branch) ipam_reconciler = IpamReconciler(db=service.database, branch=branch) for ipam_node_details in message.ipam_node_details: if ipam_node_details.is_address: - ip_value = ipaddress.ip_interface(ipam_node_details.ip_value) + ip_value: AllIPTypes = ipaddress.ip_interface(ipam_node_details.ip_value) else: ip_value = ipaddress.ip_network(ipam_node_details.ip_value) await ipam_reconciler.reconcile( diff --git a/backend/infrahub/message_bus/operations/trigger/proposed_change.py b/backend/infrahub/message_bus/operations/trigger/proposed_change.py index 2519425b75..1f2f2d2f68 100644 --- a/backend/infrahub/message_bus/operations/trigger/proposed_change.py +++ b/backend/infrahub/message_bus/operations/trigger/proposed_change.py @@ -1,3 +1,5 @@ +from prefect import flow + from infrahub.core.constants import InfrahubKind, ProposedChangeState from infrahub.log import get_logger from infrahub.message_bus import messages @@ -6,6 +8,7 @@ log = get_logger() +@flow(name="proposed-change-cancel") async def cancel(message: messages.TriggerProposedChangeCancel, service: InfrahubServices) -> None: proposed_changed_opened = await service.client.filters( kind=InfrahubKind.PROPOSEDCHANGE, include=["id", "source_branch"], state__value=ProposedChangeState.OPEN.value diff --git a/backend/infrahub/message_bus/operations/trigger/webhook.py b/backend/infrahub/message_bus/operations/trigger/webhook.py index 4f6034721f..200459a8c0 100644 --- a/backend/infrahub/message_bus/operations/trigger/webhook.py +++ b/backend/infrahub/message_bus/operations/trigger/webhook.py @@ -1,9 +1,12 @@ from typing import List +from prefect import flow + from infrahub.message_bus import InfrahubMessage, messages from infrahub.services import InfrahubServices +@flow(name="webhook-trigger-actions") async def actions(message: messages.TriggerWebhookActions, service: InfrahubServices) -> None: webhooks = await service.cache.list_keys(filter_pattern="webhook:active:*") events: List[InfrahubMessage] = [] diff --git a/backend/infrahub/middleware.py b/backend/infrahub/middleware.py index 4f9cd37d0c..99060de738 100644 --- a/backend/infrahub/middleware.py +++ b/backend/infrahub/middleware.py @@ -7,7 +7,7 @@ class InfrahubCORSMiddleware(CORSMiddleware): - def __init__(self, app: ASGIApp, *args: Any, **kwargs: Any): + def __init__(self, app: ASGIApp, *args: Any, **kwargs: Any) -> None: config.SETTINGS.initialize_and_exit() kwargs["allow_origins"] = config.SETTINGS.api.cors_allow_origins kwargs["allow_credentials"] = config.SETTINGS.api.cors_allow_credentials diff --git a/backend/infrahub/models.py b/backend/infrahub/models.py index 4f9f60c5e2..d2c2641475 100644 --- a/backend/infrahub/models.py +++ b/backend/infrahub/models.py @@ -13,6 +13,10 @@ class UserToken(BaseModel): refresh_token: str = Field(..., description="JWT refresh_token") +class UserTokenWithUrl(UserToken): + final_url: str = Field(..., description="The final url after logged in") + + class AccessTokenResponse(BaseModel): access_token: str = Field(..., description="JWT access_token") diff --git a/backend/infrahub/permissions/__init__.py b/backend/infrahub/permissions/__init__.py new file mode 100644 index 0000000000..e5ed3928ef --- /dev/null +++ b/backend/infrahub/permissions/__init__.py @@ -0,0 +1,4 @@ +from .backend import PermissionBackend +from .local_backend import LocalPermissionBackend + +__all__ = ["LocalPermissionBackend", "PermissionBackend"] diff --git a/backend/infrahub/permissions/backend.py b/backend/infrahub/permissions/backend.py new file mode 100644 index 0000000000..ed404f6bc8 --- /dev/null +++ b/backend/infrahub/permissions/backend.py @@ -0,0 +1,17 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from infrahub.core.branch import Branch + from infrahub.database import InfrahubDatabase + from infrahub.permissions.constants import AssignedPermissions + + +class PermissionBackend(ABC): + @abstractmethod + async def load_permissions(self, db: InfrahubDatabase, account_id: str, branch: Branch) -> AssignedPermissions: ... + + @abstractmethod + async def has_permission(self, db: InfrahubDatabase, account_id: str, permission: str, branch: Branch) -> bool: ... diff --git a/backend/infrahub/permissions/constants.py b/backend/infrahub/permissions/constants.py new file mode 100644 index 0000000000..3dad122d62 --- /dev/null +++ b/backend/infrahub/permissions/constants.py @@ -0,0 +1,11 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, TypedDict + +if TYPE_CHECKING: + from infrahub.core.account import GlobalPermission, ObjectPermission + + +class AssignedPermissions(TypedDict): + global_permissions: list[GlobalPermission] + object_permissions: list[ObjectPermission] diff --git a/backend/infrahub/permissions/local_backend.py b/backend/infrahub/permissions/local_backend.py new file mode 100644 index 0000000000..76df5b334f --- /dev/null +++ b/backend/infrahub/permissions/local_backend.py @@ -0,0 +1,92 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from infrahub.core.account import GlobalPermission, ObjectPermission, fetch_permissions +from infrahub.core.constants import GlobalPermissions, PermissionDecision + +from .backend import PermissionBackend + +if TYPE_CHECKING: + from infrahub.core.branch import Branch + from infrahub.database import InfrahubDatabase + from infrahub.permissions.constants import AssignedPermissions + + +class LocalPermissionBackend(PermissionBackend): + wildcard_values = ["*"] + wildcard_actions = ["any"] + + def compute_specificity(self, permission: ObjectPermission) -> int: + specificity = 0 + if permission.branch not in self.wildcard_values: + specificity += 1 + if permission.namespace not in self.wildcard_values: + specificity += 1 + if permission.name not in self.wildcard_values: + specificity += 1 + if permission.action not in self.wildcard_actions: + specificity += 1 + return specificity + + def resolve_object_permission(self, permissions: list[ObjectPermission], permission_to_check: str) -> bool: + """Compute the permissions and check if the one provided is allowed.""" + if not permission_to_check.startswith("object:"): + return False + + most_specific_permission: str | None = None + highest_specificity: int = -1 + _, branch, namespace, name, action, _ = permission_to_check.split(":") + + for permission in permissions: + if ( + permission.branch in [branch, *self.wildcard_values] + and permission.namespace in [namespace, *self.wildcard_values] + and permission.name in [name, *self.wildcard_values] + and permission.action in [action, *self.wildcard_actions] + ): + # Compute the specifity of a permission to keep the decision of the most specific if two or more permissions overlap + specificity = self.compute_specificity(permission=permission) + if specificity > highest_specificity: + most_specific_permission = permission.decision + highest_specificity = specificity + elif specificity == highest_specificity and permission.decision == PermissionDecision.DENY.value: + most_specific_permission = permission.decision + + return most_specific_permission == PermissionDecision.ALLOW.value + + def resolve_global_permission(self, permissions: list[GlobalPermission], permission_to_check: str) -> bool: + if not permission_to_check.startswith("global:"): + return False + + _, action, _ = permission_to_check.split(":") + grant_permission = False + + for permission in permissions: + if permission.action == action: + # Early exit on deny as deny preempt allow + if permission.decision == PermissionDecision.DENY.value: + return False + grant_permission = True + + return grant_permission + + async def load_permissions(self, db: InfrahubDatabase, account_id: str, branch: Branch) -> AssignedPermissions: + return await fetch_permissions(db=db, account_id=account_id, branch=branch) + + async def has_permission(self, db: InfrahubDatabase, account_id: str, permission: str, branch: Branch) -> bool: + granted_permissions = await self.load_permissions(db=db, account_id=account_id, branch=branch) + + # Check for a final super admin permission at the end if no permissions have matched before + return ( + self.resolve_global_permission( + permissions=granted_permissions["global_permissions"], permission_to_check=permission + ) + or self.resolve_object_permission( + permissions=granted_permissions["object_permissions"], permission_to_check=permission + ) + or self.resolve_global_permission( + permissions=granted_permissions["global_permissions"], + permission_to_check=f"global:{GlobalPermissions.SUPER_ADMIN.value}:{PermissionDecision.ALLOW.value}", + ) + ) diff --git a/backend/infrahub/permissions/report.py b/backend/infrahub/permissions/report.py new file mode 100644 index 0000000000..4f6e37d6a0 --- /dev/null +++ b/backend/infrahub/permissions/report.py @@ -0,0 +1,59 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from infrahub.core.account import fetch_permissions +from infrahub.core.constants import GlobalPermissions, PermissionDecision +from infrahub.core.registry import registry +from infrahub.permissions.local_backend import LocalPermissionBackend + +if TYPE_CHECKING: + from infrahub.auth import AccountSession + from infrahub.core.branch import Branch + from infrahub.core.schema import MainSchemaTypes + from infrahub.database import InfrahubDatabase + from infrahub.permissions.types import KindPermissions + + +async def report_schema_permissions( + db: InfrahubDatabase, schemas: list[MainSchemaTypes], account_session: AccountSession, branch: Branch +) -> list[KindPermissions]: + permissions = await fetch_permissions(account_id=account_session.account_id, db=db, branch=branch) + perm_backend = LocalPermissionBackend() + + restrict_changes = False + if branch.name == registry.default_branch: + restrict_changes = not perm_backend.resolve_global_permission( + permissions=permissions["global_permissions"], + permission_to_check=f"global:{GlobalPermissions.EDIT_DEFAULT_BRANCH.value}:allow", + ) + + permission_objects: list[KindPermissions] = [] + + for node in schemas: + permission_base = f"object:{branch.name}:{node.namespace}:{node.name}" + + has_create = perm_backend.resolve_object_permission( + permissions=permissions["object_permissions"], permission_to_check=f"{permission_base}:create:allow" + ) + has_delete = perm_backend.resolve_object_permission( + permissions=permissions["object_permissions"], permission_to_check=f"{permission_base}:delete:allow" + ) + has_update = perm_backend.resolve_object_permission( + permissions=permissions["object_permissions"], permission_to_check=f"{permission_base}:update:allow" + ) + has_view = perm_backend.resolve_object_permission( + permissions=permissions["object_permissions"], permission_to_check=f"{permission_base}:view:allow" + ) + + permission_objects.append( + { + "kind": node.kind, + "create": PermissionDecision.ALLOW if has_create and not restrict_changes else PermissionDecision.DENY, + "delete": PermissionDecision.ALLOW if has_delete and not restrict_changes else PermissionDecision.DENY, + "update": PermissionDecision.ALLOW if has_update and not restrict_changes else PermissionDecision.DENY, + "view": PermissionDecision.ALLOW if has_view else PermissionDecision.DENY, + } + ) + + return permission_objects diff --git a/backend/infrahub/permissions/types.py b/backend/infrahub/permissions/types.py new file mode 100644 index 0000000000..097ed1602e --- /dev/null +++ b/backend/infrahub/permissions/types.py @@ -0,0 +1,14 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, TypedDict + +if TYPE_CHECKING: + from infrahub.core.constants import PermissionDecision + + +class KindPermissions(TypedDict): + kind: str + create: PermissionDecision + delete: PermissionDecision + update: PermissionDecision + view: PermissionDecision diff --git a/backend/infrahub/pools/prefix.py b/backend/infrahub/pools/prefix.py index 374acef6d4..f28a984f71 100644 --- a/backend/infrahub/pools/prefix.py +++ b/backend/infrahub/pools/prefix.py @@ -11,7 +11,7 @@ class PrefixPool: Class to automatically manage Prefixes and help to carve out sub-prefixes """ - def __init__(self, network: str): + def __init__(self, network: str) -> None: self.network = ipaddress.ip_network(network) # Define biggest and smallest possible masks diff --git a/backend/infrahub/serve/log.py b/backend/infrahub/serve/log.py index 18c4f06a25..74c08396b8 100644 --- a/backend/infrahub/serve/log.py +++ b/backend/infrahub/serve/log.py @@ -6,7 +6,7 @@ class GunicornLogger(Logger): - def __init__(self, cfg: Any): + def __init__(self, cfg: Any) -> None: super().__init__(cfg) self.logger = get_logger("gunicorn") diff --git a/backend/infrahub/server.py b/backend/infrahub/server.py index 0ad5f5529d..e7c6ef62ac 100644 --- a/backend/infrahub/server.py +++ b/backend/infrahub/server.py @@ -37,6 +37,8 @@ from infrahub.services.adapters.cache.redis import RedisCache from infrahub.services.adapters.message_bus.nats import NATSMessageBus from infrahub.services.adapters.message_bus.rabbitmq import RabbitMQMessageBus +from infrahub.services.adapters.workflow.local import WorkflowLocalExecution +from infrahub.services.adapters.workflow.worker import WorkflowWorkerExecution from infrahub.trace import add_span_exception, configure_trace, get_traceid from infrahub.worker import WORKER_IDENTITY @@ -62,6 +64,12 @@ async def app_initialization(application: FastAPI) -> None: build_component_registry() + workflow = config.OVERRIDE.workflow or ( + WorkflowWorkerExecution() + if config.SETTINGS.workflow.driver == config.WorkflowDriver.WORKER + else WorkflowLocalExecution() + ) + message_bus = config.OVERRIDE.message_bus or ( NATSMessageBus() if config.SETTINGS.broker.driver == config.BrokerDriver.NATS else RabbitMQMessageBus() ) @@ -69,7 +77,11 @@ async def app_initialization(application: FastAPI) -> None: NATSCache() if config.SETTINGS.cache.driver == config.CacheDriver.NATS else RedisCache() ) service = InfrahubServices( - cache=cache, database=database, message_bus=message_bus, component_type=ComponentType.API_SERVER + cache=cache, + database=database, + message_bus=message_bus, + workflow=workflow, + component_type=ComponentType.API_SERVER, ) await service.initialize() initialize_lock(service=service) diff --git a/backend/infrahub/services/__init__.py b/backend/infrahub/services/__init__.py index 1dd37c2eac..3678bc7b23 100644 --- a/backend/infrahub/services/__init__.py +++ b/backend/infrahub/services/__init__.py @@ -13,7 +13,11 @@ from infrahub.message_bus.types import MessageTTL from .adapters.cache import InfrahubCache +from .adapters.event import InfrahubEventService +from .adapters.http import InfrahubHTTP +from .adapters.http.httpx import HttpxAdapter from .adapters.message_bus import InfrahubMessageBus +from .adapters.workflow import InfrahubWorkflow from .component import InfrahubComponent from .protocols import InfrahubLogger from .scheduler import InfrahubScheduler @@ -26,15 +30,21 @@ def __init__( client: Optional[InfrahubClient] = None, database: Optional[InfrahubDatabase] = None, message_bus: Optional[InfrahubMessageBus] = None, + http: InfrahubHTTP | None = None, + workflow: Optional[InfrahubWorkflow] = None, + event: InfrahubEventService | None = None, log: Optional[InfrahubLogger] = None, component_type: Optional[ComponentType] = None, - ): + ) -> None: self.cache = cache or InfrahubCache() self._client = client self._database = database self.message_bus = message_bus or InfrahubMessageBus() + self.workflow = workflow or InfrahubWorkflow() + self.event = event or InfrahubEventService() self.log = log or get_logger() self.component_type = component_type or ComponentType.NONE + self.http = http or HttpxAdapter() self.scheduler = InfrahubScheduler() self.component = InfrahubComponent() @@ -90,10 +100,13 @@ def git_report( async def initialize(self) -> None: """Initialize the Services""" - await self.component.initialize(service=self) await self.message_bus.initialize(service=self) await self.cache.initialize(service=self) + await self.http.initialize(service=self) + await self.component.initialize(service=self) await self.scheduler.initialize(service=self) + await self.workflow.initialize(service=self) + await self.event.initialize(service=self) async def shutdown(self) -> None: """Initialize the Services""" diff --git a/backend/infrahub/services/adapters/event/__init__.py b/backend/infrahub/services/adapters/event/__init__.py new file mode 100644 index 0000000000..865778ce08 --- /dev/null +++ b/backend/infrahub/services/adapters/event/__init__.py @@ -0,0 +1,46 @@ +from __future__ import annotations + +import asyncio +from typing import TYPE_CHECKING + +from prefect.events import emit_event + +from infrahub.exceptions import InitializationError + +if TYPE_CHECKING: + from infrahub.events import InfrahubEvent + from infrahub.services import InfrahubServices + + +class InfrahubEventService: + """Base class for infrahub event service""" + + def __init__(self) -> None: + self._service: InfrahubServices | None = None + + @property + def service(self) -> InfrahubServices: + if not self._service: + raise InitializationError("Event is not initialized with a service") + + return self._service + + async def initialize(self, service: InfrahubServices) -> None: + """Initialize the event service""" + self._service = service + + async def send(self, event: InfrahubEvent) -> None: + tasks = [self._send_bus(event=event), self._send_prefect(event=event)] + await asyncio.gather(*tasks) + + async def _send_bus(self, event: InfrahubEvent) -> None: + message = event.get_message() + await self.service.send(message=message) + + async def _send_prefect(self, event: InfrahubEvent) -> None: + emit_event( + event=event.get_name(), + resource=event.get_resource(), + related=event.get_related(), + payload=event.get_payload(), + ) diff --git a/backend/infrahub/services/adapters/http/__init__.py b/backend/infrahub/services/adapters/http/__init__.py new file mode 100644 index 0000000000..744715e1d5 --- /dev/null +++ b/backend/infrahub/services/adapters/http/__init__.py @@ -0,0 +1,30 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any + +if TYPE_CHECKING: + import httpx + + from infrahub.services import InfrahubServices + + +class InfrahubHTTP: + async def initialize(self, service: InfrahubServices) -> None: + """Initialize the HTTP adapter""" + + async def get( + self, + url: str, + headers: dict[str, Any] | None = None, + ) -> httpx.Response: + raise NotImplementedError() + + async def post( + self, + url: str, + data: Any | None = None, + json: Any | None = None, + headers: dict[str, Any] | None = None, + verify: bool | None = None, + ) -> httpx.Response: + raise NotImplementedError() diff --git a/backend/infrahub/services/adapters/http/httpx.py b/backend/infrahub/services/adapters/http/httpx.py new file mode 100644 index 0000000000..26bec3fc8e --- /dev/null +++ b/backend/infrahub/services/adapters/http/httpx.py @@ -0,0 +1,98 @@ +from __future__ import annotations + +import ssl +from functools import cached_property +from typing import TYPE_CHECKING, Any + +import httpx + +from infrahub import config +from infrahub.exceptions import HTTPServerError, HTTPServerSSLError, HTTPServerTimeoutError +from infrahub.services.adapters.http import InfrahubHTTP + +if TYPE_CHECKING: + from infrahub.services import InfrahubServices + + +class HttpxAdapter(InfrahubHTTP): + settings: config.HTTPSettings + service: InfrahubServices + + async def initialize(self, service: InfrahubServices) -> None: + """Initialize the HTTP adapter""" + self.service = service + self.settings = config.SETTINGS.http + + # Cache the context during init, this is to avoid issue when a CA bundle might be accessible + # when Infrahub initializes but then removed before the first external HTTP call is made. + _ = self.tls_context + + @cached_property + def tls_context(self) -> ssl.SSLContext: + return self.settings.get_tls_context() + + def verify_tls(self, verify: bool | None = None) -> bool | ssl.SSLContext: + if verify is False: + return False + + return self.tls_context + + async def _request( + self, + method: str, + url: str, + data: Any | None = None, + json: Any | None = None, + headers: dict[str, Any] | None = None, + verify: bool | None = None, + ) -> httpx.Response: + """Returns an httpx.Response object or raises HTTPServerError or child classes.""" + params: dict[str, Any] = {} + if data: + params["data"] = data + if json: + params["json"] = json + async with httpx.AsyncClient(verify=self.verify_tls(verify=verify)) as client: + try: + response = await client.request( + method=method, + url=url, + headers=headers, + timeout=self.settings.timeout, + **params, + ) + except ssl.SSLCertVerificationError as exc: + self.service.log.info(f"TLS verification failed for connection to {url}") + raise HTTPServerSSLError(message=f"Unable to validate TLS certificate for connection to {url}") from exc + except httpx.ReadTimeout as exc: + self.service.log.info(f"Connection timed out when trying to reach {url}") + raise HTTPServerTimeoutError( + message=f"Connection to {url} timed out after {self.settings.timeout}" + ) from exc + except httpx.RequestError as exc: + # Catch all error from httpx + self.service.log.warning(f"Unhandled HTTP error for {url} ({exc})") + raise HTTPServerError(message=f"Unknown http error when connecting to {url}") from exc + + return response + + async def get( + self, + url: str, + headers: dict[str, Any] | None = None, + ) -> httpx.Response: + return await self._request( + method="get", + url=url, + headers=headers, + ) + + async def post( + self, + url: str, + data: Any | None = None, + json: Any | None = None, + headers: dict[str, Any] | None = None, + verify: bool | None = None, + ) -> httpx.Response: + return await self._request(method="post", url=url, data=data, json=json, headers=headers, verify=verify) diff --git a/backend/infrahub/services/adapters/message_bus/local.py b/backend/infrahub/services/adapters/message_bus/local.py index 57e55ac3d4..09967080aa 100644 --- a/backend/infrahub/services/adapters/message_bus/local.py +++ b/backend/infrahub/services/adapters/message_bus/local.py @@ -21,7 +21,7 @@ class BusSimulator(InfrahubMessageBus): - def __init__(self, database: Optional[InfrahubDatabase] = None): + def __init__(self, database: Optional[InfrahubDatabase] = None) -> None: self.messages: list[InfrahubMessage] = [] self.messages_per_routing_key: dict[str, list[InfrahubMessage]] = {} self.service: InfrahubServices = InfrahubServices(database=database, message_bus=self) diff --git a/backend/infrahub/services/adapters/message_bus/rabbitmq.py b/backend/infrahub/services/adapters/message_bus/rabbitmq.py index 2ba4421792..053e31b3a2 100644 --- a/backend/infrahub/services/adapters/message_bus/rabbitmq.py +++ b/backend/infrahub/services/adapters/message_bus/rabbitmq.py @@ -93,7 +93,7 @@ async def initialize(self, service: InfrahubServices) -> None: ssl=self.settings.tls_enabled, ssl_options={ "no_verify_ssl": int(self.settings.tls_insecure), - "cafile": self.settings.tls_ca_file if self.settings.tls_ca_file else "", + "cafile": self.settings.tls_ca_file or "", }, ) diff --git a/backend/infrahub/services/adapters/workflow/__init__.py b/backend/infrahub/services/adapters/workflow/__init__.py new file mode 100644 index 0000000000..19aecf5b1c --- /dev/null +++ b/backend/infrahub/services/adapters/workflow/__init__.py @@ -0,0 +1,25 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Awaitable, Callable, ParamSpec, TypeVar + +if TYPE_CHECKING: + from infrahub.services import InfrahubServices + from infrahub.workflows.models import WorkflowDefinition + +Return = TypeVar("Return") +Params = ParamSpec("Params") + +FuncType = Callable[Params, Return] + + +class InfrahubWorkflow: + async def initialize(self, service: InfrahubServices) -> None: + """Initialize the Workflow engine""" + + async def execute( + self, + workflow: WorkflowDefinition | None = None, + function: Callable[..., Awaitable[Return]] | None = None, + **kwargs: Any, + ) -> Return: + raise NotImplementedError() diff --git a/backend/infrahub/services/adapters/workflow/local.py b/backend/infrahub/services/adapters/workflow/local.py new file mode 100644 index 0000000000..a917d0d241 --- /dev/null +++ b/backend/infrahub/services/adapters/workflow/local.py @@ -0,0 +1,20 @@ +from typing import Any, Awaitable, Callable + +from infrahub.workflows.models import WorkflowDefinition + +from . import InfrahubWorkflow, Return + + +class WorkflowLocalExecution(InfrahubWorkflow): + async def execute( + self, + workflow: WorkflowDefinition | None = None, + function: Callable[..., Awaitable[Return]] | None = None, + **kwargs: Any, + ) -> Return: + if workflow: + fn = workflow.get_function() + return await fn(**kwargs) + if function: + return await function(**kwargs) + raise ValueError("either a workflow definition or a flow must be provided") diff --git a/backend/infrahub/services/adapters/workflow/worker.py b/backend/infrahub/services/adapters/workflow/worker.py new file mode 100644 index 0000000000..55a6ef0d8c --- /dev/null +++ b/backend/infrahub/services/adapters/workflow/worker.py @@ -0,0 +1,45 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Awaitable, Callable + +from prefect.client.schemas import StateType +from prefect.deployments import run_deployment + +from infrahub.workflows.initialization import setup_task_manager + +from . import InfrahubWorkflow, Return + +if TYPE_CHECKING: + from prefect.client.schemas.objects import FlowRun + + from infrahub.services import InfrahubServices + from infrahub.workflows.models import WorkflowDefinition + + +class WorkflowWorkerExecution(InfrahubWorkflow): + async def initialize(self, service: InfrahubServices) -> None: + """Initialize the Workflow engine""" + + if await service.component.is_primary_api(): + await setup_task_manager() + + async def execute( + self, + workflow: WorkflowDefinition | None = None, + function: Callable[..., Awaitable[Return]] | None = None, + **kwargs: Any, + ) -> Return: + if workflow: + response: FlowRun = await run_deployment(name=workflow.full_name, parameters=kwargs or {}) # type: ignore[return-value, misc] + if not response.state: + raise RuntimeError("Unable to read state from the response") + + if response.state.type == StateType.CRASHED: + raise RuntimeError(response.state.message) + + return await response.state.result(raise_on_failure=True, fetch=True) # type: ignore[call-overload] + + if function: + return await function(**kwargs) + + raise ValueError("either a workflow definition or a flow must be provided") diff --git a/backend/infrahub/services/component.py b/backend/infrahub/services/component.py index 0cca32ee80..6f0f20e8eb 100644 --- a/backend/infrahub/services/component.py +++ b/backend/infrahub/services/component.py @@ -42,6 +42,8 @@ async def initialize(self, service: InfrahubServices) -> None: """Initialize the Message bus""" self._service = service + await self.refresh_heartbeat() + async def is_primary_api(self) -> bool: primary_identity = await self.service.cache.get(PRIMARY_API_SERVER) return primary_identity == WORKER_IDENTITY diff --git a/backend/infrahub/tasks/dummy.py b/backend/infrahub/tasks/dummy.py new file mode 100644 index 0000000000..ef639ddbe0 --- /dev/null +++ b/backend/infrahub/tasks/dummy.py @@ -0,0 +1,41 @@ +from prefect import flow, task +from pydantic import BaseModel + +from infrahub.workflows.models import WorkflowDefinition + +DUMMY_FLOW = WorkflowDefinition( + name="dummy_flow", + module="infrahub.tasks.dummy", + function="dummy_flow", +) + +DUMMY_FLOW_BROKEN = WorkflowDefinition( + name="dummy_flow_broken", + module="infrahub.tasks.dummy", + function="dummy_flow_broken", +) + + +class DummyInput(BaseModel): + firstname: str + lastname: str + + +class DummyOutput(BaseModel): + full_name: str + + +@task +async def aggregate_name(firstname: str, lastname: str) -> str: + return f"{firstname}, {lastname}" + + +@flow(persist_result=True) +async def dummy_flow(data: DummyInput) -> DummyOutput: + return DummyOutput(full_name=await aggregate_name(firstname=data.firstname, lastname=data.lastname)) + + +@flow(persist_result=True) +async def dummy_flow_broken(data: DummyInput) -> DummyOutput: + response = await aggregate_name(firstname=data.firstname, lastname=data.lastname) + return DummyOutput(not_valid=response) # type: ignore[call-arg] diff --git a/backend/infrahub/test_data/dataset01.py b/backend/infrahub/test_data/dataset01.py index cfc2dacaf2..f13f2f7fe7 100644 --- a/backend/infrahub/test_data/dataset01.py +++ b/backend/infrahub/test_data/dataset01.py @@ -1,5 +1,5 @@ -from infrahub.core.constants import InfrahubKind from infrahub.core.node import Node +from infrahub.core.protocols import CoreGroup from infrahub.database import InfrahubDatabase from infrahub.log import get_logger @@ -74,7 +74,7 @@ log = get_logger() -async def load_data(db: InfrahubDatabase, nbr_devices: int = None): +async def load_data(db: InfrahubDatabase, nbr_devices: int = 0) -> None: # ------------------------------------------ # Create User Accounts and Groups # ------------------------------------------ @@ -82,7 +82,7 @@ async def load_data(db: InfrahubDatabase, nbr_devices: int = None): # tags_dict = {} for group in GROUPS: - obj = await Node.init(db=db, schema=InfrahubKind.GENERICGROUP) + obj = await Node.init(db=db, schema=CoreGroup) await obj.new(db=db, description=group[0], name=group[1]) await obj.save(db=db) groups_dict[group[1]] = obj @@ -112,10 +112,10 @@ async def load_data(db: InfrahubDatabase, nbr_devices: int = None): role = None if device[4]: role = device[4] - obj = await Node.init(db=db, schema="InfraDevice") - await obj.new(db=db, name=device[0], status=status, type=device[2], role=role, site=site_hq) + device_obj = await Node.init(db=db, schema="InfraDevice") + await device_obj.new(db=db, name=device[0], status=status, type=device[2], role=role, site=site_hq) - await obj.save(db=db) + await device_obj.save(db=db) log.info(f"- Created Device: {device[0]}") # Add a special interface for spine1 @@ -152,7 +152,7 @@ async def load_data(db: InfrahubDatabase, nbr_devices: int = None): intf = await Node.init(db=db, schema="InfraInterfaceL3") await intf.new( db=db, - device=obj, + device=device_obj, name=intf_name, speed=10000, enabled=enabled, diff --git a/backend/infrahub/test_data/gen_connected_nodes.py b/backend/infrahub/test_data/gen_connected_nodes.py index d258f02d84..0af4e284ac 100644 --- a/backend/infrahub/test_data/gen_connected_nodes.py +++ b/backend/infrahub/test_data/gen_connected_nodes.py @@ -12,7 +12,7 @@ class GenerateConnectedNodes(DataGenerator): - async def load_data(self, nbr_tags: int = 50, nbr_repository: int = 100, nbr_query: int = 1000): + async def load_data(self, nbr_tags: int = 50, nbr_repository: int = 100, nbr_query: int = 1000) -> None: """Generate a large number of GraphQLQuery associated with some Tags and some Repositorie.""" default_branch = await registry.get_branch(db=self.db) diff --git a/backend/infrahub/test_data/gen_isolated_node.py b/backend/infrahub/test_data/gen_isolated_node.py index 8dcf06360a..ee768d3336 100644 --- a/backend/infrahub/test_data/gen_isolated_node.py +++ b/backend/infrahub/test_data/gen_isolated_node.py @@ -15,7 +15,7 @@ async def load_data( self, nbr_tags: int = 100, nbr_repository: int = 100, - ): + ) -> None: """Generate a large number of Tags and Repositories""" default_branch = await registry.get_branch(db=self.db) diff --git a/backend/infrahub/test_data/gen_node_profile_node.py b/backend/infrahub/test_data/gen_node_profile_node.py index e094d46aae..cdad46fb2c 100644 --- a/backend/infrahub/test_data/gen_node_profile_node.py +++ b/backend/infrahub/test_data/gen_node_profile_node.py @@ -14,7 +14,7 @@ class ProfileAttribute(DataGenerator): async def load_data( self, nbr_person: int = 100, - ): + ) -> None: """Generate a large number of Tags and Repositories""" default_branch = await registry.get_branch(db=self.db) diff --git a/backend/infrahub/test_data/shared.py b/backend/infrahub/test_data/shared.py index 9421263500..6978a12504 100644 --- a/backend/infrahub/test_data/shared.py +++ b/backend/infrahub/test_data/shared.py @@ -25,7 +25,7 @@ def __init__( semaphore: Optional[asyncio.Semaphore] = None, max_concurrent_execution: int = 5, return_exceptions: bool = False, - ): + ) -> None: super().__init__( semaphore=semaphore, max_concurrent_execution=max_concurrent_execution, return_exceptions=return_exceptions ) @@ -41,7 +41,9 @@ def add(self, *args: Any, **kwargs: Any) -> None: class DataGenerator: - def __init__(self, db: InfrahubDatabase, concurrent_execution: int = 2, progress: Optional[Progress] = None): + def __init__( + self, db: InfrahubDatabase, concurrent_execution: int = 2, progress: Optional[Progress] = None + ) -> None: self.db = db self.concurrent_execution = concurrent_execution self.progress = progress diff --git a/backend/infrahub/types.py b/backend/infrahub/types.py index 9ee6ef4006..5b2805d773 100644 --- a/backend/infrahub/types.py +++ b/backend/infrahub/types.py @@ -17,7 +17,7 @@ from infrahub.graphql.types.attribute import BaseAttribute as BaseAttributeType DEFAULT_MODULE_ATTRIBUTE = "infrahub.core.attribute" -DEFAULT_MODULE_GRAPHQL_INPUT = "infrahub.graphql.mutations" +DEFAULT_MODULE_GRAPHQL_INPUT = "infrahub.graphql.mutations.attribute" DEFAULT_MODULE_GRAPHQL_QUERY = "infrahub.graphql.types" @@ -32,7 +32,7 @@ class InfrahubDataType: pydantic: type @classmethod - def __init_subclass__(cls, **kwargs: typing.Any): + def __init_subclass__(cls, **kwargs: typing.Any) -> None: super().__init_subclass__(**kwargs) registry.data_type[cls.label] = cls diff --git a/backend/infrahub/utils.py b/backend/infrahub/utils.py index 05b97dc44b..0ac995275f 100644 --- a/backend/infrahub/utils.py +++ b/backend/infrahub/utils.py @@ -2,6 +2,7 @@ import os from enum import Enum, EnumMeta from pathlib import Path +from re import finditer from typing import Any, Optional KWARGS_TO_DROP = ["session"] @@ -29,6 +30,12 @@ def find_first_file_in_directory(directory: str) -> Optional[str]: return None +def extract_camelcase_words(camel_case: str) -> list[str]: + """Extract the namespace and the name for a kind given its camel-case form.""" + matches = finditer(r".+?(?:(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|$)", camel_case) + return [m.group(0) for m in matches] + + def format_label(slug: str) -> str: return " ".join([word.title() for word in slug.split("_")]) diff --git a/backend/infrahub/webhook.py b/backend/infrahub/webhook.py index c0136ff716..ee308cd78a 100644 --- a/backend/infrahub/webhook.py +++ b/backend/infrahub/webhook.py @@ -6,7 +6,6 @@ from typing import Any, Optional, Union from uuid import uuid4 -import httpx from pydantic import BaseModel, ConfigDict, Field from infrahub.core.constants import InfrahubKind @@ -37,8 +36,7 @@ def webhook_type(self) -> str: async def send(self) -> None: await self._prepare_payload() self._assign_headers() - async with httpx.AsyncClient(verify=self.validate_certificates) as client: - await client.post(self.url, json=self._payload, headers=self._headers) + await self.service.http.post(url=self.url, json=self._payload, headers=self._headers) class CustomWebhook(Webhook): diff --git a/sync/examples/ipfabric_to_infrahub/ipfabricsync/__init__.py b/backend/infrahub/workers/__init__.py similarity index 100% rename from sync/examples/ipfabric_to_infrahub/ipfabricsync/__init__.py rename to backend/infrahub/workers/__init__.py diff --git a/backend/infrahub/workers/infrahub_async.py b/backend/infrahub/workers/infrahub_async.py new file mode 100644 index 0000000000..85d6a9b00c --- /dev/null +++ b/backend/infrahub/workers/infrahub_async.py @@ -0,0 +1,160 @@ +import importlib +import logging +import os +from typing import Any, Optional + +import typer +from anyio.abc import TaskStatus +from infrahub_sdk import Config, InfrahubClient +from infrahub_sdk.exceptions import Error as SdkError +from prefect import settings as prefect_settings +from prefect.client.schemas.objects import FlowRun +from prefect.flow_engine import run_flow_async +from prefect.workers.base import BaseJobConfiguration, BaseVariables, BaseWorker, BaseWorkerResult +from prometheus_client import start_http_server + +from infrahub import config +from infrahub.components import ComponentType +from infrahub.core.initialization import initialization +from infrahub.database import InfrahubDatabase, get_db +from infrahub.dependencies.registry import build_component_registry +from infrahub.git import initialize_repositories_directory +from infrahub.lock import initialize_lock +from infrahub.services import InfrahubServices, services +from infrahub.services.adapters.cache.nats import NATSCache +from infrahub.services.adapters.cache.redis import RedisCache +from infrahub.services.adapters.message_bus.nats import NATSMessageBus +from infrahub.services.adapters.message_bus.rabbitmq import RabbitMQMessageBus +from infrahub.services.adapters.workflow.local import WorkflowLocalExecution +from infrahub.services.adapters.workflow.worker import WorkflowWorkerExecution +from infrahub.workflows.models import TASK_RESULT_STORAGE_NAME + + +class InfrahubWorkerAsyncConfiguration(BaseJobConfiguration): + pass + + +class InfrahubWorkerAsyncTemplateVariables(BaseVariables): + pass + + +class InfrahubWorkerAsyncResult(BaseWorkerResult): + """Result returned by the InfrahubWorker.""" + + +class InfrahubWorkerAsync(BaseWorker): + type: str = "infrahubasync" + job_configuration = InfrahubWorkerAsyncConfiguration + job_configuration_variables = InfrahubWorkerAsyncTemplateVariables + _documentation_url = "https://example.com/docs" + _logo_url = "https://example.com/logo" + _description = "Infrahub worker designed to run the flow in the main async loop." + + async def setup(self, **kwargs: dict[str, Any]) -> None: + logging.getLogger("websockets").setLevel(logging.ERROR) + logging.getLogger("httpx").setLevel(logging.ERROR) + logging.getLogger("httpcore").setLevel(logging.ERROR) + logging.getLogger("neo4j").setLevel(logging.ERROR) + logging.getLogger("aio_pika").setLevel(logging.ERROR) + logging.getLogger("aiormq").setLevel(logging.ERROR) + logging.getLogger("git").setLevel(logging.ERROR) + + config_file = os.environ.get("INFRAHUB_CONFIG", "infrahub.toml") + config.load_and_exit(config_file_name=config_file) + + # Start metric endpoint on port 8000 + metric_port = int(os.environ.get("INFRAHUB_METRICS_PORT", 8000)) + self._logger.info(f"Starting metric endpoint on port {metric_port}") + start_http_server(metric_port) + + self._exit_stack.enter_context( + prefect_settings.temporary_settings( + updates={ # type: ignore[arg-type] + prefect_settings.PREFECT_WORKER_QUERY_SECONDS: config.SETTINGS.workflow.worker_polling_interval, + prefect_settings.PREFECT_RESULTS_PERSIST_BY_DEFAULT: True, + prefect_settings.PREFECT_DEFAULT_RESULT_STORAGE_BLOCK: f"redisstoragecontainer/{TASK_RESULT_STORAGE_NAME}", + } + ) + ) + + await super().setup(**kwargs) + + self._logger.debug(f"Using Infrahub API at {config.SETTINGS.main.internal_address}") + client = InfrahubClient( + config=Config(address=config.SETTINGS.main.internal_address, retry_on_failure=True, log=self._logger) + ) + try: + await client.branch.all() + except SdkError as exc: + self._logger.error(f"Error in communication with Infrahub: {exc.message}") + raise typer.Exit(1) + + database = InfrahubDatabase(driver=await get_db(retry=1)) + + workflow = config.OVERRIDE.workflow or ( + WorkflowWorkerExecution() + if config.SETTINGS.workflow.driver == config.WorkflowDriver.WORKER + else WorkflowLocalExecution() + ) + + message_bus = config.OVERRIDE.message_bus or ( + NATSMessageBus() if config.SETTINGS.broker.driver == config.BrokerDriver.NATS else RabbitMQMessageBus() + ) + cache = config.OVERRIDE.cache or ( + NATSCache() if config.SETTINGS.cache.driver == config.CacheDriver.NATS else RedisCache() + ) + + service = InfrahubServices( + cache=cache, + client=client, + database=database, + message_bus=message_bus, + workflow=workflow, + component_type=ComponentType.GIT_AGENT, + ) + services.service = service + + await service.initialize() + + # Initialize the lock + initialize_lock(service=service) + + async with service.database.start_session() as db: + await initialization(db=db) + + await service.component.refresh_schema_hash() + + initialize_repositories_directory() + build_component_registry() + self._logger.info("Worker initialization completed .. ") + + async def run( + self, + flow_run: FlowRun, + configuration: BaseJobConfiguration, + task_status: Optional[TaskStatus] = None, + ) -> BaseWorkerResult: + flow_run_logger = self.get_flow_run_logger(flow_run) + + entrypoint: str = configuration._related_objects["deployment"].entrypoint + + file_path, flow_name = entrypoint.split(":") + file_path.replace("/", ".") + module_path = file_path.replace("backend/", "").replace(".py", "").replace("/", ".") + module = importlib.import_module(module_path) + + flow_func = getattr(module, flow_name) + + flow_run_logger.debug("Validating parameters") + params = flow_func.validate_parameters(parameters=flow_run.parameters) + + if task_status: + task_status.started() + + await run_flow_async(flow=flow_func, flow_run=flow_run, parameters=params, return_type="state") + + # exit_code = job_status.exit_code if job_status else -1 # Get result of execution for reporting + return InfrahubWorkerAsyncResult( + status_code=0, + identifier=str(flow_run.id), + ) diff --git a/sync/examples/librenms_to_infrahub/infrahub/__init__.py b/backend/infrahub/workflows/__init__.py similarity index 100% rename from sync/examples/librenms_to_infrahub/infrahub/__init__.py rename to backend/infrahub/workflows/__init__.py diff --git a/backend/infrahub/workflows/catalogue.py b/backend/infrahub/workflows/catalogue.py new file mode 100644 index 0000000000..412b68aaf3 --- /dev/null +++ b/backend/infrahub/workflows/catalogue.py @@ -0,0 +1,52 @@ +from .constants import WorkflowType +from .models import WorkerPoolDefinition, WorkflowDefinition + +INFRAHUB_WORKER_POOL = WorkerPoolDefinition( + name="infrahub-worker", worker_type="infrahubasync", description="Default Pool for internal tasks" +) + +WEBHOOK_SEND = WorkflowDefinition( + name="webhook_send", + type=WorkflowType.USER, + module="infrahub.message_bus.operations.send.webhook", + function="send_webhook", +) + +TRANSFORM_JINJA2_RENDER = WorkflowDefinition( + name="transform_render_jinja2_template", + type=WorkflowType.USER, + module="infrahub.message_bus.operations.transform.jinja", + function="transform_render_jinja2_template", +) + +ANONYMOUS_TELEMETRY_SEND = WorkflowDefinition( + name="anonymous_telemetry_send", + type=WorkflowType.INTERNAL, + cron="0 2 * * *", + module="infrahub.message_bus.operations.send.telemetry", + function="send_telemetry_push", +) + +SCHEMA_APPLY_MIGRATION = WorkflowDefinition( + name="schema_apply_migrations", + type=WorkflowType.INTERNAL, + module="infrahub.core.migrations.schema.tasks", + function="schema_apply_migrations", +) + +SCHEMA_VALIDATE_MIGRATION = WorkflowDefinition( + name="schema_validate_migrations", + type=WorkflowType.INTERNAL, + module="infrahub.core.validators.tasks", + function="schema_validate_migrations", +) + +worker_pools = [INFRAHUB_WORKER_POOL] + +workflows = [ + WEBHOOK_SEND, + TRANSFORM_JINJA2_RENDER, + ANONYMOUS_TELEMETRY_SEND, + SCHEMA_APPLY_MIGRATION, + SCHEMA_VALIDATE_MIGRATION, +] diff --git a/backend/infrahub/workflows/constants.py b/backend/infrahub/workflows/constants.py new file mode 100644 index 0000000000..ff650c935d --- /dev/null +++ b/backend/infrahub/workflows/constants.py @@ -0,0 +1,6 @@ +from infrahub.utils import InfrahubStringEnum + + +class WorkflowType(InfrahubStringEnum): + INTERNAL = "internal" + USER = "user" diff --git a/backend/infrahub/workflows/initialization.py b/backend/infrahub/workflows/initialization.py new file mode 100644 index 0000000000..e7c42ae356 --- /dev/null +++ b/backend/infrahub/workflows/initialization.py @@ -0,0 +1,68 @@ +from prefect import flow, task +from prefect.blocks.redis import RedisStorageContainer +from prefect.client.orchestration import PrefectClient, get_client +from prefect.client.schemas.actions import WorkPoolCreate +from prefect.exceptions import ObjectAlreadyExists +from prefect.logging import get_run_logger + +from infrahub import config + +from .catalogue import worker_pools, workflows +from .models import TASK_RESULT_STORAGE_NAME + + +@task(name="task-manager-setup-worker-pools") +async def setup_worker_pools(client: PrefectClient) -> None: + log = get_run_logger() + for worker in worker_pools: + wp = WorkPoolCreate( + name=worker.name, + type=worker.worker_type, + description=worker.description, + ) + try: + await client.create_work_pool(work_pool=wp) + log.info(f"Work pool {worker.name} created successfully ... ") + except ObjectAlreadyExists: + log.warning(f"Work pool {worker.name} already present ") + + +@task(name="task-manager-setup-deployments") +async def setup_deployments(client: PrefectClient) -> None: + log = get_run_logger() + for workflow in workflows: + # For now the workpool is hardcoded but + # later we need to make it dynamic to have a different worker based on the type of the workflow + work_pool = worker_pools[0] + await workflow.save(client=client, work_pool=work_pool) + log.info(f"Flow {workflow.name}, created successfully ... ") + + +@task(name="task-manager-setup-blocks") +async def setup_blocks() -> None: + log = get_run_logger() + + try: + await RedisStorageContainer.register_type_and_schema() + except ObjectAlreadyExists: + log.warning(f"Redis Storage {TASK_RESULT_STORAGE_NAME} already registered ") + + redis_block = RedisStorageContainer.from_host( + host=config.SETTINGS.cache.address, + port=config.SETTINGS.cache.service_port, + db=config.SETTINGS.cache.database, + username=config.SETTINGS.cache.username or None, + password=config.SETTINGS.cache.password or None, + ) + try: + await redis_block.save(name=TASK_RESULT_STORAGE_NAME, overwrite=True) + except ObjectAlreadyExists: + log.warning(f"Redis Storage {TASK_RESULT_STORAGE_NAME} already present ") + + +@flow(name="task-manager-setup") +async def setup_task_manager() -> None: + async with get_client(sync_client=False) as client: + await setup_blocks() + await setup_worker_pools(client=client) + await setup_deployments(client=client) diff --git a/backend/infrahub/workflows/models.py b/backend/infrahub/workflows/models.py new file mode 100644 index 0000000000..04618be11a --- /dev/null +++ b/backend/infrahub/workflows/models.py @@ -0,0 +1,60 @@ +import importlib +from typing import Any, Awaitable, Callable, Optional +from uuid import UUID + +from prefect.client.orchestration import PrefectClient +from prefect.client.schemas.actions import DeploymentScheduleCreate +from prefect.client.schemas.schedules import CronSchedule +from pydantic import BaseModel + +from infrahub import __version__ + +from .constants import WorkflowType + +TASK_RESULT_STORAGE_NAME = "infrahub-storage" + + +class WorkerPoolDefinition(BaseModel): + name: str + worker_type: str + description: str = "" + + +class WorkflowDefinition(BaseModel): + name: str + type: WorkflowType = WorkflowType.INTERNAL + module: str + function: str + cron: Optional[str] = None + + @property + def entrypoint(self) -> str: + return f'backend/{self.module.replace(".", "/")}:{self.function}' + + @property + def full_name(self) -> str: + return f"{self.name}/{self.name}" + + def to_deployment(self) -> dict[str, Any]: + payload: dict[str, Any] = { + "name": self.name, + "entrypoint": self.entrypoint, + } + if self.type == WorkflowType.INTERNAL: + payload["version"] = __version__ + if self.cron: + payload["schedules"] = [DeploymentScheduleCreate(schedule=CronSchedule(cron=self.cron))] + return payload + + async def save(self, client: PrefectClient, work_pool: WorkerPoolDefinition) -> UUID: + flow_id = await client.create_flow_from_name(self.name) + data = self.to_deployment() + data["work_pool_name"] = work_pool.name + return await client.create_deployment(flow_id=flow_id, **data) + + def get_function(self) -> Callable[..., Awaitable[Any]]: + module = importlib.import_module(self.module) + return getattr(module, self.function) + + def validate_workflow(self) -> None: + self.get_function() diff --git a/backend/templates/relationshipschema_imports.j2 b/backend/templates/relationshipschema_imports.j2 index 974c8e45a4..ef8421fc66 100644 --- a/backend/templates/relationshipschema_imports.j2 +++ b/backend/templates/relationshipschema_imports.j2 @@ -1,5 +1,7 @@ -from typing import Optional +from typing import Optional, TYPE_CHECKING from infrahub.core.constants import AllowOverrideType, BranchSupportType, RelationshipKind, RelationshipCardinality, RelationshipDeleteBehavior, RelationshipDirection, HashableModelState # noqa: TCH001 from infrahub.core.models import HashableModel -from infrahub.core.schema.filter import FilterSchema # noqa: TCH001 + +if TYPE_CHECKING: + from infrahub.core.schema.filter import FilterSchema # noqa: TCH001 diff --git a/backend/tests/adapters/message_bus.py b/backend/tests/adapters/message_bus.py index 441df9d895..abf39be12a 100644 --- a/backend/tests/adapters/message_bus.py +++ b/backend/tests/adapters/message_bus.py @@ -18,7 +18,7 @@ class BusRecorder(InfrahubMessageBus): - def __init__(self, component_type: Optional[ComponentType] = None): + def __init__(self, component_type: Optional[ComponentType] = None) -> None: self.messages: list[InfrahubMessage] = [] self.messages_per_routing_key: dict[str, list[InfrahubMessage]] = {} @@ -36,7 +36,7 @@ def seen_routing_keys(self) -> list[str]: class BusSimulator(InfrahubMessageBus): - def __init__(self, database: Optional[InfrahubDatabase] = None): + def __init__(self, database: Optional[InfrahubDatabase] = None) -> None: self.messages: list[InfrahubMessage] = [] self.messages_per_routing_key: dict[str, list[InfrahubMessage]] = {} self.service: InfrahubServices = InfrahubServices(database=database, message_bus=self) @@ -50,7 +50,7 @@ async def publish( if routing_key not in self.messages_per_routing_key: self.messages_per_routing_key[routing_key] = [] self.messages_per_routing_key[routing_key].append(message) - await execute_message(routing_key=routing_key, message_body=message.body, service=self.service) + await execute_message(routing_key=routing_key, message_body=message.body, service=self.service, skip_flow=True) async def reply(self, message: InfrahubMessage, routing_key: str) -> None: correlation_id = message.meta.correlation_id or "default" diff --git a/backend/tests/benchmark/test_graphql_query.py b/backend/tests/benchmark/test_graphql_query.py index e2d3d41c8f..299c049605 100644 --- a/backend/tests/benchmark/test_graphql_query.py +++ b/backend/tests/benchmark/test_graphql_query.py @@ -15,10 +15,11 @@ core_models, internal_schema, ) -from infrahub.core.schema_manager import SchemaBranch, SchemaManager +from infrahub.core.schema.manager import SchemaManager +from infrahub.core.schema.schema_branch import SchemaBranch from infrahub.core.utils import delete_all_nodes from infrahub.database import InfrahubDatabase -from infrahub.graphql import prepare_graphql_params +from infrahub.graphql.initialization import prepare_graphql_params from infrahub.test_data.dataset04 import load_data NBR_WARMUP = int(os.getenv("INFRAHUB_BENCHMARK_NBR_WARMUP", 5)) diff --git a/backend/tests/benchmark/test_load_node_to_db.py b/backend/tests/benchmark/test_load_node_to_db.py index 01f284eabc..de118375d7 100644 --- a/backend/tests/benchmark/test_load_node_to_db.py +++ b/backend/tests/benchmark/test_load_node_to_db.py @@ -4,7 +4,7 @@ SchemaRoot, internal_schema, ) -from infrahub.core.schema_manager import SchemaManager +from infrahub.core.schema.manager import SchemaManager from infrahub.database import InfrahubDatabase diff --git a/backend/tests/conftest.py b/backend/tests/conftest.py index c1255f0cea..7a92675924 100644 --- a/backend/tests/conftest.py +++ b/backend/tests/conftest.py @@ -2,6 +2,8 @@ import importlib import os import sys +import time +from contextlib import ExitStack from pathlib import Path from tempfile import TemporaryDirectory from typing import Any, AsyncGenerator, Generator, Optional, TypeVar @@ -9,8 +11,16 @@ import pytest import ujson from infrahub_sdk.utils import str_to_bool +from neo4j import GraphDatabase +from neo4j.exceptions import ServiceUnavailable +from prefect import settings as prefect_settings +from prefect.logging.loggers import disable_run_logger +from prefect.testing.utilities import prefect_test_harness +from testcontainers.core.container import DockerContainer +from testcontainers.core.waiting_utils import wait_for_logs from infrahub import config +from infrahub.config import load_and_exit from infrahub.core import registry from infrahub.core.branch import Branch from infrahub.core.constants import BranchSupportType, InfrahubKind @@ -23,7 +33,8 @@ from infrahub.core.node import Node from infrahub.core.schema import SchemaRoot, core_models, internal_schema from infrahub.core.schema.definitions.core import core_profile_schema_definition -from infrahub.core.schema_manager import SchemaBranch, SchemaManager +from infrahub.core.schema.manager import SchemaManager +from infrahub.core.schema.schema_branch import SchemaBranch from infrahub.core.utils import delete_all_nodes from infrahub.database import InfrahubDatabase, get_db from infrahub.lock import initialize_lock @@ -34,9 +45,15 @@ from tests.adapters.log import FakeLogger from tests.adapters.message_bus import BusRecorder, BusSimulator -BUILD_NAME = os.environ.get("INFRAHUB_BUILD_NAME", "infrahub") -TEST_IN_DOCKER = str_to_bool(os.environ.get("INFRAHUB_TEST_IN_DOCKER", "false")) ResponseClass = TypeVar("ResponseClass") +INFRAHUB_USE_TEST_CONTAINERS = str_to_bool(os.getenv("INFRAHUB_USE_TEST_CONTAINERS", "true")) +PORT_NATS = 4222 +PORT_REDIS = 6379 +PORT_CLIENT_RABBITMQ = 5672 +PORT_HTTP_RABBITMQ = 15672 +PORT_BOLT_NEO4J = 7687 +PORT_MEMGRAPH = 7687 +PORT_PREFECT = 4200 def pytest_addoption(parser): @@ -70,8 +87,18 @@ def event_loop(): @pytest.fixture(scope="module") -async def db() -> AsyncGenerator[InfrahubDatabase, None]: - driver = InfrahubDatabase(driver=await get_db(retry=1)) +async def db( + neo4j: Optional[dict[int, int]], memgraph: Optional[dict[int, int]], reload_settings_before_each_module +) -> AsyncGenerator[InfrahubDatabase, None]: + if INFRAHUB_USE_TEST_CONTAINERS: + config.SETTINGS.database.address = "localhost" + if neo4j is not None: + config.SETTINGS.database.port = neo4j[PORT_BOLT_NEO4J] + else: + assert memgraph is not None + config.SETTINGS.database.port = memgraph[PORT_MEMGRAPH] + + driver = InfrahubDatabase(driver=await get_db(retry=5)) yield driver @@ -133,27 +160,257 @@ async def register_core_models_schema(default_branch: Branch, register_internal_ return schema_branch +@pytest.fixture(scope="session") +def prefect_test_fixture(): + with prefect_test_harness(): + yield + + +@pytest.fixture(scope="session") +def prefect_test(prefect_test_fixture): + with disable_run_logger(): + yield + + +def get_exposed_port(container: DockerContainer, port: int) -> int: + """ + Use this method instead of DockerContainer.get_exposed_port as it is decorated with wait_container_is_ready + which we do not want to use as it does not perform a real healthcheck. DockerContainer.get_exposed_port + also introduces extra "Waiting for container" logs as we might call it multiple times for containers exposing + multiple ports such as rabbitmq. + """ + + return int(container.get_docker_client().port(container.get_wrapped_container().id, port)) + + +@pytest.fixture(scope="session") +def neo4j(request: pytest.FixtureRequest, load_settings_before_session) -> Optional[dict[int, int]]: + if not INFRAHUB_USE_TEST_CONTAINERS or config.SETTINGS.database.db_type == "memgraph": + return None + + container = ( + DockerContainer(image=os.getenv("NEO4J_DOCKER_IMAGE", "neo4j:5.20.0-enterprise")) + .with_env("NEO4J_AUTH", "neo4j/admin") + .with_env("NEO4J_ACCEPT_LICENSE_AGREEMENT", "yes") + .with_env("NEO4J_dbms_security_procedures_unrestricted", "apoc.*") + .with_env("NEO4J_dbms_security_auth__minimum__password__length", "4") + .with_exposed_ports(PORT_BOLT_NEO4J) + ) + + def cleanup(): + container.stop() + + container.start() + wait_for_logs(container, "Started.") # wait_container_is_ready does not seem to be enough + request.addfinalizer(cleanup) + + return {PORT_BOLT_NEO4J: get_exposed_port(container, PORT_BOLT_NEO4J)} + + +@pytest.fixture(scope="session") +def rabbitmq_container(request: pytest.FixtureRequest, load_settings_before_session) -> dict[int, int] | None: + if not INFRAHUB_USE_TEST_CONTAINERS or config.SETTINGS.broker.driver != config.BrokerDriver.RabbitMQ: + return None + + container = ( + DockerContainer(image="rabbitmq:3.13.1-management") + .with_env("RABBITMQ_DEFAULT_USER", "infrahub") + .with_env("RABBITMQ_DEFAULT_PASS", "infrahub") + .with_exposed_ports(PORT_CLIENT_RABBITMQ, PORT_HTTP_RABBITMQ) + ) + + def cleanup(): + container.stop() + + container.start() + wait_for_logs(container, "Server startup complete;") # wait_container_is_ready does not seem to be enough + request.addfinalizer(cleanup) + + return { + PORT_CLIENT_RABBITMQ: get_exposed_port(container, PORT_CLIENT_RABBITMQ), + PORT_HTTP_RABBITMQ: get_exposed_port(container, PORT_HTTP_RABBITMQ), + } + + +@pytest.fixture(scope="module") +def rabbitmq(rabbitmq_container: dict[int, int] | None, reload_settings_before_each_module) -> dict[int, int] | None: + if ( + rabbitmq_container + and INFRAHUB_USE_TEST_CONTAINERS + and config.SETTINGS.broker.driver == config.BrokerDriver.RabbitMQ + ): + config.SETTINGS.broker.address = "localhost" + config.SETTINGS.broker.port = rabbitmq_container[PORT_CLIENT_RABBITMQ] + config.SETTINGS.broker.rabbitmq_http_port = rabbitmq_container[PORT_HTTP_RABBITMQ] + return rabbitmq_container + + return None + + +# NOTE: This fixture needs to run before initialize_lock_fixture which is guaranteed to run after as it has a module scope. +@pytest.fixture(scope="session") +def redis_container(request: pytest.FixtureRequest, load_settings_before_session) -> dict[int, int] | None: + if not INFRAHUB_USE_TEST_CONTAINERS or config.SETTINGS.cache.driver != config.CacheDriver.Redis: + return None + + container = DockerContainer(image="redis:latest").with_exposed_ports(PORT_REDIS) + + def cleanup(): + container.stop() + + container.start() + wait_for_logs(container, "Ready to accept connections tcp") # wait_container_is_ready does not seem to be enough + request.addfinalizer(cleanup) + + return {PORT_REDIS: get_exposed_port(container, PORT_REDIS)} + + +@pytest.fixture(scope="module") +def redis(redis_container: dict[int, int] | None, reload_settings_before_each_module) -> dict[int, int] | None: + if redis_container and INFRAHUB_USE_TEST_CONTAINERS and config.SETTINGS.cache.driver == config.CacheDriver.Redis: + config.SETTINGS.cache.address = "localhost" + config.SETTINGS.cache.port = redis_container[PORT_REDIS] + config.SETTINGS.cache.username = "" + config.SETTINGS.cache.password = "" + + return redis_container + + return None + + +def wait_for_memgraph_ready(host, port, timeout=15): + # Not retrieving host/port from config.SETTINGS here as they are set later in `db`fixture. + URI = f"{config.SETTINGS.database.protocol}://{host}:{port}" + + start_time = time.time() + + with GraphDatabase.driver(URI) as client: + while time.time() - start_time < timeout: + try: + client.verify_connectivity() + return True + except ServiceUnavailable: + time.sleep(1) + + raise TimeoutError(f"memgraph took more than {timeout} seconds to start.") + + +@pytest.fixture(scope="session") +def memgraph(request: pytest.FixtureRequest, load_settings_before_session) -> Optional[dict[int, int]]: + if not INFRAHUB_USE_TEST_CONTAINERS or config.SETTINGS.database.db_type != "memgraph": + return None + + memgraph_image = os.getenv("MEMGRAPH_DOCKER_IMAGE", "memgraph/memgraph-mage:1.19-memgraph-2.19-no-ml") + + container = ( + DockerContainer(image=memgraph_image, init=True) + .with_env("APP_CYPHER_QUERY_MAX_LEN", 10000) + .with_exposed_ports(PORT_MEMGRAPH) + ) + + def cleanup(): + container.stop() + + container.start() + + # Waiting for logs is not enough to make sure memgraph is available. + # Here we need to use DockerContainer.get_exposed_port instead of our own get_exposed_port + # in order to wait for the port to be exposed. This call is responsible for "Waiting for memgraph to be ready" logs. + # Note we still need to call wait_for_memgraph_ready to be sure the container is fully started. + wait_for_memgraph_ready(host="localhost", port=container.get_exposed_port(PORT_MEMGRAPH)) + + request.addfinalizer(cleanup) + + return {PORT_MEMGRAPH: get_exposed_port(container, PORT_MEMGRAPH)} + + +@pytest.fixture(scope="session") +def nats_container(request: pytest.FixtureRequest, load_settings_before_session) -> Optional[dict[int, int]]: + if not INFRAHUB_USE_TEST_CONTAINERS or config.SETTINGS.cache.driver != config.CacheDriver.NATS: + return None + + container = DockerContainer(image="nats:alpine").with_command("--jetstream").with_exposed_ports(PORT_NATS) + + def cleanup(): + container.stop() + + container.start() + wait_for_logs(container, "Server is ready") # wait_container_is_ready does not seem to be enough + request.addfinalizer(cleanup) + + return {PORT_NATS: get_exposed_port(container, PORT_NATS)} + + +@pytest.fixture(scope="module") +def nats(nats_container: dict[int, int] | None, reload_settings_before_each_module) -> dict[int, int] | None: + if nats_container and INFRAHUB_USE_TEST_CONTAINERS and config.SETTINGS.cache.driver == config.CacheDriver.NATS: + config.SETTINGS.cache.address = "localhost" + config.SETTINGS.cache.port = nats_container[PORT_NATS] + + if nats_container and INFRAHUB_USE_TEST_CONTAINERS and config.SETTINGS.broker.driver == config.BrokerDriver.NATS: + config.SETTINGS.broker.address = "localhost" + config.SETTINGS.broker.port = nats_container[PORT_NATS] + + return nats_container + + return None + + +@pytest.fixture(scope="session") +def prefect_container(request: pytest.FixtureRequest, load_settings_before_session) -> Optional[dict[int, int]]: + if not INFRAHUB_USE_TEST_CONTAINERS: + return None + + container = ( + DockerContainer(image="prefecthq/prefect:3.0.3-python3.12") + .with_command("prefect server start --host 0.0.0.0 --ui") + .with_exposed_ports(PORT_PREFECT) + ) + + def cleanup(): + container.stop() + + container.start() + wait_for_logs(container, "Configure Prefect to communicate with the server") + request.addfinalizer(cleanup) + + return {PORT_PREFECT: get_exposed_port(container, PORT_PREFECT)} + + +@pytest.fixture(scope="module") +def prefect(prefect_container: dict[int, int] | None, reload_settings_before_each_module) -> Generator[str, None, None]: + if prefect_container: + server_port = prefect_container[PORT_PREFECT] + server_api_url = f"http://localhost:{server_port}/api" + else: + server_api_url = f"http://localhost:{PORT_PREFECT}/api" + + with ExitStack() as stack: + stack.enter_context( + prefect_settings.temporary_settings( + updates={ + prefect_settings.PREFECT_API_URL: server_api_url, + } + ) + ) + yield server_api_url + + +@pytest.fixture(scope="session", autouse=True) +def load_settings_before_session(): + load_and_exit() + + @pytest.fixture(scope="module", autouse=True) -def execute_before_any_test(worker_id, tmpdir_factory): - config.load_and_exit() +def reload_settings_before_each_module(tmpdir_factory): + # Settings need to be reloaded between each test module, as some module might modify settings that might break tests within other modules. + load_and_exit() + # Other settings config.SETTINGS.storage.driver = config.StorageDriver.FileSystemStorage - if TEST_IN_DOCKER: - try: - db_id = int(worker_id[2]) + 1 - except (ValueError, IndexError): - db_id = 1 - - config.SETTINGS.cache.address = f"{BUILD_NAME}-cache-{db_id}" - if config.SETTINGS.cache.driver == config.CacheDriver.NATS: - config.SETTINGS.cache.address = f"{BUILD_NAME}-message-queue-{db_id}" - config.SETTINGS.database.address = f"{BUILD_NAME}-database-{db_id}" - config.SETTINGS.broker.address = f"{BUILD_NAME}-message-queue-{db_id}" - config.SETTINGS.storage.local = config.FileSystemStorageSettings(path="/opt/infrahub/storage") - else: - storage_dir = tmpdir_factory.mktemp("storage") - config.SETTINGS.storage.local._path = str(storage_dir) + storage_dir = tmpdir_factory.mktemp("storage") + config.SETTINGS.storage.local.path_ = str(storage_dir) config.SETTINGS.broker.enable = False config.SETTINGS.cache.enable = True @@ -162,7 +419,7 @@ def execute_before_any_test(worker_id, tmpdir_factory): config.SETTINGS.main.internal_address = "http://mock" config.OVERRIDE.message_bus = BusRecorder() - initialize_lock() + initialize_lock(local_only=True) @pytest.fixture diff --git a/backend/tests/fixtures/schemas/core_schema_01.json b/backend/tests/fixtures/schemas/core_schema_01.json index 05f96e3f1b..25cbcae155 100644 --- a/backend/tests/fixtures/schemas/core_schema_01.json +++ b/backend/tests/fixtures/schemas/core_schema_01.json @@ -40,18 +40,6 @@ "optional": true, "cardinality": "one" } - ], - "filters": [ - { - "name": "name__value", - "kind": "String", - "description": null - }, - { - "name": "location__value", - "kind": "String", - "description": null - } ] }, { @@ -126,52 +114,13 @@ "inherited": false, "cardinality": "many", "branch": "aware", - "optional": true, - "filters": [ - { - "name": "id", - "kind": "String", - "description": null - }, - { - "name": "name__value", - "kind": "String", - "description": null - }, - { - "name": "description__value", - "kind": "String", - "description": null - } - ] + "optional": true } ], "label": null, "inherit_from": [], "branch": "aware", - "default_filter": "name__value", - "filters": [ - { - "name": "ids", - "kind": "List", - "description": null - }, - { - "name": "query__value", - "kind": "String", - "description": null - }, - { - "name": "description__value", - "kind": "String", - "description": null - }, - { - "name": "name__value", - "kind": "String", - "description": null - } - ] + "default_filter": "name__value" }, { "name": "Repository", @@ -255,24 +204,7 @@ "inherited": false, "cardinality": "many", "branch": "aware", - "optional": true, - "filters": [ - { - "name": "id", - "kind": "String", - "description": null - }, - { - "name": "name__value", - "kind": "String", - "description": null - }, - { - "name": "description__value", - "kind": "String", - "description": null - } - ] + "optional": true }, { "name": "queries", @@ -283,29 +215,7 @@ "inherited": false, "cardinality": "many", "branch": "aware", - "optional": true, - "filters": [ - { - "name": "id", - "kind": "String", - "description": null - }, - { - "name": "query__value", - "kind": "String", - "description": null - }, - { - "name": "description__value", - "kind": "String", - "description": null - }, - { - "name": "name__value", - "kind": "String", - "description": null - } - ] + "optional": true }, { "name": "credential", @@ -323,54 +233,7 @@ "CoreGenericRepository" ], "branch": "aware", - "default_filter": "name__value", - "filters": [ - { - "name": "ids", - "kind": "List", - "description": null - }, - { - "name": "username__value", - "kind": "String", - "description": null - }, - { - "name": "type__value", - "kind": "String", - "description": null - }, - { - "name": "name__value", - "kind": "String", - "description": null - }, - { - "name": "description__value", - "kind": "String", - "description": null - }, - { - "name": "commit__value", - "kind": "String", - "description": null - }, - { - "name": "location__value", - "kind": "String", - "description": null - }, - { - "name": "password__value", - "kind": "String", - "description": null - }, - { - "name": "default_branch__value", - "kind": "String", - "description": null - } - ] + "default_filter": "name__value" }, { "name": "PasswordCredential", @@ -400,13 +263,6 @@ "branch": "aware", "optional": false } - ], - "filters": [ - { - "name": "ids", - "kind": "List", - "description": null - } ] }, { @@ -441,24 +297,7 @@ "label": null, "inherit_from": [], "branch": "aware", - "default_filter": "name__value", - "filters": [ - { - "name": "ids", - "kind": "List", - "description": null - }, - { - "name": "name__value", - "kind": "String", - "description": null - }, - { - "name": "description__value", - "kind": "String", - "description": null - } - ] + "default_filter": "name__value" }, { "name": "Location", diff --git a/backend/tests/fixtures/schemas/schema_01.json b/backend/tests/fixtures/schemas/schema_01.json index d92529600c..51e76c112a 100644 --- a/backend/tests/fixtures/schemas/schema_01.json +++ b/backend/tests/fixtures/schemas/schema_01.json @@ -49,52 +49,13 @@ "inherited": false, "cardinality": "many", "branch": "aware", - "optional": true, - "filters": [ - { - "name": "id", - "kind": "String", - "description": null - }, - { - "name": "name__value", - "kind": "String", - "description": null - }, - { - "name": "description__value", - "kind": "String", - "description": null - } - ] + "optional": true } ], "label": null, "inherit_from": [], "branch": "aware", - "default_filter": "name__value", - "filters": [ - { - "name": "ids", - "kind": "List", - "description": null - }, - { - "name": "query__value", - "kind": "String", - "description": null - }, - { - "name": "description__value", - "kind": "String", - "description": null - }, - { - "name": "name__value", - "kind": "String", - "description": null - } - ] + "default_filter": "name__value" }, { "name": "Repository", @@ -211,24 +172,7 @@ "inherited": false, "cardinality": "many", "branch": "aware", - "optional": true, - "filters": [ - { - "name": "id", - "kind": "String", - "description": null - }, - { - "name": "name__value", - "kind": "String", - "description": null - }, - { - "name": "description__value", - "kind": "String", - "description": null - } - ] + "optional": true }, { "name": "queries", @@ -239,29 +183,7 @@ "inherited": false, "cardinality": "many", "branch": "aware", - "optional": true, - "filters": [ - { - "name": "id", - "kind": "String", - "description": null - }, - { - "name": "query__value", - "kind": "String", - "description": null - }, - { - "name": "description__value", - "kind": "String", - "description": null - }, - { - "name": "name__value", - "kind": "String", - "description": null - } - ] + "optional": true } ], "label": null, @@ -270,54 +192,7 @@ "DataSource" ], "branch": "aware", - "default_filter": "name__value", - "filters": [ - { - "name": "ids", - "kind": "List", - "description": null - }, - { - "name": "username__value", - "kind": "String", - "description": null - }, - { - "name": "type__value", - "kind": "String", - "description": null - }, - { - "name": "name__value", - "kind": "String", - "description": null - }, - { - "name": "description__value", - "kind": "String", - "description": null - }, - { - "name": "commit__value", - "kind": "String", - "description": null - }, - { - "name": "location__value", - "kind": "String", - "description": null - }, - { - "name": "password__value", - "kind": "String", - "description": null - }, - { - "name": "default_branch__value", - "kind": "String", - "description": null - } - ] + "default_filter": "name__value" }, { "name": "Tag", @@ -351,24 +226,7 @@ "label": null, "inherit_from": [], "branch": "aware", - "default_filter": "name__value", - "filters": [ - { - "name": "ids", - "kind": "List", - "description": null - }, - { - "name": "name__value", - "kind": "String", - "description": null - }, - { - "name": "description__value", - "kind": "String", - "description": null - } - ] + "default_filter": "name__value" }, { "name": "Location", diff --git a/backend/tests/fixtures/schemas/schema_02.json b/backend/tests/fixtures/schemas/schema_02.json index 28ddde01eb..e75017a00a 100644 --- a/backend/tests/fixtures/schemas/schema_02.json +++ b/backend/tests/fixtures/schemas/schema_02.json @@ -77,15 +77,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 4000 }, { @@ -99,15 +90,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 5000 } ], @@ -116,36 +98,6 @@ "CoreGroup" ], "branch": "aware", - "filters": [ - { - "name": "ids", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "kind": "CoreStandardGroup" }, { @@ -241,15 +193,6 @@ "cardinality": "one", "branch": "aware", "optional": false, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 4000 }, { @@ -263,15 +206,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 4000 }, { @@ -285,15 +219,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 5000 } ], @@ -302,36 +227,6 @@ "CoreGroup" ], "branch": "local", - "filters": [ - { - "name": "ids", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "kind": "CoreGraphQLQueryGroup" }, { @@ -423,29 +318,6 @@ "cardinality": "one", "branch": "aware", "optional": false, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "height__value", - "kind": "Number", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 5000 }, { @@ -459,36 +331,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 6000 }, { @@ -502,36 +344,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 7000 } ], @@ -540,43 +352,6 @@ "TestCar" ], "branch": "aware", - "filters": [ - { - "name": "ids", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "nbr_engine__value", - "kind": "Number", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "nbr_seats__value", - "kind": "Number", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "color__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "kind": "TestElectricCar" }, { @@ -668,29 +443,6 @@ "cardinality": "one", "branch": "aware", "optional": false, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "height__value", - "kind": "Number", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 5000 }, { @@ -704,36 +456,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 6000 }, { @@ -747,36 +469,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 7000 } ], @@ -785,43 +477,6 @@ "TestCar" ], "branch": "aware", - "filters": [ - { - "name": "ids", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "mpg__value", - "kind": "Number", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "nbr_seats__value", - "kind": "Number", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "color__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "kind": "TestGazCar" }, { @@ -881,36 +536,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "nbr_seats__value", - "kind": "Number", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "color__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 3000 }, { @@ -924,36 +549,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 4000 }, { @@ -967,65 +562,12 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 5000 } ], "label": null, "inherit_from": [], "branch": "aware", - "filters": [ - { - "name": "ids", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "height__value", - "kind": "Number", - "enum": null, - "object_kind": null, - "description": null - } - ], "kind": "TestPerson" }, { @@ -1115,36 +657,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 4000 }, { @@ -1158,83 +670,12 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 5000 } ], "label": "Criticality", "inherit_from": [], "branch": "aware", - "filters": [ - { - "name": "ids", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "level__value", - "kind": "Number", - "enum": [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10 - ], - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "kind": "BuiltinCriticality" }, { @@ -1296,36 +737,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 3000 }, { @@ -1339,65 +750,12 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 4000 } ], "label": "Tag", "inherit_from": [], "branch": "aware", - "filters": [ - { - "name": "ids", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "kind": "BuiltinTag" }, { @@ -1476,29 +834,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 4000 }, { @@ -1512,36 +847,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 5000 }, { @@ -1555,79 +860,12 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 6000 } ], "label": "Organization", "inherit_from": [], "branch": "aware", - "filters": [ - { - "name": "ids", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "tags__id", - "kind": "Object", - "enum": null, - "object_kind": "BuiltinTag", - "description": null - } - ], "kind": "CoreOrganization" }, { @@ -1766,29 +1004,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "token__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 7000 }, { @@ -1802,36 +1017,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 3000 }, { @@ -1845,36 +1030,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 4000 } ], @@ -1884,59 +1039,6 @@ "LineageSource" ], "branch": "aware", - "filters": [ - { - "name": "ids", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "type__value", - "kind": "Text", - "enum": [ - "User", - "Script", - "Bot", - "Git" - ], - "object_kind": null, - "description": null - }, - { - "name": "role__value", - "kind": "Text", - "enum": [ - "admin", - "read-only", - "read-write" - ], - "object_kind": null, - "description": null - } - ], "kind": "CoreAccount" }, { @@ -2013,59 +1115,6 @@ "cardinality": "one", "branch": "aware", "optional": false, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "type__value", - "kind": "Text", - "enum": [ - "User", - "Script", - "Bot", - "Git" - ], - "object_kind": null, - "description": null - }, - { - "name": "role__value", - "kind": "Text", - "enum": [ - "admin", - "read-only", - "read-write" - ], - "object_kind": null, - "description": null - } - ], "order_weight": 4000 }, { @@ -2079,36 +1128,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 5000 }, { @@ -2122,65 +1141,12 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 6000 } ], "label": "Account Token", "inherit_from": [], "branch": "aware", - "filters": [ - { - "name": "ids", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "token__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "kind": "InternalAccountToken" }, { @@ -2221,59 +1187,6 @@ "cardinality": "one", "branch": "aware", "optional": false, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "type__value", - "kind": "Text", - "enum": [ - "User", - "Script", - "Bot", - "Git" - ], - "object_kind": null, - "description": null - }, - { - "name": "role__value", - "kind": "Text", - "enum": [ - "admin", - "read-only", - "read-write" - ], - "object_kind": null, - "description": null - } - ], "order_weight": 2000 }, { @@ -2287,36 +1200,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 3000 }, { @@ -2330,51 +1213,12 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 4000 } ], "label": "Refresh Token", "inherit_from": [], "branch": "aware", - "filters": [ - { - "name": "ids", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "kind": "InternalRefreshToken" }, { @@ -2451,59 +1295,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "type__value", - "kind": "Text", - "enum": [ - "User", - "Script", - "Bot", - "Git" - ], - "object_kind": null, - "description": null - }, - { - "name": "role__value", - "kind": "Text", - "enum": [ - "admin", - "read-only", - "read-write" - ], - "object_kind": null, - "description": null - } - ], "order_weight": 4000 }, { @@ -2517,59 +1308,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "type__value", - "kind": "Text", - "enum": [ - "User", - "Script", - "Bot", - "Git" - ], - "object_kind": null, - "description": null - }, - { - "name": "role__value", - "kind": "Text", - "enum": [ - "admin", - "read-only", - "read-write" - ], - "object_kind": null, - "description": null - } - ], "order_weight": 5000 }, { @@ -2583,59 +1321,6 @@ "cardinality": "one", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "type__value", - "kind": "Text", - "enum": [ - "User", - "Script", - "Bot", - "Git" - ], - "object_kind": null, - "description": null - }, - { - "name": "role__value", - "kind": "Text", - "enum": [ - "admin", - "read-only", - "read-write" - ], - "object_kind": null, - "description": null - } - ], "order_weight": 6000 }, { @@ -2649,15 +1334,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 7000 }, { @@ -2671,22 +1347,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "resolved__value", - "kind": "Boolean", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 8000 }, { @@ -2700,36 +1360,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 9000 }, { @@ -2743,103 +1373,29 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 10000 } ], "label": "Proposed Change", "inherit_from": [], "branch": "aware", - "filters": [ + "kind": "CoreProposedChange" + }, + { + "name": "ChangeThread", + "namespace": "Core", + "description": "A thread on proposed change", + "default_filter": null, + "order_by": null, + "display_labels": null, + "attributes": [ { - "name": "ids", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "source_branch__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "destination_branch__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "approved_by__id", - "kind": "Object", - "enum": null, - "object_kind": "CoreAccount", - "description": null - }, - { - "name": "reviewers__id", - "kind": "Object", - "enum": null, - "object_kind": "CoreAccount", - "description": null - } - ], - "kind": "CoreProposedChange" - }, - { - "name": "ChangeThread", - "namespace": "Core", - "description": "A thread on proposed change", - "default_filter": null, - "order_by": null, - "display_labels": null, - "attributes": [ - { - "name": "resolved", - "kind": "Boolean", - "namespace": "Attribute", - "label": null, - "description": null, - "default_value": false, + "name": "resolved", + "kind": "Boolean", + "namespace": "Attribute", + "label": null, + "description": null, + "default_value": false, "enum": null, "regex": null, "max_length": null, @@ -2880,36 +1436,6 @@ "cardinality": "one", "branch": "aware", "optional": false, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "source_branch__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "destination_branch__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 3000 }, { @@ -2923,15 +1449,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 4000 }, { @@ -2945,59 +1462,6 @@ "cardinality": "one", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "type__value", - "kind": "Text", - "enum": [ - "User", - "Script", - "Bot", - "Git" - ], - "object_kind": null, - "description": null - }, - { - "name": "role__value", - "kind": "Text", - "enum": [ - "admin", - "read-only", - "read-write" - ], - "object_kind": null, - "description": null - } - ], "order_weight": 5000 }, { @@ -3011,36 +1475,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 6000 }, { @@ -3054,36 +1488,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 7000 } ], @@ -3092,29 +1496,6 @@ "CoreThread" ], "branch": "aware", - "filters": [ - { - "name": "ids", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "resolved__value", - "kind": "Boolean", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "change__id", - "kind": "Object", - "enum": null, - "object_kind": "CoreProposedChange", - "description": null - } - ], "kind": "CoreChangeThread" }, { @@ -3223,64 +1604,6 @@ "cardinality": "one", "branch": "aware", "optional": false, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "location__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "default_branch__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "commit__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "username__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "password__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 6000 }, { @@ -3294,36 +1617,6 @@ "cardinality": "one", "branch": "aware", "optional": false, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "source_branch__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "destination_branch__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 7000 }, { @@ -3337,15 +1630,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 8000 }, { @@ -3359,59 +1643,6 @@ "cardinality": "one", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "type__value", - "kind": "Text", - "enum": [ - "User", - "Script", - "Bot", - "Git" - ], - "object_kind": null, - "description": null - }, - { - "name": "role__value", - "kind": "Text", - "enum": [ - "admin", - "read-only", - "read-write" - ], - "object_kind": null, - "description": null - } - ], "order_weight": 9000 }, { @@ -3425,36 +1656,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 10000 }, { @@ -3468,36 +1669,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 11000 } ], @@ -3506,50 +1677,6 @@ "CoreThread" ], "branch": "aware", - "filters": [ - { - "name": "ids", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "file__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "commit__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "line_number__value", - "kind": "Number", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "resolved__value", - "kind": "Boolean", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "change__id", - "kind": "Object", - "enum": null, - "object_kind": "CoreProposedChange", - "description": null - } - ], "kind": "CoreFileThread" }, { @@ -3624,36 +1751,6 @@ "cardinality": "one", "branch": "aware", "optional": false, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "source_branch__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "destination_branch__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 4000 }, { @@ -3667,15 +1764,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 5000 }, { @@ -3689,59 +1777,6 @@ "cardinality": "one", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "type__value", - "kind": "Text", - "enum": [ - "User", - "Script", - "Bot", - "Git" - ], - "object_kind": null, - "description": null - }, - { - "name": "role__value", - "kind": "Text", - "enum": [ - "admin", - "read-only", - "read-write" - ], - "object_kind": null, - "description": null - } - ], "order_weight": 6000 }, { @@ -3755,36 +1790,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 7000 }, { @@ -3798,36 +1803,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 8000 } ], @@ -3836,36 +1811,6 @@ "CoreThread" ], "branch": "aware", - "filters": [ - { - "name": "ids", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "object_path__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "resolved__value", - "kind": "Boolean", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "change__id", - "kind": "Object", - "enum": null, - "object_kind": "CoreProposedChange", - "description": null - } - ], "kind": "CoreObjectThread" }, { @@ -3925,36 +1870,6 @@ "cardinality": "one", "branch": "aware", "optional": false, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "source_branch__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "destination_branch__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 3000 }, { @@ -3968,59 +1883,6 @@ "cardinality": "one", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "type__value", - "kind": "Text", - "enum": [ - "User", - "Script", - "Bot", - "Git" - ], - "object_kind": null, - "description": null - }, - { - "name": "role__value", - "kind": "Text", - "enum": [ - "admin", - "read-only", - "read-write" - ], - "object_kind": null, - "description": null - } - ], "order_weight": 4000 }, { @@ -4034,36 +1896,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 5000 }, { @@ -4077,36 +1909,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 6000 } ], @@ -4115,22 +1917,6 @@ "CoreComment" ], "branch": "aware", - "filters": [ - { - "name": "ids", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "change__id", - "kind": "Object", - "enum": null, - "object_kind": "CoreProposedChange", - "description": null - } - ], "kind": "CoreChangeComment" }, { @@ -4190,22 +1976,6 @@ "cardinality": "one", "branch": "aware", "optional": false, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "resolved__value", - "kind": "Boolean", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 3000 }, { @@ -4219,59 +1989,6 @@ "cardinality": "one", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "type__value", - "kind": "Text", - "enum": [ - "User", - "Script", - "Bot", - "Git" - ], - "object_kind": null, - "description": null - }, - { - "name": "role__value", - "kind": "Text", - "enum": [ - "admin", - "read-only", - "read-write" - ], - "object_kind": null, - "description": null - } - ], "order_weight": 4000 }, { @@ -4285,36 +2002,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 5000 }, { @@ -4328,36 +2015,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 6000 } ], @@ -4366,22 +2023,6 @@ "CoreComment" ], "branch": "aware", - "filters": [ - { - "name": "ids", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "thread__id", - "kind": "Object", - "enum": null, - "object_kind": "CoreThread", - "description": null - } - ], "kind": "CoreThreadComment" }, { @@ -4460,36 +2101,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 4000 }, { @@ -4503,72 +2114,12 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 5000 } ], "label": "Status", "inherit_from": [], "branch": "aware", - "filters": [ - { - "name": "ids", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "kind": "BuiltinStatus" }, { @@ -4647,36 +2198,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 4000 }, { @@ -4690,72 +2211,12 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 5000 } ], "label": "Role", "inherit_from": [], "branch": "aware", - "filters": [ - { - "name": "ids", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "kind": "BuiltinRole" }, { @@ -4834,29 +2295,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 4000 }, { @@ -4870,36 +2308,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 5000 }, { @@ -4913,79 +2321,12 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 6000 } ], "label": "Location", "inherit_from": [], "branch": "aware", - "filters": [ - { - "name": "ids", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "type__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "tags__id", - "kind": "Object", - "enum": null, - "object_kind": "BuiltinTag", - "description": null - } - ], "kind": "InfraSite" }, { @@ -5132,59 +2473,6 @@ "cardinality": "one", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "type__value", - "kind": "Text", - "enum": [ - "User", - "Script", - "Bot", - "Git" - ], - "object_kind": null, - "description": null - }, - { - "name": "role__value", - "kind": "Text", - "enum": [ - "admin", - "read-only", - "read-write" - ], - "object_kind": null, - "description": null - } - ], "order_weight": 8000 }, { @@ -5198,29 +2486,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 9000 }, { @@ -5234,50 +2499,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "template_path__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "timeout__value", - "kind": "Number", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 10000 }, { @@ -5291,29 +2512,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 11000 }, { @@ -5327,50 +2525,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "file_path__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "class_name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "timeout__value", - "kind": "Number", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 12000 }, { @@ -5384,64 +2538,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "file_path__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "class_name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "url__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "timeout__value", - "kind": "Number", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 13000 }, { @@ -5455,36 +2551,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 3000 }, { @@ -5498,36 +2564,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 4000 } ], @@ -5537,78 +2573,6 @@ "LineageSource" ], "branch": "aware", - "filters": [ - { - "name": "ids", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "location__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "default_branch__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "commit__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "username__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "password__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "account__id", - "kind": "Object", - "enum": null, - "object_kind": "CoreAccount", - "description": null - }, - { - "name": "tags__id", - "kind": "Object", - "enum": null, - "object_kind": "BuiltinTag", - "description": null - } - ], "kind": "CoreRepository" }, { @@ -5721,64 +2685,6 @@ "cardinality": "one", "branch": "aware", "optional": false, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "location__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "default_branch__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "commit__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "username__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "password__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 7000 }, { @@ -5792,29 +2698,6 @@ "cardinality": "one", "branch": "aware", "optional": false, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 8000 }, { @@ -5828,29 +2711,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 9000 }, { @@ -5864,36 +2724,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 10000 }, { @@ -5907,36 +2737,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 11000 } ], @@ -5945,71 +2745,6 @@ "CoreTransformation" ], "branch": "aware", - "filters": [ - { - "name": "ids", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "template_path__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "timeout__value", - "kind": "Number", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "repository__id", - "kind": "Object", - "enum": null, - "object_kind": "CoreRepository", - "description": null - }, - { - "name": "query__id", - "kind": "Object", - "enum": null, - "object_kind": "CoreGraphQLQuery", - "description": null - }, - { - "name": "tags__id", - "kind": "Object", - "enum": null, - "object_kind": "BuiltinTag", - "description": null - } - ], "kind": "CoreTransformJinja2" }, { @@ -6127,64 +2862,6 @@ "cardinality": "one", "branch": "aware", "optional": false, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "location__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "default_branch__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "commit__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "username__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "password__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 7000 }, { @@ -6198,29 +2875,6 @@ "cardinality": "one", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 8000 }, { @@ -6234,29 +2888,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 9000 }, { @@ -6270,36 +2901,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 10000 }, { @@ -6313,107 +2914,12 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 11000 } ], "label": "Check Definition", "inherit_from": [], "branch": "aware", - "filters": [ - { - "name": "ids", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "file_path__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "class_name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "timeout__value", - "kind": "Number", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "repository__id", - "kind": "Object", - "enum": null, - "object_kind": "CoreRepository", - "description": null - }, - { - "name": "query__id", - "kind": "Object", - "enum": null, - "object_kind": "CoreGraphQLQuery", - "description": null - }, - { - "name": "tags__id", - "kind": "Object", - "enum": null, - "object_kind": "BuiltinTag", - "description": null - } - ], "kind": "CoreCheckDefinition" }, { @@ -6560,64 +3066,6 @@ "cardinality": "one", "branch": "aware", "optional": false, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "location__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "default_branch__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "commit__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "username__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "password__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 9000 }, { @@ -6631,29 +3079,6 @@ "cardinality": "one", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 10000 }, { @@ -6667,29 +3092,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 11000 }, { @@ -6703,36 +3105,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 12000 }, { @@ -6746,36 +3118,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 13000 } ], @@ -6784,85 +3126,6 @@ "CoreTransformation" ], "branch": "aware", - "filters": [ - { - "name": "ids", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "file_path__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "class_name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "url__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "timeout__value", - "kind": "Number", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "repository__id", - "kind": "Object", - "enum": null, - "object_kind": "CoreRepository", - "description": null - }, - { - "name": "query__id", - "kind": "Object", - "enum": null, - "object_kind": "CoreGraphQLQuery", - "description": null - }, - { - "name": "tags__id", - "kind": "Object", - "enum": null, - "object_kind": "BuiltinTag", - "description": null - } - ], "kind": "CoreTransformPython" }, { @@ -6941,64 +3204,6 @@ "cardinality": "one", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "location__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "default_branch__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "commit__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "username__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "password__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 4000 }, { @@ -7012,29 +3217,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 5000 }, { @@ -7048,36 +3230,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 6000 }, { @@ -7091,79 +3243,12 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 7000 } ], "label": "GraphQL Query", "inherit_from": [], "branch": "aware", - "filters": [ - { - "name": "ids", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "repository__id", - "kind": "Object", - "enum": null, - "object_kind": "CoreRepository", - "description": null - }, - { - "name": "tags__id", - "kind": "Object", - "enum": null, - "object_kind": "BuiltinTag", - "description": null - } - ], "kind": "CoreGraphQLQuery" }, { @@ -7256,7 +3341,12 @@ "label": null, "description": null, "default_value": null, - "enum": ["Error", "Ready", "Pending", "Processing"], + "enum": [ + "Error", + "Ready", + "Pending", + "Processing" + ], "regex": null, "max_length": null, "min_length": null, @@ -7296,15 +3386,6 @@ "cardinality": "one", "branch": "aware", "optional": false, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 6000 }, { @@ -7318,46 +3399,6 @@ "cardinality": "one", "branch": "aware", "optional": false, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "artifact_name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "content_type__value", - "kind": "Text", - "enum": [ - "application/json", - "text/plain" - ], - "object_kind": null, - "description": null - } - ], "order_weight": 7000 }, { @@ -7371,29 +3412,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 8000 }, { @@ -7407,36 +3425,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 9000 }, { @@ -7450,96 +3438,12 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 10000 } ], "label": "Artifact", "inherit_from": [], "branch": "aware", - "filters": [ - { - "name": "ids", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "content_type__value", - "kind": "Text", - "enum": [ - "application/json", - "text/plain" - ], - "object_kind": null, - "description": null - }, - { - "name": "checksum__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "object__ids", - "kind": "Text", - "enum": null, - "object_kind": "CoreNode", - "description": null - }, - { - "name": "definition__ids", - "kind": "Text", - "enum": null, - "object_kind": "CoreArtifactDefinition", - "description": null - }, - { - "name": "tags__ids", - "kind": "Text", - "enum": null, - "object_kind": "BuiltinTag", - "description": null - } - ], "kind": "CoreArtifact" }, { @@ -7655,36 +3559,6 @@ "cardinality": "one", "branch": "aware", "optional": false, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 6000 }, { @@ -7698,43 +3572,6 @@ "cardinality": "one", "branch": "aware", "optional": false, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "timeout__value", - "kind": "Number", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 7000 }, { @@ -7748,36 +3585,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 8000 }, { @@ -7791,96 +3598,12 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [ - { - "name": "id", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "label__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 9000 } ], "label": "Artifact Definition", "inherit_from": [], "branch": "aware", - "filters": [ - { - "name": "ids", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "artifact_name__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "content_type__value", - "kind": "Text", - "enum": [ - "application/json", - "text/plain" - ], - "object_kind": null, - "description": null - }, - { - "name": "targets__id", - "kind": "Object", - "enum": null, - "object_kind": "CoreGroup", - "description": null - }, - { - "name": "transformation__id", - "kind": "Object", - "enum": null, - "object_kind": "CoreTransformation", - "description": null - } - ], "kind": "CoreArtifactDefinition" } ], @@ -7942,7 +3665,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [], "order_weight": 3000 }, { @@ -7956,7 +3678,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [], "order_weight": 4000 } ], @@ -8025,7 +3746,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [], "order_weight": 3000 }, { @@ -8039,7 +3759,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [], "order_weight": 4000 } ], @@ -8128,7 +3847,6 @@ "cardinality": "one", "branch": "aware", "optional": false, - "filters": [], "order_weight": 4000 }, { @@ -8142,7 +3860,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [], "order_weight": 5000 }, { @@ -8156,7 +3873,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [], "order_weight": 6000 } ], @@ -8244,7 +3960,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [], "order_weight": 4000 }, { @@ -8258,7 +3973,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [], "order_weight": 5000 } ], @@ -8289,7 +4003,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [], "order_weight": 1000 }, { @@ -8303,7 +4016,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [], "order_weight": 2000 } ], @@ -8371,7 +4083,6 @@ "cardinality": "one", "branch": "aware", "optional": true, - "filters": [], "order_weight": 3000 }, { @@ -8385,7 +4096,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [], "order_weight": 4000 }, { @@ -8399,7 +4109,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [], "order_weight": 5000 } ], @@ -8470,7 +4179,6 @@ "cardinality": "one", "branch": "aware", "optional": false, - "filters": [], "order_weight": 3000 }, { @@ -8484,7 +4192,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [], "order_weight": 4000 }, { @@ -8498,7 +4205,6 @@ "cardinality": "one", "branch": "aware", "optional": true, - "filters": [], "order_weight": 5000 }, { @@ -8512,7 +4218,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [], "order_weight": 6000 }, { @@ -8526,7 +4231,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [], "order_weight": 7000 } ], @@ -8632,7 +4336,6 @@ "cardinality": "one", "branch": "aware", "optional": false, - "filters": [], "order_weight": 6000 }, { @@ -8646,7 +4349,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [], "order_weight": 7000 }, { @@ -8660,7 +4362,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [], "order_weight": 8000 }, { @@ -8674,7 +4375,6 @@ "cardinality": "many", "branch": "aware", "optional": true, - "filters": [], "order_weight": 9000 } ], @@ -8687,4 +4387,4 @@ "kind": "CoreTransformation" } ] -} +} \ No newline at end of file diff --git a/backend/tests/helpers/graphql.py b/backend/tests/helpers/graphql.py index fa769af2e0..805991a8ad 100644 --- a/backend/tests/helpers/graphql.py +++ b/backend/tests/helpers/graphql.py @@ -5,7 +5,7 @@ from graphql import ExecutionResult, graphql from infrahub.core.branch import Branch -from infrahub.graphql import prepare_graphql_params +from infrahub.graphql.initialization import prepare_graphql_params from infrahub.services import InfrahubServices, services if TYPE_CHECKING: diff --git a/backend/tests/helpers/schema/__init__.py b/backend/tests/helpers/schema/__init__.py index 0096e224d3..fb283c5372 100644 --- a/backend/tests/helpers/schema/__init__.py +++ b/backend/tests/helpers/schema/__init__.py @@ -27,4 +27,4 @@ async def load_schema(db: InfrahubDatabase, schema: SchemaRoot) -> None: await registry.schema.update_schema_branch(schema=tmp_schema, db=db, branch=default_branch_name, update_db=True) -__all__ = ["CAR", "MANUFACTURER", "PERSON", "CAR_SCHEMA", "TICKET"] +__all__ = ["CAR", "CAR_SCHEMA", "MANUFACTURER", "PERSON", "TICKET"] diff --git a/backend/tests/helpers/test_app.py b/backend/tests/helpers/test_app.py index cb6cfe66b5..c5eb28f241 100644 --- a/backend/tests/helpers/test_app.py +++ b/backend/tests/helpers/test_app.py @@ -12,13 +12,17 @@ create_default_branch, create_global_branch, create_root_node, + create_super_administrator_role, + create_super_administrators_group, initialization, ) from infrahub.core.schema import SchemaRoot, core_models, internal_schema -from infrahub.core.schema_manager import SchemaBranch, SchemaManager +from infrahub.core.schema.manager import SchemaManager +from infrahub.core.schema.schema_branch import SchemaBranch from infrahub.core.utils import delete_all_nodes from infrahub.database import InfrahubDatabase from infrahub.server import app, app_initialization +from infrahub.services.adapters.workflow.local import WorkflowLocalExecution from tests.adapters.message_bus import BusSimulator from .test_client import InfrahubTestClient @@ -60,6 +64,14 @@ def bus_simulator(self, db: InfrahubDatabase) -> Generator[BusSimulator, None, N yield bus config.OVERRIDE.message_bus = original + @pytest.fixture(scope="class", autouse=True) + def workflow_local(self) -> Generator[WorkflowLocalExecution, None, None]: + original = config.OVERRIDE.workflow + workflow = WorkflowLocalExecution() + config.OVERRIDE.workflow = workflow + yield workflow + config.OVERRIDE.workflow = original + @pytest.fixture(scope="class") async def register_internal_schema(self, db: InfrahubDatabase, default_branch: Branch) -> SchemaBranch: schema = SchemaRoot(**internal_schema) @@ -80,8 +92,7 @@ async def register_core_schema( @pytest.fixture(scope="class") async def test_client( - self, - initialize_registry: None, + self, initialize_registry: None, redis: dict[int, int] | None, nats: dict[int, int] | None ) -> InfrahubTestClient: await app_initialization(app) return InfrahubTestClient(app=app) @@ -104,11 +115,10 @@ async def client( async def initialize_registry( self, db: InfrahubDatabase, register_core_schema: SchemaBranch, bus_simulator: BusSimulator, api_token: str ) -> None: - await create_account( - db=db, - name="admin", - password=config.SETTINGS.initial.admin_password, - token_value=api_token, + admin_account = await create_account( + db=db, name="admin", password=config.SETTINGS.initial.admin_password, token_value=api_token ) + administrator_role = await create_super_administrator_role(db=db) + await create_super_administrators_group(db=db, role=administrator_role, admin_accounts=[admin_account]) await initialization(db=db) diff --git a/backend/tests/helpers/test_client.py b/backend/tests/helpers/test_client.py index 45698538ed..c3333dc053 100644 --- a/backend/tests/helpers/test_client.py +++ b/backend/tests/helpers/test_client.py @@ -15,7 +15,7 @@ async def dummy_async_request( class InfrahubTestClient(httpx.AsyncClient): - def __init__(self, app: FastAPI, base_url: str = ""): + def __init__(self, app: FastAPI, base_url: str = "") -> None: self.loop = asyncio.get_event_loop() super().__init__(app=app, base_url=base_url) diff --git a/backend/tests/integration/conftest.py b/backend/tests/integration/conftest.py index b7dec10a9e..e1d836450a 100644 --- a/backend/tests/integration/conftest.py +++ b/backend/tests/integration/conftest.py @@ -1,12 +1,14 @@ import asyncio import os from pathlib import Path -from typing import Any, AsyncGenerator, Optional +from typing import Any, Optional import pytest import yaml from infrahub_sdk import UUIDT +from prefect.testing.utilities import prefect_test_harness +from infrahub import config from infrahub.core import registry from infrahub.core.constants import InfrahubKind from infrahub.core.initialization import first_time_initialization, initialization @@ -14,8 +16,9 @@ from infrahub.core.node import Node from infrahub.core.schema import SchemaRoot from infrahub.core.utils import delete_all_nodes -from infrahub.database import InfrahubDatabase, get_db +from infrahub.database import InfrahubDatabase from infrahub.utils import get_models_dir +from tests.helpers.file_repo import FileRepo @pytest.fixture(scope="session", autouse=True) @@ -23,6 +26,12 @@ def add_tracker(): os.environ["PYTEST_RUNNING"] = "true" +@pytest.fixture(autouse=True, scope="session") +def prefect_test_fixture(): + with prefect_test_harness(): + yield + + @pytest.fixture(scope="session") def event_loop(): """Overrides pytest default function scoped event loop""" @@ -32,15 +41,6 @@ def event_loop(): loop.close() -@pytest.fixture(scope="module") -async def db() -> AsyncGenerator[InfrahubDatabase, None]: - driver = InfrahubDatabase(driver=await get_db(retry=1)) - - yield driver - - await driver.close() - - async def load_infrastructure_schema(db: InfrahubDatabase): base_dir = get_models_dir() / "base" @@ -89,18 +89,11 @@ async def create_token(self, account_name: Optional[str] = None) -> str: token = str(UUIDT()) account_name = account_name or "admin" response = await NodeManager.query( - schema=InfrahubKind.ACCOUNT, - db=self.db, - filters={"name__value": account_name}, - limit=1, + schema=InfrahubKind.ACCOUNT, db=self.db, filters={"name__value": account_name}, limit=1 ) account = response[0] account_token = await Node.init(db=self.db, schema=InfrahubKind.ACCOUNTTOKEN) - await account_token.new( - db=self.db, - token=token, - account=account, - ) + await account_token.new(db=self.db, token=token, account=account) await account_token.save(db=self.db) return token @@ -108,3 +101,35 @@ async def create_token(self, account_name: Optional[str] = None) -> str: @pytest.fixture(scope="class") def integration_helper(db: InfrahubDatabase) -> IntegrationHelper: return IntegrationHelper(db=db) + + +@pytest.fixture +def git_sources_dir(tmp_path: Path) -> Path: + source_dir = tmp_path / "sources" + source_dir.mkdir() + + return source_dir + + +@pytest.fixture +def git_repos_dir(tmp_path: Path) -> Path: + repos_dir = tmp_path / "repositories" + repos_dir.mkdir() + + config.SETTINGS.git.repositories_directory = str(repos_dir) + + return repos_dir + + +@pytest.fixture +def git_repo_infrahub_demo_edge(git_sources_dir: Path) -> FileRepo: + """Git Repository used as part of the demo-edge tutorial.""" + + return FileRepo(name="infrahub-demo-edge", sources_directory=git_sources_dir) + + +@pytest.fixture +def git_repo_car_dealership(git_sources_dir: Path) -> FileRepo: + """Simple Git Repository used for testing.""" + + return FileRepo(name="car-dealership", sources_directory=git_sources_dir) diff --git a/backend/tests/integration/git/conftest.py b/backend/tests/integration/git/conftest.py deleted file mode 100644 index 8716a2b423..0000000000 --- a/backend/tests/integration/git/conftest.py +++ /dev/null @@ -1,31 +0,0 @@ -from pathlib import Path - -import pytest - -from infrahub import config -from tests.helpers.file_repo import FileRepo - - -@pytest.fixture -def git_sources_dir(tmp_path: Path) -> Path: - source_dir = tmp_path / "sources" - source_dir.mkdir() - - return source_dir - - -@pytest.fixture -def git_repos_dir(tmp_path: Path) -> Path: - repos_dir = tmp_path / "repositories" - repos_dir.mkdir() - - config.SETTINGS.git.repositories_directory = str(repos_dir) - - return repos_dir - - -@pytest.fixture -def git_repo_infrahub_demo_edge(git_sources_dir: Path) -> FileRepo: - """Git Repository used as part of the demo-edge tutorial.""" - - return FileRepo(name="infrahub-demo-edge", sources_directory=git_sources_dir) diff --git a/backend/tests/integration/git/test_git_repository.py b/backend/tests/integration/git/test_git_repository.py index a12478ffe3..1379107049 100644 --- a/backend/tests/integration/git/test_git_repository.py +++ b/backend/tests/integration/git/test_git_repository.py @@ -5,6 +5,7 @@ import yaml from infrahub_sdk import Config, InfrahubClient, NodeNotFoundError +from infrahub import config from infrahub.core import registry from infrahub.core.constants import InfrahubKind from infrahub.core.initialization import first_time_initialization, initialization @@ -14,6 +15,7 @@ from infrahub.database import InfrahubDatabase from infrahub.git import InfrahubRepository from infrahub.server import app, app_initialization +from infrahub.services.adapters.workflow.local import WorkflowLocalExecution from infrahub.utils import get_models_dir from tests.adapters.log import FakeTaskReportLogger from tests.helpers.file_repo import FileRepo @@ -43,7 +45,15 @@ async def load_infrastructure_schema(db: InfrahubDatabase): class TestInfrahubClient: @pytest.fixture(scope="class") - async def base_dataset(self, db: InfrahubDatabase): + def workflow_local(prefect_test_fixture): + original = config.OVERRIDE.workflow + workflow = WorkflowLocalExecution() + config.OVERRIDE.workflow = workflow + yield workflow + config.OVERRIDE.workflow = original + + @pytest.fixture(scope="class") + async def base_dataset(self, db: InfrahubDatabase, redis, nats): await delete_all_nodes(db=db) await first_time_initialization(db=db) await load_infrastructure_schema(db=db) @@ -53,6 +63,7 @@ async def base_dataset(self, db: InfrahubDatabase): async def test_client( self, base_dataset, + workflow_local, ) -> InfrahubTestClient: await app_initialization(app) return InfrahubTestClient(app=app) diff --git a/backend/tests/integration/ipam/conftest.py b/backend/tests/integration/ipam/conftest.py index 39fef23397..d1d834d166 100644 --- a/backend/tests/integration/ipam/conftest.py +++ b/backend/tests/integration/ipam/conftest.py @@ -6,7 +6,7 @@ from infrahub.core.branch import Branch from infrahub.core.constants import BranchSupportType, InfrahubKind from infrahub.core.schema import SchemaRoot -from infrahub.core.schema_manager import SchemaBranch +from infrahub.core.schema.schema_branch import SchemaBranch @pytest.fixture(scope="class") diff --git a/backend/tests/integration/ipam/test_ipam_utilization.py b/backend/tests/integration/ipam/test_ipam_utilization.py index 0bdc68a057..742f628311 100644 --- a/backend/tests/integration/ipam/test_ipam_utilization.py +++ b/backend/tests/integration/ipam/test_ipam_utilization.py @@ -10,7 +10,7 @@ from infrahub.core.ipam.utilization import PrefixUtilizationGetter from infrahub.core.manager import NodeManager from infrahub.core.node import Node -from infrahub.graphql import prepare_graphql_params +from infrahub.graphql.initialization import prepare_graphql_params from tests.helpers.test_app import TestInfrahubApp if TYPE_CHECKING: diff --git a/backend/tests/integration/message_bus/operations/request/test_proposed_change.py b/backend/tests/integration/message_bus/operations/request/test_proposed_change.py index 7ed4f85ecf..05e66a2a91 100644 --- a/backend/tests/integration/message_bus/operations/request/test_proposed_change.py +++ b/backend/tests/integration/message_bus/operations/request/test_proposed_change.py @@ -63,7 +63,7 @@ @pytest.fixture(scope="module") -async def test_client(init_db_base) -> AsyncGenerator[InfrahubTestClient, None]: +async def test_client(init_db_base, redis, nats) -> AsyncGenerator[InfrahubTestClient, None]: await app_initialization(app) async with InfrahubTestClient(app=app, base_url="http://test") as client: yield client diff --git a/backend/tests/integration/profiles/test_profile_lifecycle.py b/backend/tests/integration/profiles/test_profile_lifecycle.py index f0d86c4e6e..2dcc916f76 100644 --- a/backend/tests/integration/profiles/test_profile_lifecycle.py +++ b/backend/tests/integration/profiles/test_profile_lifecycle.py @@ -8,7 +8,7 @@ from infrahub.core.schema.attribute_schema import AttributeSchema from infrahub.core.schema.node_schema import NodeSchema from infrahub.database import InfrahubDatabase -from infrahub.graphql import prepare_graphql_params +from infrahub.graphql.initialization import prepare_graphql_params from tests.helpers.schema import load_schema from tests.helpers.test_app import TestInfrahubApp diff --git a/backend/tests/integration/services/adapters/message_bus/test_rabbitmq.py b/backend/tests/integration/services/adapters/message_bus/test_rabbitmq.py index 099934bd33..95d98b9554 100644 --- a/backend/tests/integration/services/adapters/message_bus/test_rabbitmq.py +++ b/backend/tests/integration/services/adapters/message_bus/test_rabbitmq.py @@ -62,7 +62,7 @@ class RabbitMQManager: @property def base_url(self) -> str: scheme = "https" if self.settings.tls_enabled else "http" - port = f"1{self.settings.service_port}" + port = f"{self.settings.rabbitmq_http_port}" return f"{scheme}://{self.settings.address}:{port}/api" async def create_virtual_host(self) -> None: @@ -136,7 +136,7 @@ async def _request(self, method: str, url: str, payload: Optional[dict] = None) @pytest.fixture -async def rabbitmq_api() -> RabbitMQManager: +async def rabbitmq_api(rabbitmq) -> RabbitMQManager: settings = deepcopy(config.SETTINGS.broker) settings.virtualhost = "integration-tests" manager = RabbitMQManager(settings=settings) diff --git a/sync/examples/librenms_to_infrahub/librenms/__init__.py b/backend/tests/integration/services/adapters/workflow/__init__.py similarity index 100% rename from sync/examples/librenms_to_infrahub/librenms/__init__.py rename to backend/tests/integration/services/adapters/workflow/__init__.py diff --git a/backend/tests/integration/services/adapters/workflow/conftest.py b/backend/tests/integration/services/adapters/workflow/conftest.py new file mode 100644 index 0000000000..abcbc230ce --- /dev/null +++ b/backend/tests/integration/services/adapters/workflow/conftest.py @@ -0,0 +1,17 @@ +from typing import AsyncGenerator + +import pytest +from prefect.client.orchestration import PrefectClient, get_client + +from infrahub.workflows.initialization import setup_task_manager + + +@pytest.fixture +async def prefect_server(redis, prefect): + await setup_task_manager() + + +@pytest.fixture +async def prefect_client(prefect) -> AsyncGenerator[PrefectClient, None]: + async with get_client(sync_client=False) as client: + yield client diff --git a/backend/tests/integration/services/adapters/workflow/test_prefect.py b/backend/tests/integration/services/adapters/workflow/test_prefect.py new file mode 100644 index 0000000000..e4524ae59b --- /dev/null +++ b/backend/tests/integration/services/adapters/workflow/test_prefect.py @@ -0,0 +1,19 @@ +from prefect.client.orchestration import get_client + +from infrahub.workflows.catalogue import INFRAHUB_WORKER_POOL +from infrahub.workflows.initialization import setup_task_manager + + +async def test_setup_task_manager(redis, prefect): + await setup_task_manager() + + async with get_client(sync_client=False) as client: + response = await client.read_work_pool(INFRAHUB_WORKER_POOL.name) + assert response.type == INFRAHUB_WORKER_POOL.worker_type + + # Setup the task manager a second time to validate that it's idempotent + await setup_task_manager() + + async with get_client(sync_client=False) as client: + response = await client.read_work_pool(INFRAHUB_WORKER_POOL.name) + assert response.type == INFRAHUB_WORKER_POOL.worker_type diff --git a/sync/examples/nautobot-v1_to_infrahub/infrahub/__init__.py b/backend/tests/integration/transform/__init__.py similarity index 100% rename from sync/examples/nautobot-v1_to_infrahub/infrahub/__init__.py rename to backend/tests/integration/transform/__init__.py diff --git a/backend/tests/integration/transform/test_transform_jinja2.py b/backend/tests/integration/transform/test_transform_jinja2.py new file mode 100644 index 0000000000..4eb53d2da0 --- /dev/null +++ b/backend/tests/integration/transform/test_transform_jinja2.py @@ -0,0 +1,130 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +import pytest +from infrahub_sdk import Config, InfrahubClient + +from infrahub.core.constants import InfrahubKind +from infrahub.core.initialization import first_time_initialization, initialization +from infrahub.core.manager import NodeManager +from infrahub.core.node import Node +from infrahub.core.utils import delete_all_nodes +from infrahub.database import InfrahubDatabase +from infrahub.git import InfrahubRepository +from infrahub.server import app, app_initialization +from tests.adapters.log import FakeTaskReportLogger +from tests.constants import TestKind +from tests.helpers.schema import CAR_SCHEMA, load_schema +from tests.helpers.test_app import TestInfrahubApp +from tests.helpers.test_client import InfrahubTestClient + +if TYPE_CHECKING: + from infrahub.database import InfrahubDatabase + from tests.helpers.file_repo import FileRepo + + +class TestCreateRepository(TestInfrahubApp): + @pytest.fixture(scope="class") + async def base_dataset(self, db: InfrahubDatabase): + await delete_all_nodes(db=db) + await first_time_initialization(db=db) + await load_schema(db, schema=CAR_SCHEMA) + + await initialization(db=db) + + john = await Node.init(schema=TestKind.PERSON, db=db) + await john.new(db=db, name="John", height=175, age=25) + await john.save(db=db) + + people = await Node.init(schema=InfrahubKind.STANDARDGROUP, db=db) + await people.new(db=db, name="people", members=[john]) + await people.save(db=db) + + query1 = """ + query PersonWithTheirCars($name: String!) { + TestingPerson(name__value: $name) { + edges { + node { + name { + value + } + age { + value + } + cars { + edges { + node { + name { + value + } + } + } + } + } + } + } + } + """ + + q1 = await Node.init(db=db, schema=InfrahubKind.GRAPHQLQUERY) + await q1.new(db=db, name="query01", query=query1) + await q1.save(db=db) + + @pytest.fixture(scope="class") + async def test_client( + self, + base_dataset, + redis: dict[int, int] | None, + nats: dict[int, int] | None, + ) -> InfrahubTestClient: + await app_initialization(app) + return InfrahubTestClient(app=app) + + @pytest.fixture + async def client(self, test_client: InfrahubTestClient, integration_helper): # type: ignore[override] + admin_token = await integration_helper.create_token() + config = Config(api_token=admin_token, requester=test_client.async_request) + + return InfrahubClient(config=config) + + @pytest.fixture + async def repo(self, test_client, client, db: InfrahubDatabase, git_repo_car_dealership: FileRepo, git_repos_dir): + # Create the repository in the Graph + obj = await Node.init(schema=InfrahubKind.REPOSITORY, db=db) + await obj.new( + db=db, + name=git_repo_car_dealership.name, + description="test repository", + location="git@github.com:mock/test.git", + ) + await obj.save(db=db) + + # Initialize the repository on the file system + repo = await InfrahubRepository.new( + id=obj.id, + name=git_repo_car_dealership.name, + location=git_repo_car_dealership.path, + task_report=FakeTaskReportLogger(), + client=client, + ) + + return repo + + async def test_transform_jinja(self, db: InfrahubDatabase, client: InfrahubClient, repo: InfrahubRepository): + repositories = await NodeManager.query(db=db, schema=InfrahubKind.REPOSITORY) + queries = await NodeManager.query(db=db, schema=InfrahubKind.GRAPHQLQUERY) + + t1 = await Node.init(db=db, schema=InfrahubKind.TRANSFORMJINJA2) + await t1.new( + db=db, + name="test-rfile", + query=str(queries[0].id), + repository=str(repositories[0].id), + template_path="templates/person_with_cars.j2", + ) + await t1.save(db=db) + + response = await client._get(url=f"{client.address}/api/transform/jinja2/test-rfile?name=John") + + assert response.text == "Name: John" diff --git a/backend/tests/integration/user_workflows/test_user_worflow.py b/backend/tests/integration/user_workflows/test_user_worflow.py index b39ec38e18..9665871b5a 100644 --- a/backend/tests/integration/user_workflows/test_user_worflow.py +++ b/backend/tests/integration/user_workflows/test_user_worflow.py @@ -142,7 +142,7 @@ def __init__(self) -> None: class TestUserWorkflow01: @pytest.fixture(scope="class") - async def client(self): + async def client(self, redis, nats): client = TestClient(app) return client diff --git a/backend/tests/scale/common/config.py b/backend/tests/scale/common/config.py index 6a7945e91d..963a9355cf 100644 --- a/backend/tests/scale/common/config.py +++ b/backend/tests/scale/common/config.py @@ -5,7 +5,7 @@ class Config(BaseSettings): model_config = SettingsConfigDict(env_prefix="INFRAHUB_") url: str = "http://localhost:8000" api_token: str = "06438eb2-8019-4776-878c-0941b1f1d1ec" - server_container: str = "infrahub-infrahub-server-1" + server_container: str = "infrahub-server-1" db_container: str = "infrahub-database-1" db_volume: str = "infrahub_database_data" test_task_iterations: int = 1 diff --git a/backend/tests/scale/common/events.py b/backend/tests/scale/common/events.py index be9a840d98..c9ddc56590 100644 --- a/backend/tests/scale/common/events.py +++ b/backend/tests/scale/common/events.py @@ -68,7 +68,7 @@ def request_event_handler( "name": name, "start_time": f"{start_time:.2f}", "response_time": f"{response_time:.2f}ms", - "failed": True if exception else False, + "failed": bool(exception), } if exception: diff --git a/backend/tests/scale/common/protocols.py b/backend/tests/scale/common/protocols.py index 665fafa8f7..e3cb7d3a8c 100644 --- a/backend/tests/scale/common/protocols.py +++ b/backend/tests/scale/common/protocols.py @@ -7,7 +7,7 @@ class LocustInfrahubClient(InfrahubClientSync): "Locust protocol for Python Infrahub SDK client" - def __init__(self, address: str, config: Config, request_event): + def __init__(self, address: str, config: Config, request_event) -> None: super().__init__(address=address, config=config) self._request_event = request_event diff --git a/backend/tests/scale/common/users.py b/backend/tests/scale/common/users.py index 85ad652243..df072ff5c2 100644 --- a/backend/tests/scale/common/users.py +++ b/backend/tests/scale/common/users.py @@ -15,7 +15,7 @@ class InfrahubClientUser(User): delete_this_node = None update_this_node = None - def __init__(self, environment): + def __init__(self, environment) -> None: super().__init__(environment) self.address = environment.custom_options["config"].url diff --git a/backend/tests/unit/api/conftest.py b/backend/tests/unit/api/conftest.py index a9dbe6b07a..32ef935026 100644 --- a/backend/tests/unit/api/conftest.py +++ b/backend/tests/unit/api/conftest.py @@ -3,6 +3,7 @@ import pendulum import pytest from fastapi.testclient import TestClient +from prefect.testing.utilities import prefect_test_harness from infrahub import config from infrahub.core.constants import InfrahubKind @@ -10,10 +11,11 @@ from infrahub.core.manager import NodeManager from infrahub.core.node import Node from infrahub.database import InfrahubDatabase +from infrahub.services.adapters.workflow.local import WorkflowLocalExecution @pytest.fixture -def client(): +def client(nats, redis): # In order to mock some methods later we can't load app by default because it will automatically load all import in main.py as well from infrahub.server import app @@ -30,6 +32,12 @@ def admin_headers(): return {"X-INFRAHUB-KEY": "admin-security"} +@pytest.fixture(autouse=True, scope="session") +def prefect_test_fixture(): + with prefect_test_harness(): + yield + + @pytest.fixture def rpc_bus(helper): original = config.OVERRIDE.message_bus @@ -48,6 +56,15 @@ def rpc_bus_simulator(helper, db): config.OVERRIDE.message_bus = original +@pytest.fixture() +def workflow_local(): + original = config.OVERRIDE.workflow + workflow = WorkflowLocalExecution() + config.OVERRIDE.workflow = workflow + yield workflow + config.OVERRIDE.workflow = original + + @pytest.fixture async def car_person_data( db: InfrahubDatabase, register_core_models_schema, car_person_schema, first_account diff --git a/backend/tests/unit/api/test_05_query_api.py b/backend/tests/unit/api/test_05_query_api.py index 942b949796..700ab04df8 100644 --- a/backend/tests/unit/api/test_05_query_api.py +++ b/backend/tests/unit/api/test_05_query_api.py @@ -1,11 +1,19 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + import pytest -from fastapi.testclient import TestClient -from infrahub.core.branch import Branch from infrahub.core.initialization import create_branch -from infrahub.database import InfrahubDatabase from infrahub.message_bus import messages +if TYPE_CHECKING: + from fastapi.testclient import TestClient + + from infrahub.core.branch import Branch + from infrahub.core.node import Node + from infrahub.database import InfrahubDatabase + @pytest.fixture async def base_authentication( @@ -13,17 +21,13 @@ async def base_authentication( default_branch: Branch, create_test_admin, register_core_models_schema, -): +) -> None: pass async def test_query_endpoint_group_no_params( - db: InfrahubDatabase, client_headers, default_branch, car_person_data, patch_services + db: InfrahubDatabase, client: TestClient, client_headers, default_branch, car_person_data, patch_services ): - from infrahub.server import app - - client = TestClient(app) - # Must execute in a with block to execute the startup/shutdown events with client: response = client.get( @@ -61,11 +65,9 @@ async def test_query_endpoint_group_no_params( ) -async def test_query_endpoint_group_params(db: InfrahubDatabase, client_headers, default_branch, car_person_data): - from infrahub.server import app - - client = TestClient(app) - +async def test_query_endpoint_group_params( + db: InfrahubDatabase, client: TestClient, client_headers, default_branch, car_person_data +): # Must execute in a with block to execute the startup/shutdown events with client: response = client.get( @@ -98,7 +100,7 @@ async def test_query_endpoint_group_params(db: InfrahubDatabase, client_headers, async def test_query_endpoint_get_default_branch( - db: InfrahubDatabase, client, client_headers, default_branch, car_person_data + db: InfrahubDatabase, client: TestClient, client_headers, default_branch, car_person_data ): # Must execute in a with block to execute the startup/shutdown events with client: @@ -120,7 +122,7 @@ async def test_query_endpoint_get_default_branch( async def test_query_endpoint_post_no_payload( db: InfrahubDatabase, - client, + client: TestClient, admin_headers, default_branch, car_person_data, @@ -146,7 +148,7 @@ async def test_query_endpoint_post_no_payload( async def test_query_endpoint_post_with_params( db: InfrahubDatabase, - client, + client: TestClient, admin_headers, default_branch, car_person_data, @@ -166,7 +168,7 @@ async def test_query_endpoint_post_with_params( async def test_query_endpoint_branch1( - db: InfrahubDatabase, client, client_headers, default_branch, car_person_data, authentication_base + db: InfrahubDatabase, client: TestClient, client_headers, default_branch, car_person_data, authentication_base ): await create_branch(branch_name="branch1", db=db) @@ -189,7 +191,12 @@ async def test_query_endpoint_branch1( async def test_query_endpoint_wrong_query( - db: InfrahubDatabase, client, client_headers, default_branch, car_person_schema, register_core_models_schema + db: InfrahubDatabase, + client: TestClient, + client_headers, + default_branch, + car_person_schema, + register_core_models_schema, ): # Must execute in a with block to execute the startup/shutdown events with client: @@ -202,7 +209,12 @@ async def test_query_endpoint_wrong_query( async def test_query_endpoint_wrong_branch( - db: InfrahubDatabase, client, client_headers, default_branch, car_person_schema, register_core_models_schema + db: InfrahubDatabase, + client: TestClient, + client_headers, + default_branch, + car_person_schema, + register_core_models_schema, ): # Must execute in a with block to execute the startup/shutdown events with client: @@ -212,3 +224,29 @@ async def test_query_endpoint_wrong_branch( ) assert response.status_code == 400 + + +async def test_query_endpoint_missing_privs( + db: InfrahubDatabase, + client: TestClient, + first_account: Node, + default_branch: Branch, + car_person_data: dict[str, Node], + base_authentication: None, +) -> None: + with client: + token = client.post( + "/api/auth/login", json={"username": first_account.name.value, "password": first_account.password.value} + ) + assert token.status_code == 200 + access_token = token.json()["access_token"] + + response = client.post( + "/api/query/query01", + headers={"Authorization": f"Bearer {access_token}"}, + ) + + assert response.status_code == 403 + error = response.json() + assert error["errors"] + assert "You do not have the following permission" in error["errors"][0]["message"] diff --git a/backend/tests/unit/api/test_10_transformation_api.py b/backend/tests/unit/api/test_10_transformation_api.py index d4edb6790e..94b549796f 100644 --- a/backend/tests/unit/api/test_10_transformation_api.py +++ b/backend/tests/unit/api/test_10_transformation_api.py @@ -1,20 +1,13 @@ -from fastapi.testclient import TestClient - from infrahub.core.constants import InfrahubKind from infrahub.core.manager import NodeManager from infrahub.core.node import Node from infrahub.database import InfrahubDatabase -from infrahub.message_bus.messages.transform_jinja_template import TransformJinjaTemplateResponse from infrahub.message_bus.messages.transform_python_data import TransformPythonDataResponse async def test_transform_endpoint( - db: InfrahubDatabase, client_headers, default_branch, rpc_bus, register_core_models_schema, car_person_data + db: InfrahubDatabase, client, client_headers, default_branch, rpc_bus, register_core_models_schema, car_person_data ): - from infrahub.server import app - - client = TestClient(app) - repositories = await NodeManager.query(db=db, schema=InfrahubKind.REPOSITORY) queries = await NodeManager.query(db=db, schema=InfrahubKind.GRAPHQLQUERY) @@ -46,39 +39,3 @@ async def test_transform_endpoint( result = response.json() assert result == {"KEY1": "value1", "KEY2": "value2"} - - -async def test_rfile_endpoint( - db: InfrahubDatabase, client_headers, default_branch, rpc_bus, register_core_models_schema, car_person_data -): - from infrahub.server import app - - client = TestClient(app) - - repositories = await NodeManager.query(db=db, schema=InfrahubKind.REPOSITORY) - queries = await NodeManager.query(db=db, schema=InfrahubKind.GRAPHQLQUERY) - - t1 = await Node.init(db=db, schema=InfrahubKind.TRANSFORMJINJA2) - await t1.new( - db=db, - name="test-rfile", - query=str(queries[0].id), - repository=str(repositories[0].id), - template_path="templates/device_startup_config.tpl.j2", - ) - await t1.save(db=db) - - # Must execute in a with block to execute the startup/shutdown events - with client: - mock_response = TransformJinjaTemplateResponse( - data={"rendered_template": "Rendered by a mocked agent"}, - ) - rpc_bus.add_mock_reply(response=mock_response) - - response = client.get( - "/api/transform/jinja2/test-rfile", - headers=client_headers, - ) - - assert response.status_code == 200 - assert response.text == "Rendered by a mocked agent" diff --git a/backend/tests/unit/api/test_11_artifact.py b/backend/tests/unit/api/test_11_artifact.py index f6d2104788..b982c5dc53 100644 --- a/backend/tests/unit/api/test_11_artifact.py +++ b/backend/tests/unit/api/test_11_artifact.py @@ -1,5 +1,3 @@ -from fastapi.testclient import TestClient - from infrahub.core import registry from infrahub.core.constants import InfrahubKind from infrahub.core.node import Node @@ -9,6 +7,7 @@ async def test_artifact_definition_endpoint( db: InfrahubDatabase, + client, admin_headers, default_branch, rpc_bus, @@ -17,10 +16,6 @@ async def test_artifact_definition_endpoint( car_person_data_generic, authentication_base, ): - from infrahub.server import app - - client = TestClient(app) - g1 = await Node.init(db=db, schema=InfrahubKind.STANDARDGROUP) await g1.new(db=db, name="group1", members=[car_person_data_generic["c1"], car_person_data_generic["c2"]]) await g1.save(db=db) @@ -64,16 +59,13 @@ async def test_artifact_definition_endpoint( async def test_artifact_endpoint( db: InfrahubDatabase, + client, admin_headers, register_core_models_schema, register_builtin_models_schema, car_person_data_generic, authentication_base, ): - from infrahub.server import app - - client = TestClient(app) - with client: response = client.get("/api/artifact/95008984-16ca-4e58-8323-0899bb60035f", headers=admin_headers) assert response.status_code == 404 diff --git a/backend/tests/unit/api/test_12_file.py b/backend/tests/unit/api/test_12_file.py index 1bf3c1d048..3a4a966e46 100644 --- a/backend/tests/unit/api/test_12_file.py +++ b/backend/tests/unit/api/test_12_file.py @@ -1,5 +1,3 @@ -from fastapi.testclient import TestClient - from infrahub.core.constants import InfrahubKind from infrahub.core.node import Node from infrahub.database import InfrahubDatabase @@ -8,15 +6,12 @@ async def test_get_file( db: InfrahubDatabase, + client, client_headers, default_branch, rpc_bus, register_core_models_schema, ): - from infrahub.server import app - - client = TestClient(app) - r1 = await Node.init(db=db, schema=InfrahubKind.REPOSITORY) await r1.new(db=db, name="repo01", location="git@github.com:user/repo01.git") await r1.save(db=db) diff --git a/backend/tests/unit/api/test_20_graphql_api.py b/backend/tests/unit/api/test_20_graphql_api.py index 17ded28d46..2239ed6f93 100644 --- a/backend/tests/unit/api/test_20_graphql_api.py +++ b/backend/tests/unit/api/test_20_graphql_api.py @@ -221,7 +221,14 @@ async def test_download_schema(db: InfrahubDatabase, client, client_headers): async def test_query_at_previous_schema( - db: InfrahubDatabase, client, admin_headers, default_branch: Branch, authentication_base, car_person_data + db: InfrahubDatabase, + client, + admin_headers, + default_branch: Branch, + authentication_base, + prefect_test_fixture, + workflow_local, + car_person_data, ): # Load the schema in the database schema = registry.schema.get_schema_branch(name=default_branch.name) diff --git a/backend/tests/unit/api/test_40_schema_api.py b/backend/tests/unit/api/test_40_schema_api.py index 3c51455024..28ececed2d 100644 --- a/backend/tests/unit/api/test_40_schema_api.py +++ b/backend/tests/unit/api/test_40_schema_api.py @@ -7,21 +7,16 @@ from infrahub.core.path import SchemaPath from infrahub.core.schema import SchemaRoot, core_models from infrahub.core.utils import count_relationships -from infrahub.core.validators.model import SchemaViolation from infrahub.database import InfrahubDatabase from infrahub.message_bus.messages.schema_migration_path import ( SchemaMigrationPathResponse, SchemaMigrationPathResponseData, ) -from infrahub.message_bus.messages.schema_validator_path import ( - SchemaValidatorPathResponse, - SchemaValidatorPathResponseData, -) async def test_schema_read_endpoint_default_branch( db: InfrahubDatabase, - client, + client: TestClient, client_headers, default_branch: Branch, car_person_schema_generics: SchemaRoot, @@ -97,7 +92,7 @@ async def test_schema_read_endpoint_wrong_branch( async def test_schema_summary_default_branch( db: InfrahubDatabase, - client, + client: TestClient, client_headers, default_branch: Branch, car_person_schema_generics: SchemaRoot, @@ -121,7 +116,7 @@ async def test_schema_summary_default_branch( async def test_schema_kind_default_branch( db: InfrahubDatabase, - client, + client: TestClient, client_headers, default_branch: Branch, car_person_schema_generics: SchemaRoot, @@ -140,7 +135,6 @@ async def test_schema_kind_default_branch( assert "id" in schema assert "hash" in schema - assert "filters" in schema assert "relationships" in schema @@ -174,7 +168,7 @@ async def test_json_schema_kind_default_branch( async def test_schema_kind_not_valid( db: InfrahubDatabase, - client, + client: TestClient, client_headers, default_branch: Branch, car_person_schema_generics: SchemaRoot, @@ -195,6 +189,8 @@ async def test_schema_load_endpoint_valid_simple( client: TestClient, admin_headers, default_branch: Branch, + prefect_test_fixture, + workflow_local, authentication_base, helper, ): @@ -230,6 +226,8 @@ async def test_schema_load_restricted_namespace( client: TestClient, admin_headers, default_branch: Branch, + prefect_test_fixture, + workflow_local, authentication_base, helper, ): @@ -249,6 +247,8 @@ async def test_schema_load_endpoint_idempotent_simple( client: TestClient, admin_headers, default_branch: Branch, + prefect_test_fixture, + workflow_local, register_core_schema_db, authentication_base, helper, @@ -296,6 +296,8 @@ async def test_schema_load_endpoint_valid_with_generics( client: TestClient, admin_headers, default_branch: Branch, + prefect_test_fixture, + workflow_local, register_core_schema_db, authentication_base, helper, @@ -325,6 +327,8 @@ async def test_schema_load_endpoint_idempotent_with_generics( client: TestClient, admin_headers, default_branch: Branch, + prefect_test_fixture, + workflow_local, register_core_schema_db, authentication_base, helper, @@ -372,6 +376,8 @@ async def test_schema_load_endpoint_valid_with_extensions( admin_headers, rpc_bus, default_branch: Branch, + prefect_test_fixture, + workflow_local, authentication_base, helper, ): @@ -418,6 +424,8 @@ async def test_schema_load_endpoint_not_valid_simple_02( client: TestClient, admin_headers, default_branch: Branch, + prefect_test_fixture, + workflow_local, authentication_base, helper, ): @@ -437,6 +445,8 @@ async def test_schema_load_endpoint_not_valid_simple_03( client: TestClient, admin_headers, default_branch: Branch, + prefect_test_fixture, + workflow_local, authentication_base, helper, ): @@ -456,6 +466,8 @@ async def test_schema_load_endpoint_not_valid_simple_04( client: TestClient, admin_headers, default_branch: Branch, + prefect_test_fixture, + workflow_local, authentication_base, helper, ): @@ -475,6 +487,8 @@ async def test_schema_load_endpoint_not_valid_simple_05( client: TestClient, admin_headers, default_branch: Branch, + prefect_test_fixture, + workflow_local, authentication_base, helper, ): @@ -517,6 +531,8 @@ async def test_schema_load_endpoint_constraints_not_valid( admin_headers, rpc_bus, default_branch: Branch, + prefect_test_fixture, + workflow_local, authentication_base, car_person_schema, car_accord_main, @@ -524,32 +540,10 @@ async def test_schema_load_endpoint_constraints_not_valid( person_john_main, helper, ): - # person = await Node.init(db=db, schema="TestPerson", branch=default_branch) - # await person.new(db=db, name="ALFRED", height=160, cars=[car_accord_main.id]) - # await person.save(db=db) - # Load the schema in the database schema = registry.schema.get_schema_branch(name=default_branch.name) await registry.schema.load_schema_to_db(schema=schema, branch=default_branch, db=db) - rpc_bus.response.append( - SchemaValidatorPathResponse( - data=SchemaValidatorPathResponseData( - violations=[ - SchemaViolation( - node_id="cf85d101-c6d6-41aa-b1ab-41bc4c7d46f1", - node_kind="TestPerson", - display_label="ALFRED", - full_display_label="Alfred TestPerson(cf85d101-c6d6-41aa-b1ab-41bc4c7d46f1)", - message="clear error message", - ) - ], - constraint_name="attribute.regex.update", - schema_path=SchemaPath(path_type=SchemaPathType.ATTRIBUTE, schema_kind="TestPerson", field_name="name"), - ) - ) - ) - person_schema = { "name": "Person", "namespace": "Test", @@ -572,8 +566,9 @@ async def test_schema_load_endpoint_constraints_not_valid( json={"schemas": [{"version": "1.0", "nodes": [person_schema]}]}, ) + error_message = f"Node John (TestPerson: {person_john_main.id}) is not compatible with the constraint 'attribute.regex.update' at 'schema/TestPerson/name/regex'" # noqa: E501 assert response.json() == { "data": None, - "errors": [{"extensions": {"code": 422}, "message": "clear error message"}], + "errors": [{"extensions": {"code": 422}, "message": error_message}], } assert response.status_code == 422 diff --git a/backend/tests/unit/api/test_50_config_api.py b/backend/tests/unit/api/test_50_config_api.py index 139b98b16c..a1c5b3ca0b 100644 --- a/backend/tests/unit/api/test_50_config_api.py +++ b/backend/tests/unit/api/test_50_config_api.py @@ -13,4 +13,4 @@ async def test_config_endpoint(db: InfrahubDatabase, client, client_headers, def config = response.json() - assert sorted(config.keys()) == ["analytics", "experimental_features", "logging", "main"] + assert sorted(config.keys()) == ["analytics", "experimental_features", "logging", "main", "sso"] diff --git a/backend/tests/unit/api/test_60_storage.py b/backend/tests/unit/api/test_60_storage.py index 2dfc606da4..b9a7937318 100644 --- a/backend/tests/unit/api/test_60_storage.py +++ b/backend/tests/unit/api/test_60_storage.py @@ -9,15 +9,16 @@ async def test_file_upload( - db: InfrahubDatabase, helper, local_storage_dir: str, admin_headers, default_branch: Branch, authentication_base + db: InfrahubDatabase, + client: TestClient, + helper, + local_storage_dir: str, + admin_headers, + default_branch: Branch, + authentication_base, ): - from infrahub.server import app - - client = TestClient(app) - fixture_dir = helper.get_fixtures_dir() - fixture_dir = helper.get_fixtures_dir() files_dir = os.path.join(fixture_dir, "schemas") filenames = [item.name for item in os.scandir(files_dir) if item.is_file()] file_path = Path(os.path.join(files_dir, filenames[0])) @@ -40,14 +41,14 @@ async def test_file_upload( async def test_content_upload( - db: InfrahubDatabase, helper, local_storage_dir: str, admin_headers, default_branch: Branch, authentication_base + db: InfrahubDatabase, + client: TestClient, + helper, + local_storage_dir: str, + admin_headers, + default_branch: Branch, + authentication_base, ): - from infrahub.server import app - - client = TestClient(app) - - fixture_dir = helper.get_fixtures_dir() - fixture_dir = helper.get_fixtures_dir() files_dir = os.path.join(fixture_dir, "schemas") filenames = [item.name for item in os.scandir(files_dir) if item.is_file()] diff --git a/backend/tests/unit/api/test_api_base.py b/backend/tests/unit/api/test_api_base.py index 0b52a09638..0057268231 100644 --- a/backend/tests/unit/api/test_api_base.py +++ b/backend/tests/unit/api/test_api_base.py @@ -1,6 +1,4 @@ -async def test_get_invalid( - client, -): +async def test_get_invalid(client, db): with client: response = client.get( "/api/so-such-route", diff --git a/backend/tests/unit/api/test_api_exception_handler.py b/backend/tests/unit/api/test_api_exception_handler.py index d674926a7a..dee82c508f 100644 --- a/backend/tests/unit/api/test_api_exception_handler.py +++ b/backend/tests/unit/api/test_api_exception_handler.py @@ -26,7 +26,7 @@ class MockError(Error): HTTP_CODE = 418 DESCRIPTION = "the teapot error" - def __init__(self, message: Optional[str]): + def __init__(self, message: Optional[str]) -> None: self.message = message or "" diff --git a/backend/tests/unit/api/test_menu.py b/backend/tests/unit/api/test_menu.py index d727792c1c..4d37efaf70 100644 --- a/backend/tests/unit/api/test_menu.py +++ b/backend/tests/unit/api/test_menu.py @@ -1,5 +1,6 @@ from infrahub.api.menu import InterfaceMenu from infrahub.core.branch import Branch +from infrahub.core.initialization import create_default_menu from infrahub.core.schema import SchemaRoot from infrahub.database import InfrahubDatabase @@ -24,3 +25,23 @@ async def test_get_menu( menu = [InterfaceMenu(**menu_item) for menu_item in response.json()] assert menu[0].title == "Objects" assert menu[0].children[0].title == "Car" + + +async def test_get_new_menu( + db: InfrahubDatabase, + client, + client_headers, + default_branch: Branch, + car_person_schema_generics: SchemaRoot, + car_person_data_generic, +): + await create_default_menu(db=db) + + with client: + response = client.get( + "/api/menu/new", + headers=client_headers, + ) + + assert response.status_code == 200 + assert response.json() is not None diff --git a/backend/tests/unit/conftest.py b/backend/tests/unit/conftest.py index 81d168b0b6..9b1f87ceb1 100644 --- a/backend/tests/unit/conftest.py +++ b/backend/tests/unit/conftest.py @@ -22,7 +22,14 @@ StringOptional, ) from infrahub.core.branch import Branch -from infrahub.core.constants import GLOBAL_BRANCH_NAME, BranchSupportType, InfrahubKind +from infrahub.core.constants import ( + GLOBAL_BRANCH_NAME, + BranchSupportType, + GlobalPermissions, + InfrahubKind, + PermissionAction, + PermissionDecision, +) from infrahub.core.initialization import ( create_branch, create_default_branch, @@ -42,12 +49,13 @@ SchemaRoot, core_models, ) -from infrahub.core.schema_manager import SchemaBranch +from infrahub.core.schema.schema_branch import SchemaBranch from infrahub.core.utils import delete_all_nodes from infrahub.database import InfrahubDatabase from infrahub.dependencies.registry import build_component_registry from infrahub.git import InfrahubRepository from infrahub.test_data import dataset01 as ds01 +from infrahub.utils import format_label from tests.helpers.file_repo import FileRepo from tests.helpers.test_client import dummy_async_request @@ -2454,11 +2462,16 @@ async def register_core_schema_db(db: InfrahubDatabase, default_branch: Branch, @pytest.fixture async def register_account_schema(db: InfrahubDatabase) -> None: SCHEMAS_TO_REGISTER = [ + InfrahubKind.ACCOUNTGROUP, + InfrahubKind.ACCOUNTROLE, InfrahubKind.GENERICACCOUNT, InfrahubKind.ACCOUNT, InfrahubKind.ACCOUNTTOKEN, InfrahubKind.GENERICGROUP, InfrahubKind.REFRESHTOKEN, + InfrahubKind.BASEPERMISSION, + InfrahubKind.GLOBALPERMISSION, + InfrahubKind.OBJECTPERMISSION, ] nodes = [item for item in core_models["nodes"] if f'{item["namespace"]}{item["name"]}' in SCHEMAS_TO_REGISTER] generics = [item for item in core_models["generics"] if f'{item["namespace"]}{item["name"]}' in SCHEMAS_TO_REGISTER] @@ -2522,11 +2535,47 @@ async def register_ipam_extended_schema(default_branch: Branch, register_ipam_sc @pytest.fixture async def create_test_admin(db: InfrahubDatabase, register_core_models_schema, data_schema) -> Node: + """Create a test admin account, group and role with all global permissions.""" + permissions: list[Node] = [] + global_permission = await Node.init(db=db, schema=InfrahubKind.GLOBALPERMISSION) + await global_permission.new( + db=db, + name=format_label(GlobalPermissions.SUPER_ADMIN.value), + action=GlobalPermissions.SUPER_ADMIN.value, + decision=PermissionDecision.ALLOW.value, + ) + await global_permission.save(db=db) + permissions.append(global_permission) + + object_permission = await Node.init(db=db, schema=InfrahubKind.OBJECTPERMISSION) + await object_permission.new( + db=db, + branch="*", + namespace="*", + name="*", + action=PermissionAction.ANY.value, + decision=PermissionDecision.ALLOW.value, + ) + await object_permission.save(db=db) + permissions.append(object_permission) + + role = await Node.init(db=db, schema=InfrahubKind.ACCOUNTROLE) + await role.new(db=db, name="admin", permissions=permissions) + await role.save(db=db) + + group = await Node.init(db=db, schema=InfrahubKind.ACCOUNTGROUP) + await group.new(db=db, name="admin", roles=[role]) + await group.save(db=db) + account = await Node.init(db=db, schema=InfrahubKind.ACCOUNT) await account.new( db=db, name="test-admin", account_type="User", password=config.SETTINGS.initial.admin_password, role="admin" ) await account.save(db=db) + + await group.members.add(db=db, data=account) + await group.members.save(db=db) + token = await Node.init(db=db, schema=InfrahubKind.ACCOUNTTOKEN) await token.new(db=db, token="admin-security", account=account) await token.save(db=db) @@ -2560,6 +2609,12 @@ async def first_account(db: InfrahubDatabase, data_schema, node_group_schema, re return obj +@pytest.fixture +async def session_first_account(db: InfrahubDatabase, first_account) -> AccountSession: + session = AccountSession(authenticated=True, auth_type=AuthType.API, account_id=first_account.id, role="read-write") + return session + + @pytest.fixture async def second_account(db: InfrahubDatabase, data_schema, node_group_schema, register_account_schema) -> Node: obj = await Node.init(db=db, schema=InfrahubKind.ACCOUNT) diff --git a/backend/tests/unit/core/constraint_validators/test_relationship_profiles_kind.py b/backend/tests/unit/core/constraint_validators/test_relationship_profiles_kind.py index 9675f06fa9..75675a8309 100644 --- a/backend/tests/unit/core/constraint_validators/test_relationship_profiles_kind.py +++ b/backend/tests/unit/core/constraint_validators/test_relationship_profiles_kind.py @@ -6,7 +6,7 @@ from infrahub.core.node import Node from infrahub.core.relationship.constraints.profiles_kind import RelationshipProfilesKindConstraint from infrahub.core.schema import SchemaRoot -from infrahub.core.schema_manager import SchemaBranch +from infrahub.core.schema.schema_branch import SchemaBranch from infrahub.database import InfrahubDatabase from infrahub.exceptions import ValidationError from tests.helpers.test_app import TestInfrahubApp diff --git a/backend/tests/unit/core/constraint_validators/test_task_schema_validate_migrations.py b/backend/tests/unit/core/constraint_validators/test_task_schema_validate_migrations.py new file mode 100644 index 0000000000..260e349cae --- /dev/null +++ b/backend/tests/unit/core/constraint_validators/test_task_schema_validate_migrations.py @@ -0,0 +1,49 @@ +from infrahub_sdk import InfrahubClient + +from infrahub.core import registry +from infrahub.core.branch import Branch +from infrahub.core.constants import SchemaPathType +from infrahub.core.models import SchemaUpdateConstraintInfo +from infrahub.core.node import Node +from infrahub.core.path import SchemaPath +from infrahub.core.validators.models.validate_migration import SchemaValidateMigrationData +from infrahub.core.validators.tasks import schema_validate_migrations +from infrahub.database import InfrahubDatabase +from infrahub.services import InfrahubServices, services +from infrahub.services.adapters.message_bus.local import BusSimulator +from infrahub.services.adapters.workflow.local import WorkflowLocalExecution + + +async def test_schema_validate_migrations( + db: InfrahubDatabase, + default_branch: Branch, + prefect_test_fixture, + car_accord_main: Node, + car_volt_main: Node, + person_john_main, + helper, +): + schema = registry.schema.get_schema_branch(name=default_branch.name).duplicate() + person_schema = schema.get(name="TestPerson") + name_attr = person_schema.get_attribute(name="name") + name_attr.regex = r"^[A-Z]+$" + schema.set(name="TestPerson", schema=person_schema) + + constraints = [ + SchemaUpdateConstraintInfo( + constraint_name="attribute.regex.update", + path=SchemaPath(path_type=SchemaPathType.ATTRIBUTE, schema_kind="TestPerson", field_name="name"), + ) + ] + + services.service = InfrahubServices( + message_bus=BusSimulator(database=db), client=InfrahubClient(), workflow=WorkflowLocalExecution(), database=db + ) + + message = SchemaValidateMigrationData(branch=default_branch, schema_branch=schema, constraints=constraints) + errors = await schema_validate_migrations( + message=message, + ) + + assert len(errors) == 1 + assert "is not compatible with the constraint 'attribute.regex.update'" in errors[0] diff --git a/backend/tests/unit/core/diff/test_diff_calculator.py b/backend/tests/unit/core/diff/test_diff_calculator.py index a9c4a3d295..d426f62f19 100644 --- a/backend/tests/unit/core/diff/test_diff_calculator.py +++ b/backend/tests/unit/core/diff/test_diff_calculator.py @@ -8,7 +8,7 @@ from infrahub.core.initialization import create_branch from infrahub.core.manager import NodeManager from infrahub.core.node import Node -from infrahub.core.schema_manager import SchemaBranch +from infrahub.core.schema.schema_branch import SchemaBranch from infrahub.core.timestamp import Timestamp from infrahub.database import InfrahubDatabase diff --git a/backend/tests/unit/core/diff/test_diff_combiner.py b/backend/tests/unit/core/diff/test_diff_combiner.py index 70906cdb5b..2ff868215d 100644 --- a/backend/tests/unit/core/diff/test_diff_combiner.py +++ b/backend/tests/unit/core/diff/test_diff_combiner.py @@ -19,8 +19,8 @@ EnrichedDiffs, EnrichedDiffSingleRelationship, ) +from infrahub.core.schema.manager import SchemaManager from infrahub.core.schema.node_schema import NodeSchema -from infrahub.core.schema_manager import SchemaManager from infrahub.core.timestamp import Timestamp from .factories import ( diff --git a/backend/tests/unit/core/diff/test_diff_merger.py b/backend/tests/unit/core/diff/test_diff_merger.py new file mode 100644 index 0000000000..f30e3480e3 --- /dev/null +++ b/backend/tests/unit/core/diff/test_diff_merger.py @@ -0,0 +1,221 @@ +from unittest.mock import AsyncMock, call +from uuid import uuid4 + +import pytest + +from infrahub.core.branch import Branch +from infrahub.core.constants import DiffAction +from infrahub.core.diff.merger.merger import DiffMerger +from infrahub.core.diff.merger.serializer import DiffMergeSerializer +from infrahub.core.diff.model.path import ( + BranchTrackingId, + ConflictSelection, + EnrichedDiffNode, + EnrichedDiffRoot, +) +from infrahub.core.diff.repository.repository import DiffRepository +from infrahub.core.initialization import create_branch +from infrahub.core.manager import NodeManager +from infrahub.core.node import Node +from infrahub.core.timestamp import Timestamp +from infrahub.database import InfrahubDatabase +from infrahub.exceptions import NodeNotFoundError +from tests.unit.core.diff.factories import EnrichedConflictFactory, EnrichedNodeFactory, EnrichedRootFactory + + +class TestMergeDiff: + @pytest.fixture + async def source_branch(self, db: InfrahubDatabase, default_branch: Branch) -> Branch: + return await create_branch(db=db, branch_name="source") + + @pytest.fixture + def mock_diff_repository(self) -> DiffRepository: + return AsyncMock(spec=DiffRepository) + + @pytest.fixture + def diff_merger( + self, db: InfrahubDatabase, default_branch: Branch, source_branch: Branch, mock_diff_repository: DiffRepository + ) -> DiffMerger: + return DiffMerger( + db=db, + source_branch=source_branch, + destination_branch=default_branch, + diff_repository=mock_diff_repository, + serializer=DiffMergeSerializer(), + ) + + @pytest.fixture + async def person_node_branch(self, db: InfrahubDatabase, source_branch: Branch, car_person_schema) -> Node: + new_node = await Node.init(db=db, schema="TestPerson", branch=source_branch) + await new_node.new(db=db, name="Albert", height=172) + await new_node.save(db=db) + return new_node + + @pytest.fixture + async def person_node_main(self, db: InfrahubDatabase, default_branch: Branch, car_person_schema) -> Node: + new_node = await Node.init(db=db, schema="TestPerson", branch=default_branch) + await new_node.new(db=db, name="Albert", height=172) + await new_node.save(db=db) + return new_node + + @pytest.fixture + def empty_diff_root(self, default_branch: Branch, source_branch: Branch) -> EnrichedDiffRoot: + return EnrichedRootFactory.build( + base_branch_name=default_branch.name, + diff_branch_name=source_branch.name, + from_time=Timestamp(source_branch.get_created_at()), + to_time=Timestamp(), + uuid=str(uuid4()), + partner_uuid=str(uuid4()), + tracking_id=BranchTrackingId(name=source_branch.name), + nodes=set(), + ) + + def _get_empty_node_diff(self, node: Node, action: DiffAction) -> EnrichedDiffNode: + return EnrichedNodeFactory.build( + uuid=node.get_id(), action=action, kind=node.get_kind(), label="", attributes=set(), relationships=set() + ) + + async def test_merge_node_added( + self, + db: InfrahubDatabase, + default_branch: Branch, + source_branch: Branch, + person_node_branch: Node, + mock_diff_repository: DiffRepository, + diff_merger: DiffMerger, + empty_diff_root: EnrichedDiffRoot, + ): + added_node_diff = self._get_empty_node_diff(node=person_node_branch, action=DiffAction.ADDED) + empty_diff_root.nodes = {added_node_diff} + mock_diff_repository.get_one.return_value = empty_diff_root + at = Timestamp() + + await diff_merger.merge_graph(at=at) + + mock_diff_repository.get_one.assert_awaited_once_with( + diff_branch_name=source_branch.name, tracking_id=BranchTrackingId(name=source_branch.name) + ) + target_car = await NodeManager.get_one(db=db, branch=default_branch, id=person_node_branch.id) + assert target_car.id == person_node_branch.id + assert target_car.get_updated_at() == at + + async def test_merge_node_added_idempotent( + self, + db: InfrahubDatabase, + default_branch: Branch, + source_branch: Branch, + person_node_branch: Node, + mock_diff_repository: DiffRepository, + diff_merger: DiffMerger, + empty_diff_root: EnrichedDiffRoot, + ): + added_node_diff = self._get_empty_node_diff(node=person_node_branch, action=DiffAction.ADDED) + empty_diff_root.nodes = {added_node_diff} + mock_diff_repository.get_one.return_value = empty_diff_root + at = Timestamp() + + await diff_merger.merge_graph(at=at) + await diff_merger.merge_graph(at=at) + + assert mock_diff_repository.get_one.await_args_list == [ + call(diff_branch_name=source_branch.name, tracking_id=BranchTrackingId(name=source_branch.name)), + call(diff_branch_name=source_branch.name, tracking_id=BranchTrackingId(name=source_branch.name)), + ] + target_car = await NodeManager.get_one(db=db, branch=default_branch, id=person_node_branch.id) + assert target_car.id == person_node_branch.id + assert target_car.get_updated_at() == at + + async def test_merge_node_deleted( + self, + db: InfrahubDatabase, + default_branch: Branch, + person_node_main: Node, + source_branch: Branch, + mock_diff_repository: DiffRepository, + diff_merger: DiffMerger, + empty_diff_root: EnrichedDiffRoot, + ): + person_node_branch = await NodeManager.get_one(db=db, branch=source_branch, id=person_node_main.id) + await person_node_branch.delete(db=db) + deleted_node_diff = self._get_empty_node_diff(node=person_node_branch, action=DiffAction.REMOVED) + empty_diff_root.nodes = {deleted_node_diff} + mock_diff_repository.get_one.return_value = empty_diff_root + at = Timestamp() + + await diff_merger.merge_graph(at=at) + + mock_diff_repository.get_one.assert_awaited_once_with( + diff_branch_name=source_branch.name, tracking_id=BranchTrackingId(name=source_branch.name) + ) + with pytest.raises(NodeNotFoundError): + await NodeManager.get_one(db=db, branch=default_branch, id=person_node_main.id, raise_on_error=True) + + async def test_merge_node_deleted_idempotent( + self, + db: InfrahubDatabase, + default_branch: Branch, + person_node_main: Node, + source_branch: Branch, + mock_diff_repository: DiffRepository, + diff_merger: DiffMerger, + empty_diff_root: EnrichedDiffRoot, + ): + person_node_branch = await NodeManager.get_one(db=db, branch=source_branch, id=person_node_main.id) + await person_node_branch.delete(db=db) + deleted_node_diff = self._get_empty_node_diff(node=person_node_branch, action=DiffAction.REMOVED) + empty_diff_root.nodes = {deleted_node_diff} + mock_diff_repository.get_one.return_value = empty_diff_root + at = Timestamp() + + await diff_merger.merge_graph(at=at) + await diff_merger.merge_graph(at=at) + + assert mock_diff_repository.get_one.await_args_list == [ + call(diff_branch_name=source_branch.name, tracking_id=BranchTrackingId(name=source_branch.name)), + call(diff_branch_name=source_branch.name, tracking_id=BranchTrackingId(name=source_branch.name)), + ] + with pytest.raises(NodeNotFoundError): + await NodeManager.get_one(db=db, branch=default_branch, id=person_node_main.id, raise_on_error=True) + + @pytest.mark.parametrize( + "conflict_selection,expect_deleted", + [(ConflictSelection.DIFF_BRANCH, True), (ConflictSelection.BASE_BRANCH, False)], + ) + async def test_merge_node_deleted_with_conflict( + self, + db: InfrahubDatabase, + default_branch: Branch, + person_node_main: Node, + source_branch: Branch, + mock_diff_repository: DiffRepository, + diff_merger: DiffMerger, + empty_diff_root: EnrichedDiffRoot, + conflict_selection: ConflictSelection, + expect_deleted: bool, + ): + person_node_branch = await NodeManager.get_one(db=db, branch=source_branch, id=person_node_main.id) + await person_node_branch.delete(db=db) + deleted_node_diff = self._get_empty_node_diff(node=person_node_branch, action=DiffAction.REMOVED) + node_conflict = EnrichedConflictFactory.build( + base_branch_action=DiffAction.UPDATED, + diff_branch_action=DiffAction.REMOVED, + selected_branch=conflict_selection, + ) + deleted_node_diff.conflict = node_conflict + empty_diff_root.nodes = {deleted_node_diff} + mock_diff_repository.get_one.return_value = empty_diff_root + at = Timestamp() + + await diff_merger.merge_graph(at=at) + + mock_diff_repository.get_one.assert_awaited_once_with( + diff_branch_name=source_branch.name, tracking_id=BranchTrackingId(name=source_branch.name) + ) + if expect_deleted: + with pytest.raises(NodeNotFoundError): + await NodeManager.get_one(db=db, branch=default_branch, id=person_node_main.id, raise_on_error=True) + else: + target_car = await NodeManager.get_one(db=db, branch=default_branch, id=person_node_branch.id) + assert target_car.id == person_node_branch.id + assert target_car.get_updated_at() < at diff --git a/backend/tests/unit/core/diff/test_diff_payload.py b/backend/tests/unit/core/diff/test_diff_payload.py index 5d8aaf4df3..fbf70a2d35 100644 --- a/backend/tests/unit/core/diff/test_diff_payload.py +++ b/backend/tests/unit/core/diff/test_diff_payload.py @@ -10,7 +10,7 @@ from infrahub.core.manager import NodeManager from infrahub.core.node import Node from infrahub.core.schema.relationship_schema import RelationshipSchema -from infrahub.core.schema_manager import SchemaBranch +from infrahub.core.schema.schema_branch import SchemaBranch from infrahub.database import InfrahubDatabase diff --git a/backend/tests/unit/core/ipam/conftest.py b/backend/tests/unit/core/ipam/conftest.py index 173beccebe..8daaeb0f49 100644 --- a/backend/tests/unit/core/ipam/conftest.py +++ b/backend/tests/unit/core/ipam/conftest.py @@ -4,7 +4,7 @@ from infrahub.core.branch import Branch from infrahub.core.constants import InfrahubKind from infrahub.core.node import Node -from infrahub.core.schema_manager import SchemaBranch +from infrahub.core.schema.schema_branch import SchemaBranch from infrahub.database import InfrahubDatabase diff --git a/backend/tests/unit/core/ipam/test_ipam.py b/backend/tests/unit/core/ipam/test_ipam.py index 70dda42230..68c898aa72 100644 --- a/backend/tests/unit/core/ipam/test_ipam.py +++ b/backend/tests/unit/core/ipam/test_ipam.py @@ -12,7 +12,7 @@ IPPrefixSubnetFetch, get_ip_addresses, ) -from infrahub.core.schema_manager import SchemaBranch +from infrahub.core.schema.schema_branch import SchemaBranch from infrahub.database import InfrahubDatabase diff --git a/backend/tests/unit/core/ipam/test_ipam_reconcile_query.py b/backend/tests/unit/core/ipam/test_ipam_reconcile_query.py index 17de8559b9..41fa172ec6 100644 --- a/backend/tests/unit/core/ipam/test_ipam_reconcile_query.py +++ b/backend/tests/unit/core/ipam/test_ipam_reconcile_query.py @@ -6,7 +6,7 @@ from infrahub.core.initialization import create_branch, create_ipam_namespace, get_default_ipnamespace from infrahub.core.node import Node from infrahub.core.query.ipam import IPPrefixReconcileQuery -from infrahub.core.schema_manager import SchemaBranch +from infrahub.core.schema.schema_branch import SchemaBranch from infrahub.database import InfrahubDatabase diff --git a/backend/tests/unit/core/migrations/graph/test_003.py b/backend/tests/unit/core/migrations/graph/test_003.py index d8a0b118d9..ae5358a793 100644 --- a/backend/tests/unit/core/migrations/graph/test_003.py +++ b/backend/tests/unit/core/migrations/graph/test_003.py @@ -3,7 +3,7 @@ from infrahub.core.migrations.graph.m003_relationship_parent_optional import Migration003, Migration003Query01 from infrahub.core.node import Node from infrahub.core.schema import SchemaRoot, internal_schema -from infrahub.core.schema_manager import SchemaBranch +from infrahub.core.schema.schema_branch import SchemaBranch from infrahub.core.utils import count_relationships from infrahub.database import InfrahubDatabase diff --git a/backend/tests/unit/core/migrations/graph/test_012.py b/backend/tests/unit/core/migrations/graph/test_012.py index 00e162d811..f7d41d91df 100644 --- a/backend/tests/unit/core/migrations/graph/test_012.py +++ b/backend/tests/unit/core/migrations/graph/test_012.py @@ -11,7 +11,7 @@ ) from infrahub.core.node import Node from infrahub.core.schema import AttributeSchema, NodeSchema, RelationshipSchema, SchemaRoot, internal_schema -from infrahub.core.schema_manager import SchemaBranch +from infrahub.core.schema.schema_branch import SchemaBranch from infrahub.core.utils import count_nodes, count_relationships from infrahub.database import InfrahubDatabase diff --git a/backend/tests/unit/core/migrations/graph/test_013.py b/backend/tests/unit/core/migrations/graph/test_013.py index e515b93a24..31e39c38c7 100644 --- a/backend/tests/unit/core/migrations/graph/test_013.py +++ b/backend/tests/unit/core/migrations/graph/test_013.py @@ -12,7 +12,7 @@ ) from infrahub.core.node import Node from infrahub.core.schema import AttributeSchema, NodeSchema, RelationshipSchema, SchemaRoot, internal_schema -from infrahub.core.schema_manager import SchemaBranch +from infrahub.core.schema.schema_branch import SchemaBranch from infrahub.core.utils import count_nodes, count_relationships from infrahub.database import InfrahubDatabase diff --git a/backend/tests/unit/core/resource_manager/test_ipaddress_pool.py b/backend/tests/unit/core/resource_manager/test_ipaddress_pool.py index 1e88ae571d..7e369610b2 100644 --- a/backend/tests/unit/core/resource_manager/test_ipaddress_pool.py +++ b/backend/tests/unit/core/resource_manager/test_ipaddress_pool.py @@ -5,7 +5,7 @@ from infrahub.core.constants import InfrahubKind from infrahub.core.node import Node from infrahub.core.node.resource_manager.ip_address_pool import CoreIPAddressPool -from infrahub.core.schema_manager import SchemaBranch +from infrahub.core.schema.schema_branch import SchemaBranch from infrahub.database import InfrahubDatabase from infrahub.exceptions import PoolExhaustedError diff --git a/backend/tests/unit/core/resource_manager/test_prefix_pool.py b/backend/tests/unit/core/resource_manager/test_prefix_pool.py index 4695a784e8..134309784f 100644 --- a/backend/tests/unit/core/resource_manager/test_prefix_pool.py +++ b/backend/tests/unit/core/resource_manager/test_prefix_pool.py @@ -6,7 +6,7 @@ from infrahub.core.initialization import create_branch from infrahub.core.node import Node from infrahub.core.node.resource_manager.ip_prefix_pool import CoreIPPrefixPool -from infrahub.core.schema_manager import SchemaBranch +from infrahub.core.schema.schema_branch import SchemaBranch from infrahub.database import InfrahubDatabase diff --git a/backend/tests/unit/core/schema_manager/test_manager_schema.py b/backend/tests/unit/core/schema_manager/test_manager_schema.py index 625447051a..23ad98c2f1 100644 --- a/backend/tests/unit/core/schema_manager/test_manager_schema.py +++ b/backend/tests/unit/core/schema_manager/test_manager_schema.py @@ -1,9 +1,9 @@ import copy +import json import re import uuid import pytest -from deepdiff import DeepDiff from infrahub_sdk.utils import compare_lists from infrahub.core import registry @@ -11,8 +11,6 @@ from infrahub.core.constants import ( AllowOverrideType, BranchSupportType, - FilterSchemaKind, - HashableModelState, InfrahubKind, RelationshipDeleteBehavior, RelationshipKind, @@ -25,7 +23,8 @@ core_models, internal_schema, ) -from infrahub.core.schema_manager import SchemaBranch, SchemaManager +from infrahub.core.schema.manager import SchemaManager +from infrahub.core.schema.schema_branch import SchemaBranch from infrahub.database import InfrahubDatabase from infrahub.exceptions import SchemaNotFoundError, ValidationError @@ -1489,513 +1488,23 @@ async def test_schema_branch_validate_count_against_cardinality_invalid(relation schema_branch.validate_count_against_cardinality() -async def test_schema_branch_process_filters( - db: InfrahubDatabase, reset_registry, default_branch: Branch, register_internal_models_schema -): - FULL_SCHEMA = { - "nodes": [ - { - "name": "Criticality", - "namespace": "Builtin", - "default_filter": "name__value", - "human_friendly_id": ["name__value"], - "label": "Criticality", - "attributes": [ - {"name": "name", "kind": "Text", "label": "Name", "unique": True}, - {"name": "level", "kind": "Number", "label": "Level"}, - {"name": "color", "kind": "Text", "label": "Color", "default_value": "#444444"}, - {"name": "description", "kind": "Text", "label": "Description", "optional": True}, - ], - "relationships": [ - { - "name": "tags", - "peer": InfrahubKind.TAG, - "label": "Tags", - "optional": True, - "cardinality": "many", - }, - { - "name": "primary_tag", - "peer": InfrahubKind.TAG, - "label": "Primary Tag", - "identifier": "primary_tag__criticality", - "optional": True, - "cardinality": "one", - }, - ], - }, - { - "name": "Tag", - "namespace": "Builtin", - "label": "Tag", - "default_filter": "name__value", - "attributes": [ - {"name": "name", "kind": "Text", "label": "Name", "unique": True}, - {"name": "description", "kind": "Text", "label": "Description", "optional": True}, - ], - }, - ] - } +async def test_schema_branch_from_dict_schema_object(): + schema = SchemaRoot(**core_models) schema_branch = SchemaBranch(cache={}, name="test") - schema_branch.load_schema(schema=SchemaRoot(**FULL_SCHEMA)) - schema_branch.process_filters() + schema_branch.load_schema(schema=schema) - assert len(schema_branch.nodes) == 2 - criticality_dict = schema_branch.get("BuiltinCriticality").model_dump() - tag_dict = schema_branch.get("BuiltinTag").model_dump() + exported = schema_branch.to_dict_schema_object() - criticality_expected_filters = [ - { - "id": None, - "name": "ids", - "kind": FilterSchemaKind.TEXT, - "enum": None, - "object_kind": None, - "description": None, - "state": HashableModelState.PRESENT, - }, - { - "description": None, - "enum": None, - "id": None, - "kind": FilterSchemaKind.TEXT, - "name": "hfid", - "object_kind": None, - "state": HashableModelState.PRESENT, - }, - { - "id": None, - "name": "name__value", - "kind": FilterSchemaKind.TEXT, - "enum": None, - "object_kind": None, - "description": None, - "state": HashableModelState.PRESENT, - }, - { - "id": None, - "name": "name__values", - "kind": FilterSchemaKind.TEXT, - "enum": None, - "object_kind": None, - "description": None, - "state": HashableModelState.PRESENT, - }, - { - "id": None, - "name": "name__is_visible", - "kind": FilterSchemaKind.BOOLEAN, - "enum": None, - "object_kind": None, - "description": None, - "state": HashableModelState.PRESENT, - }, - { - "id": None, - "name": "name__is_protected", - "kind": FilterSchemaKind.BOOLEAN, - "enum": None, - "object_kind": None, - "description": None, - "state": HashableModelState.PRESENT, - }, - { - "id": None, - "name": "name__source__id", - "kind": FilterSchemaKind.TEXT, - "enum": None, - "object_kind": None, - "description": None, - "state": HashableModelState.PRESENT, - }, - { - "id": None, - "name": "name__owner__id", - "kind": FilterSchemaKind.TEXT, - "enum": None, - "object_kind": None, - "description": None, - "state": HashableModelState.PRESENT, - }, - { - "id": None, - "name": "level__value", - "kind": FilterSchemaKind.NUMBER, - "enum": None, - "object_kind": None, - "description": None, - "state": HashableModelState.PRESENT, - }, - { - "id": None, - "name": "level__values", - "kind": FilterSchemaKind.TEXT, - "enum": None, - "object_kind": None, - "description": None, - "state": HashableModelState.PRESENT, - }, - { - "id": None, - "name": "level__is_visible", - "kind": FilterSchemaKind.BOOLEAN, - "enum": None, - "object_kind": None, - "description": None, - "state": HashableModelState.PRESENT, - }, - { - "id": None, - "name": "level__is_protected", - "kind": FilterSchemaKind.BOOLEAN, - "enum": None, - "object_kind": None, - "description": None, - "state": HashableModelState.PRESENT, - }, - { - "id": None, - "name": "level__source__id", - "kind": FilterSchemaKind.TEXT, - "enum": None, - "object_kind": None, - "description": None, - "state": HashableModelState.PRESENT, - }, - { - "id": None, - "name": "level__owner__id", - "kind": FilterSchemaKind.TEXT, - "enum": None, - "object_kind": None, - "description": None, - "state": HashableModelState.PRESENT, - }, - { - "id": None, - "name": "color__value", - "kind": FilterSchemaKind.TEXT, - "enum": None, - "object_kind": None, - "description": None, - "state": HashableModelState.PRESENT, - }, - { - "id": None, - "name": "color__values", - "kind": FilterSchemaKind.TEXT, - "enum": None, - "object_kind": None, - "description": None, - "state": HashableModelState.PRESENT, - }, - { - "id": None, - "name": "color__is_visible", - "kind": FilterSchemaKind.BOOLEAN, - "enum": None, - "object_kind": None, - "description": None, - "state": HashableModelState.PRESENT, - }, - { - "id": None, - "name": "color__is_protected", - "kind": FilterSchemaKind.BOOLEAN, - "enum": None, - "object_kind": None, - "description": None, - "state": HashableModelState.PRESENT, - }, - { - "id": None, - "name": "color__source__id", - "kind": FilterSchemaKind.TEXT, - "enum": None, - "object_kind": None, - "description": None, - "state": HashableModelState.PRESENT, - }, - { - "id": None, - "name": "color__owner__id", - "kind": FilterSchemaKind.TEXT, - "enum": None, - "object_kind": None, - "description": None, - "state": HashableModelState.PRESENT, - }, - { - "id": None, - "name": "description__value", - "kind": FilterSchemaKind.TEXT, - "enum": None, - "object_kind": None, - "description": None, - "state": HashableModelState.PRESENT, - }, - { - "id": None, - "name": "description__values", - "kind": FilterSchemaKind.TEXT, - "enum": None, - "object_kind": None, - "description": None, - "state": HashableModelState.PRESENT, - }, - { - "id": None, - "name": "description__is_visible", - "kind": FilterSchemaKind.BOOLEAN, - "enum": None, - "object_kind": None, - "description": None, - "state": HashableModelState.PRESENT, - }, - { - "id": None, - "name": "description__is_protected", - "kind": FilterSchemaKind.BOOLEAN, - "enum": None, - "object_kind": None, - "description": None, - "state": HashableModelState.PRESENT, - }, - { - "id": None, - "name": "description__source__id", - "kind": FilterSchemaKind.TEXT, - "enum": None, - "object_kind": None, - "description": None, - "state": HashableModelState.PRESENT, - }, - { - "id": None, - "name": "description__owner__id", - "kind": FilterSchemaKind.TEXT, - "enum": None, - "object_kind": None, - "description": None, - "state": HashableModelState.PRESENT, - }, - { - "id": None, - "name": "any__value", - "kind": FilterSchemaKind.TEXT, - "enum": None, - "object_kind": None, - "description": None, - "state": HashableModelState.PRESENT, - }, - { - "id": None, - "name": "any__is_visible", - "kind": FilterSchemaKind.BOOLEAN, - "enum": None, - "object_kind": None, - "description": None, - "state": HashableModelState.PRESENT, - }, - { - "id": None, - "name": "any__is_protected", - "kind": FilterSchemaKind.BOOLEAN, - "enum": None, - "object_kind": None, - "description": None, - "state": HashableModelState.PRESENT, - }, - { - "id": None, - "name": "any__source__id", - "kind": FilterSchemaKind.TEXT, - "enum": None, - "object_kind": None, - "description": None, - "state": HashableModelState.PRESENT, - }, - { - "id": None, - "name": "any__owner__id", - "kind": FilterSchemaKind.TEXT, - "enum": None, - "object_kind": None, - "description": None, - "state": HashableModelState.PRESENT, - }, - ] - tag_expected_filters = [ - { - "description": None, - "enum": None, - "id": None, - "kind": FilterSchemaKind.TEXT, - "name": "ids", - "object_kind": None, - "state": HashableModelState.PRESENT, - }, - { - "description": None, - "enum": None, - "id": None, - "kind": FilterSchemaKind.TEXT, - "name": "name__value", - "object_kind": None, - "state": HashableModelState.PRESENT, - }, - { - "description": None, - "enum": None, - "id": None, - "kind": FilterSchemaKind.TEXT, - "name": "name__values", - "object_kind": None, - "state": HashableModelState.PRESENT, - }, - { - "description": None, - "enum": None, - "id": None, - "kind": FilterSchemaKind.BOOLEAN, - "name": "name__is_visible", - "object_kind": None, - "state": HashableModelState.PRESENT, - }, - { - "description": None, - "enum": None, - "id": None, - "kind": FilterSchemaKind.BOOLEAN, - "name": "name__is_protected", - "object_kind": None, - "state": HashableModelState.PRESENT, - }, - { - "description": None, - "enum": None, - "id": None, - "kind": FilterSchemaKind.TEXT, - "name": "name__source__id", - "object_kind": None, - "state": HashableModelState.PRESENT, - }, - { - "description": None, - "enum": None, - "id": None, - "kind": FilterSchemaKind.TEXT, - "name": "name__owner__id", - "object_kind": None, - "state": HashableModelState.PRESENT, - }, - { - "description": None, - "enum": None, - "id": None, - "kind": FilterSchemaKind.TEXT, - "name": "description__value", - "object_kind": None, - "state": HashableModelState.PRESENT, - }, - { - "description": None, - "enum": None, - "id": None, - "kind": FilterSchemaKind.TEXT, - "name": "description__values", - "object_kind": None, - "state": HashableModelState.PRESENT, - }, - { - "description": None, - "enum": None, - "id": None, - "kind": FilterSchemaKind.BOOLEAN, - "name": "description__is_visible", - "object_kind": None, - "state": HashableModelState.PRESENT, - }, - { - "description": None, - "enum": None, - "id": None, - "kind": FilterSchemaKind.BOOLEAN, - "name": "description__is_protected", - "object_kind": None, - "state": HashableModelState.PRESENT, - }, - { - "description": None, - "enum": None, - "id": None, - "kind": FilterSchemaKind.TEXT, - "name": "description__source__id", - "object_kind": None, - "state": HashableModelState.PRESENT, - }, - { - "description": None, - "enum": None, - "id": None, - "kind": FilterSchemaKind.TEXT, - "name": "description__owner__id", - "object_kind": None, - "state": HashableModelState.PRESENT, - }, - { - "description": None, - "enum": None, - "id": None, - "kind": FilterSchemaKind.TEXT, - "name": "any__value", - "object_kind": None, - "state": HashableModelState.PRESENT, - }, - { - "description": None, - "enum": None, - "id": None, - "kind": FilterSchemaKind.BOOLEAN, - "name": "any__is_visible", - "object_kind": None, - "state": HashableModelState.PRESENT, - }, - { - "description": None, - "enum": None, - "id": None, - "kind": FilterSchemaKind.BOOLEAN, - "name": "any__is_protected", - "object_kind": None, - "state": HashableModelState.PRESENT, - }, - { - "description": None, - "enum": None, - "id": None, - "kind": FilterSchemaKind.TEXT, - "name": "any__source__id", - "object_kind": None, - "state": HashableModelState.PRESENT, - }, - { - "description": None, - "enum": None, - "id": None, - "kind": FilterSchemaKind.TEXT, - "name": "any__owner__id", - "object_kind": None, - "state": HashableModelState.PRESENT, - }, - ] + exported_json = json.dumps(exported, default=lambda x: x.dict()) - assert criticality_dict["filters"] == criticality_expected_filters - assert not DeepDiff(criticality_dict["filters"], criticality_expected_filters, ignore_order=True) + exported_dict = json.loads(exported_json) + schema_branch_after = SchemaBranch.from_dict_schema_object(data=exported_dict) - assert tag_dict["filters"] == tag_expected_filters - assert not DeepDiff(tag_dict["filters"], tag_expected_filters, ignore_order=True) + assert ( + schema_branch_after.get_node(name=InfrahubKind.TAG).get_hash() + == schema_branch.get_node(name=InfrahubKind.TAG).get_hash() + ) async def test_process_relationships_on_delete_defaults_set(schema_all_in_one): diff --git a/backend/tests/unit/core/schema_manager/test_parent_component_validation.py b/backend/tests/unit/core/schema_manager/test_parent_component_validation.py index cfd8831343..641fa5ff10 100644 --- a/backend/tests/unit/core/schema_manager/test_parent_component_validation.py +++ b/backend/tests/unit/core/schema_manager/test_parent_component_validation.py @@ -3,7 +3,7 @@ import pytest from infrahub.core.schema import SchemaRoot -from infrahub.core.schema_manager import SchemaBranch +from infrahub.core.schema.schema_branch import SchemaBranch from .conftest import _get_schema_by_kind diff --git a/backend/tests/unit/core/schema_manager/test_validate_schema_path.py b/backend/tests/unit/core/schema_manager/test_validate_schema_path.py index 10c3d35a15..82a01b4f3e 100644 --- a/backend/tests/unit/core/schema_manager/test_validate_schema_path.py +++ b/backend/tests/unit/core/schema_manager/test_validate_schema_path.py @@ -7,7 +7,7 @@ from infrahub.core.schema import ( SchemaRoot, ) -from infrahub.core.schema_manager import SchemaBranch +from infrahub.core.schema.schema_branch import SchemaBranch from infrahub.database import InfrahubDatabase diff --git a/backend/tests/unit/core/test_manager_node.py b/backend/tests/unit/core/test_manager_node.py index f256f8146b..1bf684cbb9 100644 --- a/backend/tests/unit/core/test_manager_node.py +++ b/backend/tests/unit/core/test_manager_node.py @@ -8,7 +8,7 @@ from infrahub.core.query.node import NodeToProcess from infrahub.core.registry import registry from infrahub.core.schema import NodeSchema -from infrahub.core.schema_manager import SchemaBranch +from infrahub.core.schema.schema_branch import SchemaBranch from infrahub.core.timestamp import Timestamp from infrahub.database import InfrahubDatabase from infrahub.exceptions import NodeNotFoundError diff --git a/backend/tests/unit/core/test_registry.py b/backend/tests/unit/core/test_registry.py index f301c2d9ff..a0a99f538a 100644 --- a/backend/tests/unit/core/test_registry.py +++ b/backend/tests/unit/core/test_registry.py @@ -1,7 +1,7 @@ from infrahub.core.branch import Branch from infrahub.core.registry import registry from infrahub.core.schema import SchemaRoot, internal_schema -from infrahub.core.schema_manager import SchemaManager +from infrahub.core.schema.manager import SchemaManager from infrahub.database import InfrahubDatabase diff --git a/backend/tests/unit/core/test_relationship.py b/backend/tests/unit/core/test_relationship.py index f9c3d7d0f7..6b2c90eadd 100644 --- a/backend/tests/unit/core/test_relationship.py +++ b/backend/tests/unit/core/test_relationship.py @@ -9,7 +9,7 @@ from infrahub.core.node.resource_manager.ip_prefix_pool import CoreIPPrefixPool from infrahub.core.query.relationship import RelationshipGetPeerQuery from infrahub.core.relationship.model import Relationship, RelationshipValidatorList -from infrahub.core.schema_manager import SchemaBranch +from infrahub.core.schema.schema_branch import SchemaBranch from infrahub.core.timestamp import Timestamp from infrahub.database import InfrahubDatabase diff --git a/backend/tests/unit/core/test_schema_parse_schema_path.py b/backend/tests/unit/core/test_schema_parse_schema_path.py index 58e3146f45..c0de6cd8aa 100644 --- a/backend/tests/unit/core/test_schema_parse_schema_path.py +++ b/backend/tests/unit/core/test_schema_parse_schema_path.py @@ -2,7 +2,7 @@ from infrahub.core import registry from infrahub.core.schema import AttributePathParsingError, SchemaAttributePath -from infrahub.core.schema_manager import SchemaBranch +from infrahub.core.schema.schema_branch import SchemaBranch class TestSchemaParseSchemaPath: diff --git a/backend/tests/unit/git/test_git_rpc.py b/backend/tests/unit/git/test_git_rpc.py index 3150e444ee..05bcb7bc99 100644 --- a/backend/tests/unit/git/test_git_rpc.py +++ b/backend/tests/unit/git/test_git_rpc.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Optional +from typing import TYPE_CHECKING, Any, Optional, Self from unittest.mock import AsyncMock, patch from infrahub_sdk import UUIDT, Config, InfrahubClient @@ -27,7 +27,7 @@ class AsyncContextManagerMock: - async def __aenter__(self, *args: Any, **kwargs: Any): + async def __aenter__(self, *args: Any, **kwargs: Any) -> Self: return self async def __aexit__( @@ -35,10 +35,10 @@ async def __aexit__( exc_type: Optional[type[BaseException]], exc_value: Optional[BaseException], traceback: Optional[TracebackType], - ): + ) -> None: pass - def __call__(self, *args: Any, **kwargs: Any): + def __call__(self, *args: Any, **kwargs: Any) -> Self: return self diff --git a/sync/examples/nautobot-v1_to_infrahub/nautobot/__init__.py b/backend/tests/unit/graphql/auth/query_permission_checker/__init__.py similarity index 100% rename from sync/examples/nautobot-v1_to_infrahub/nautobot/__init__.py rename to backend/tests/unit/graphql/auth/query_permission_checker/__init__.py diff --git a/backend/tests/unit/graphql/auth/query_permission_checker/test_default_branch_checker.py b/backend/tests/unit/graphql/auth/query_permission_checker/test_default_branch_checker.py new file mode 100644 index 0000000000..bcfb81fcbd --- /dev/null +++ b/backend/tests/unit/graphql/auth/query_permission_checker/test_default_branch_checker.py @@ -0,0 +1,138 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING +from unittest.mock import MagicMock +from uuid import uuid4 + +import pytest + +from infrahub.auth import AccountSession, AuthType +from infrahub.core.constants import AccountRole, GlobalPermissions, InfrahubKind +from infrahub.core.node import Node +from infrahub.core.registry import registry +from infrahub.exceptions import PermissionDeniedError +from infrahub.graphql.analyzer import InfrahubGraphQLQueryAnalyzer +from infrahub.graphql.auth.query_permission_checker.default_branch_checker import DefaultBranchPermissionChecker +from infrahub.graphql.auth.query_permission_checker.interface import CheckerResolution +from infrahub.graphql.initialization import GraphqlParams +from infrahub.permissions.local_backend import LocalPermissionBackend + +if TYPE_CHECKING: + from infrahub.core.branch import Branch + from infrahub.core.protocols import CoreAccount + from infrahub.database import InfrahubDatabase + from tests.unit.graphql.conftest import PermissionsHelper + + +class TestDefaultBranchPermission: + async def test_setup( + self, + db: InfrahubDatabase, + register_core_models_schema: None, + default_branch: Branch, + first_account: CoreAccount, + second_account: CoreAccount, + permissions_helper: PermissionsHelper, + ): + registry.permission_backends = [LocalPermissionBackend()] + permissions_helper._default_branch = default_branch + + permission = await Node.init(db=db, schema=InfrahubKind.GLOBALPERMISSION) + await permission.new( + db=db, name=GlobalPermissions.EDIT_DEFAULT_BRANCH.value, action=GlobalPermissions.EDIT_DEFAULT_BRANCH.value + ) + await permission.save(db=db) + + role = await Node.init(db=db, schema=InfrahubKind.ACCOUNTROLE) + await role.new(db=db, name="admin", permissions=[permission]) + await role.save(db=db) + + group = await Node.init(db=db, schema=InfrahubKind.ACCOUNTGROUP) + await group.new(db=db, name="admin", roles=[role]) + await group.save(db=db) + + await group.members.add(db=db, data={"id": first_account.id}) + await group.members.save(db=db) + + permissions_helper._first = first_account + permissions_helper._second = second_account + + @pytest.mark.parametrize( + "user", + [ + AccountSession(account_id="abc", auth_type=AuthType.JWT, role=AccountRole.ADMIN), + AccountSession(authenticated=False, account_id="anonymous", auth_type=AuthType.NONE), + ], + ) + async def test_supports_default_branch_permission_accounts( + self, user: AccountSession, db: InfrahubDatabase, permissions_helper: PermissionsHelper + ): + checker = DefaultBranchPermissionChecker() + is_supported = await checker.supports(db=db, account_session=user, branch=permissions_helper.default_branch) + assert is_supported == user.authenticated + + @pytest.mark.parametrize( + "contains_mutation,branch_name", + [(True, "main"), (False, "main"), (True, "not_default_branch"), (False, "not_default_branch")], + ) + async def test_account_with_permission( + self, db: InfrahubDatabase, permissions_helper: PermissionsHelper, contains_mutation: bool, branch_name: str + ): + checker = DefaultBranchPermissionChecker() + session = AccountSession( + authenticated=True, account_id=permissions_helper.first.id, session_id=str(uuid4()), auth_type=AuthType.JWT + ) + + graphql_query = MagicMock(spec=InfrahubGraphQLQueryAnalyzer) + graphql_query.branch = MagicMock() + graphql_query.branch.name = branch_name + graphql_query.contains_mutation = contains_mutation + graphql_query.operation_name = "CreateTags" + + resolution = await checker.check( + db=db, + account_session=session, + analyzed_query=graphql_query, + query_parameters=MagicMock(spec=GraphqlParams), + branch=permissions_helper.default_branch, + ) + assert resolution == CheckerResolution.NEXT_CHECKER + + @pytest.mark.parametrize( + "contains_mutation,branch_name", + [(True, "main"), (False, "main"), (True, "not_default_branch"), (False, "not_default_branch")], + ) + async def test_account_without_permission( + self, db: InfrahubDatabase, permissions_helper: PermissionsHelper, contains_mutation: bool, branch_name: str + ): + checker = DefaultBranchPermissionChecker() + session = AccountSession( + authenticated=True, account_id=permissions_helper.second.id, session_id=str(uuid4()), auth_type=AuthType.JWT + ) + + graphql_query = MagicMock(spec=InfrahubGraphQLQueryAnalyzer) + graphql_query.branch = MagicMock() + graphql_query.branch.name = branch_name + graphql_query.contains_mutation = contains_mutation + graphql_query.operation_name = "CreateTags" + + if not contains_mutation or branch_name != "main": + resolution = await checker.check( + db=db, + account_session=session, + analyzed_query=graphql_query, + query_parameters=MagicMock(spec=GraphqlParams), + branch=permissions_helper.default_branch, + ) + assert resolution == CheckerResolution.NEXT_CHECKER + else: + with pytest.raises( + PermissionDeniedError, match=r"You are not allowed to change data in the default branch" + ): + await checker.check( + db=db, + account_session=session, + analyzed_query=graphql_query, + query_parameters=MagicMock(spec=GraphqlParams), + branch=permissions_helper.default_branch, + ) diff --git a/backend/tests/unit/graphql/auth/query_permission_checker/test_merge_operation_checker.py b/backend/tests/unit/graphql/auth/query_permission_checker/test_merge_operation_checker.py new file mode 100644 index 0000000000..ed31289eb4 --- /dev/null +++ b/backend/tests/unit/graphql/auth/query_permission_checker/test_merge_operation_checker.py @@ -0,0 +1,141 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING +from unittest.mock import AsyncMock, MagicMock +from uuid import uuid4 + +import pytest + +from infrahub.auth import AccountSession, AuthType +from infrahub.core.constants import AccountRole, GlobalPermissions, InfrahubKind +from infrahub.core.node import Node +from infrahub.core.registry import registry +from infrahub.exceptions import PermissionDeniedError +from infrahub.graphql.analyzer import InfrahubGraphQLQueryAnalyzer +from infrahub.graphql.auth.query_permission_checker.interface import CheckerResolution +from infrahub.graphql.auth.query_permission_checker.merge_operation_checker import MergeBranchPermissionChecker +from infrahub.graphql.initialization import GraphqlParams +from infrahub.permissions.local_backend import LocalPermissionBackend + +if TYPE_CHECKING: + from infrahub.core.branch import Branch + from infrahub.core.protocols import CoreAccount + from infrahub.database import InfrahubDatabase + from tests.unit.graphql.conftest import PermissionsHelper + + +class TestMergeBranchPermission: + async def test_setup( + self, + db: InfrahubDatabase, + register_core_models_schema: None, + default_branch: Branch, + permissions_helper: PermissionsHelper, + first_account: CoreAccount, + second_account: CoreAccount, + ): + registry.permission_backends = [LocalPermissionBackend()] + permissions_helper._default_branch = default_branch + + permission = await Node.init(db=db, schema=InfrahubKind.GLOBALPERMISSION) + await permission.new( + db=db, name=GlobalPermissions.MERGE_BRANCH.value, action=GlobalPermissions.MERGE_BRANCH.value + ) + await permission.save(db=db) + + role = await Node.init(db=db, schema=InfrahubKind.ACCOUNTROLE) + await role.new(db=db, name="admin", permissions=[permission]) + await role.save(db=db) + + group = await Node.init(db=db, schema=InfrahubKind.ACCOUNTGROUP) + await group.new(db=db, name="admin", roles=[role]) + await group.save(db=db) + + await group.members.add(db=db, data={"id": first_account.id}) + await group.members.save(db=db) + + permissions_helper._first = first_account + permissions_helper._second = second_account + + @pytest.mark.parametrize( + "user", + [ + AccountSession(account_id="abc", auth_type=AuthType.JWT, role=AccountRole.ADMIN), + AccountSession(authenticated=False, account_id="anonymous", auth_type=AuthType.NONE), + ], + ) + async def test_supports_merge_branch_permission_accounts( + self, user: AccountSession, db: InfrahubDatabase, permissions_helper: PermissionsHelper + ): + checker = MergeBranchPermissionChecker() + is_supported = await checker.supports(db=db, account_session=user, branch=permissions_helper.default_branch) + assert is_supported == user.authenticated + + @pytest.mark.parametrize( + "operation_name,checker_resolution", + [("BranchMerge", CheckerResolution.TERMINATE), ("BuiltinTagCreate", CheckerResolution.NEXT_CHECKER)], + ) + async def test_account_with_permission( + self, + operation_name: str, + checker_resolution: None | CheckerResolution, + db: InfrahubDatabase, + permissions_helper: PermissionsHelper, + ): + checker = MergeBranchPermissionChecker() + graphql_query = AsyncMock(spec=InfrahubGraphQLQueryAnalyzer) + graphql_query.operation_name = "Foo" + graphql_query.operations = [MagicMock()] + graphql_query.operations[0].name = operation_name + + session = AccountSession( + authenticated=True, account_id=permissions_helper.first.id, session_id=str(uuid4()), auth_type=AuthType.JWT + ) + resolution = await checker.check( + db=db, + account_session=session, + analyzed_query=graphql_query, + query_parameters=MagicMock(spec=GraphqlParams), + branch=permissions_helper.default_branch, + ) + assert resolution == checker_resolution + + @pytest.mark.parametrize( + "operation_name,checker_resolution", + [("BranchMerge", None), ("BuiltinTagCreate", CheckerResolution.NEXT_CHECKER)], + ) + async def test_account_without_permission( + self, + operation_name: str, + checker_resolution: None | CheckerResolution, + db: InfrahubDatabase, + permissions_helper: PermissionsHelper, + ): + checker = MergeBranchPermissionChecker() + graphql_query = AsyncMock(spec=InfrahubGraphQLQueryAnalyzer) + graphql_query.operation_name = "Foo" + graphql_query.operations = [MagicMock()] + graphql_query.operations[0].name = operation_name + + session = AccountSession( + authenticated=True, account_id=permissions_helper.second.id, session_id=str(uuid4()), auth_type=AuthType.JWT + ) + + if checker_resolution is None: + with pytest.raises(PermissionDeniedError, match=r"You are not allowed to merge a branch"): + await checker.check( + db=db, + account_session=session, + analyzed_query=graphql_query, + query_parameters=MagicMock(spec=GraphqlParams), + branch=permissions_helper.default_branch, + ) + else: + resolution = await checker.check( + db=db, + account_session=session, + analyzed_query=graphql_query, + query_parameters=MagicMock(spec=GraphqlParams), + branch=permissions_helper.default_branch, + ) + assert resolution == checker_resolution diff --git a/backend/tests/unit/graphql/auth/query_permission_checker/test_object_permission_checker.py b/backend/tests/unit/graphql/auth/query_permission_checker/test_object_permission_checker.py new file mode 100644 index 0000000000..ab0a12665e --- /dev/null +++ b/backend/tests/unit/graphql/auth/query_permission_checker/test_object_permission_checker.py @@ -0,0 +1,710 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING +from uuid import uuid4 + +import pytest + +from infrahub.auth import AccountSession, AuthType +from infrahub.core.account import ObjectPermission +from infrahub.core.constants import ( + AccountRole, + GlobalPermissions, + InfrahubKind, + PermissionAction, + PermissionDecision, +) +from infrahub.core.node import Node +from infrahub.core.registry import registry +from infrahub.exceptions import PermissionDeniedError +from infrahub.graphql.analyzer import InfrahubGraphQLQueryAnalyzer +from infrahub.graphql.auth.query_permission_checker.interface import CheckerResolution +from infrahub.graphql.auth.query_permission_checker.object_permission_checker import ( + AccountManagerPermissionChecker, + ObjectPermissionChecker, + PermissionManagerPermissionChecker, + RepositoryManagerPermissionChecker, +) +from infrahub.graphql.initialization import prepare_graphql_params +from infrahub.permissions.local_backend import LocalPermissionBackend + +if TYPE_CHECKING: + from infrahub.core.branch import Branch + from infrahub.core.protocols import CoreAccount + from infrahub.database import InfrahubDatabase + from tests.unit.graphql.conftest import PermissionsHelper + + +QUERY_TAGS = """ +query { + BuiltinTag { + edges { + node { + display_label + } + } + } +} +""" + +QUERY_REPOS = """ +query { + CoreRepository { + edges { + node { + display_label + } + } + } +} +""" + +QUERY_GRAPHQL = """ +query { + CoreGraphQLQuery { + edges { + node { + display_label + } + } + } +} +""" + +QUERY_GRAPHQL_AND_REPO = """ +query { + CoreGraphQLQuery { + edges { + node { + display_label + repository { + node { + display_label + } + } + } + } + } +} +""" + +MUTATION_ACCOUNT = """ +mutation { + CoreAccountCreate(data: { + name: {value: "test"} + password: {value: "test"} + }) { + ok + } +} +""" + +MUTATION_ACCOUNT_GROUP = """ +mutation { + CoreAccountGroupCreate(data: { + name: {value: "test"} + }) { + ok + } +} +""" + +MUTATION_ACCOUNT_ROLE = """ +mutation { + CoreAccountRoleCreate(data: { + name: {value: "test"} + }) { + ok + } +} +""" + +QUERY_ACCOUNT_PROFILE = """ +query { + AccountProfile { + name { + value + } + } +} +""" + +MUTATION_GLOBAL_PERMISSION = """ +mutation { + CoreGlobalPermissionCreate(data: { + name: {value: "Merge branch"} + action: {value: "merge_branch"} + }) { + ok + object { + identifier { + value + } + } + } +} +""" + +MUTATION_OBJECT_PERMISSION = """ +mutation { + CoreObjectPermissionCreate(data: { + branch: {value: "*"} + namespace: {value: "*"} + name: {value: "*"} + }) { + ok + object { + identifier { + value + } + } + } +} +""" + +QUERY_ACCOUNT_PERMISSIONS = """ +query { + InfrahubPermissions { + global_permissions { + edges { + node { + identifier + } + } + } + object_permissions { + edges { + node { + identifier + } + } + } + } +} +""" + +MUTATION_REPOSITORY = """ +mutation { + CoreRepositoryCreate(data: { + name: {value: "Test"} + location: {value: "/var/random"} + }) { + ok + } +} +""" + +MUTATION_READONLY_REPOSITORY = """ +mutation { + CoreReadOnlyRepositoryCreate(data: { + name: {value: "Test"} + location: {value: "/var/random"} + }) { + ok + } +} +""" + +MUTATION_GENERIC_REPOSITORY = """ +mutation { + CoreGenericRepositoryUpdate(data: { + name: {value: "Test"} + location: {value: "/var/random"} + }) { + ok + } +} +""" + + +class TestObjectPermissions: + async def test_setup( + self, + db: InfrahubDatabase, + register_core_models_schema: None, + default_branch: Branch, + permissions_helper: PermissionsHelper, + first_account: CoreAccount, + ): + registry.permission_backends = [LocalPermissionBackend()] + permissions_helper._default_branch = default_branch + + permissions = [] + for object_permission in [ + ObjectPermission( + id="", + branch="main", + namespace="Builtin", + name="*", + action=PermissionAction.ANY.value, + decision=PermissionDecision.ALLOW.value, + ), + ObjectPermission( + id="", + branch="main", + namespace="Core", + name="GraphQLQuery", + action=PermissionAction.VIEW.value, + decision=PermissionDecision.ALLOW.value, + ), + ]: + obj = await Node.init(db=db, schema=InfrahubKind.OBJECTPERMISSION) + await obj.new( + db=db, + branch=object_permission.branch, + namespace=object_permission.namespace, + name=object_permission.name, + action=object_permission.action, + decision=object_permission.decision, + ) + await obj.save(db=db) + permissions.append(obj) + + role = await Node.init(db=db, schema=InfrahubKind.ACCOUNTROLE) + await role.new(db=db, name="admin", permissions=permissions) + await role.save(db=db) + + group = await Node.init(db=db, schema=InfrahubKind.ACCOUNTGROUP) + await group.new(db=db, name="admin", roles=[role]) + await group.save(db=db) + + await group.members.add(db=db, data={"id": first_account.id}) + await group.members.save(db=db) + + permissions_helper._first = first_account + + async def test_first_account_tags(self, db: InfrahubDatabase, permissions_helper: PermissionsHelper) -> None: + gql_params = prepare_graphql_params(db=db, include_mutation=True, branch=permissions_helper.default_branch) + analyzed_query = InfrahubGraphQLQueryAnalyzer( + query=QUERY_TAGS, schema=gql_params.schema, branch=permissions_helper.default_branch + ) + perms = ObjectPermissionChecker() + session = AccountSession( + authenticated=True, + account_id=permissions_helper.first.id, + session_id=str(uuid4()), + auth_type=AuthType.JWT, + ) + + await perms.check( + db=db, + account_session=session, + analyzed_query=analyzed_query, + branch=permissions_helper.default_branch, + query_parameters=gql_params, + ) + + async def test_first_account_repos(self, db: InfrahubDatabase, permissions_helper: PermissionsHelper) -> None: + gql_params = prepare_graphql_params(db=db, include_mutation=True, branch=permissions_helper.default_branch) + analyzed_query = InfrahubGraphQLQueryAnalyzer( + query=QUERY_REPOS, schema=gql_params.schema, branch=permissions_helper.default_branch + ) + perms = ObjectPermissionChecker() + session = AccountSession( + authenticated=True, + account_id=permissions_helper.first.id, + session_id=str(uuid4()), + auth_type=AuthType.JWT, + ) + + with pytest.raises( + PermissionDeniedError, + match="You do not have the following permission: object:main:Core:Repository:view:allow", + ): + await perms.check( + db=db, + account_session=session, + analyzed_query=analyzed_query, + branch=permissions_helper.default_branch, + query_parameters=gql_params, + ) + + async def test_first_account_graphql(self, db: InfrahubDatabase, permissions_helper: PermissionsHelper) -> None: + """The user should have permissions to list GraphQLQueries.""" + gql_params = prepare_graphql_params(db=db, include_mutation=True, branch=permissions_helper.default_branch) + analyzed_query = InfrahubGraphQLQueryAnalyzer( + query=QUERY_GRAPHQL, schema=gql_params.schema, branch=permissions_helper.default_branch + ) + perms = ObjectPermissionChecker() + session = AccountSession( + authenticated=True, + account_id=permissions_helper.first.id, + session_id=str(uuid4()), + auth_type=AuthType.JWT, + ) + + await perms.check( + db=db, + account_session=session, + analyzed_query=analyzed_query, + branch=permissions_helper.default_branch, + query_parameters=gql_params, + ) + + async def test_first_account_graphql_and_repos( + self, db: InfrahubDatabase, permissions_helper: PermissionsHelper + ) -> None: + """The user should have permissions to list GraphQLQueries but not repositories linked to them""" + gql_params = prepare_graphql_params(db=db, include_mutation=True, branch=permissions_helper.default_branch) + analyzed_query = InfrahubGraphQLQueryAnalyzer( + query=QUERY_GRAPHQL_AND_REPO, schema=gql_params.schema, branch=permissions_helper.default_branch + ) + perms = ObjectPermissionChecker() + session = AccountSession( + authenticated=True, + account_id=permissions_helper.first.id, + session_id=str(uuid4()), + auth_type=AuthType.JWT, + ) + + with pytest.raises(PermissionDeniedError, match="Repository:view:allow"): + await perms.check( + db=db, + account_session=session, + analyzed_query=analyzed_query, + branch=permissions_helper.default_branch, + query_parameters=gql_params, + ) + + +class TestAccountManagerPermissions: + async def test_setup( + self, + db: InfrahubDatabase, + register_core_models_schema: None, + default_branch: Branch, + permissions_helper: PermissionsHelper, + first_account: CoreAccount, + second_account: CoreAccount, + ): + registry.permission_backends = [LocalPermissionBackend()] + permissions_helper._default_branch = default_branch + + permission = await Node.init(db=db, schema=InfrahubKind.GLOBALPERMISSION) + await permission.new( + db=db, name=GlobalPermissions.MANAGE_ACCOUNTS.value, action=GlobalPermissions.MANAGE_ACCOUNTS.value + ) + await permission.save(db=db) + + role = await Node.init(db=db, schema=InfrahubKind.ACCOUNTROLE) + await role.new(db=db, name="admin", permissions=[permission]) + await role.save(db=db) + + group = await Node.init(db=db, schema=InfrahubKind.ACCOUNTGROUP) + await group.new(db=db, name="admin", roles=[role]) + await group.save(db=db) + + await group.members.add(db=db, data={"id": first_account.id}) + await group.members.save(db=db) + + permissions_helper._first = first_account + permissions_helper._second = second_account + + @pytest.mark.parametrize( + "user", + [ + AccountSession(account_id="abc", auth_type=AuthType.JWT, role=AccountRole.ADMIN), + AccountSession(authenticated=False, account_id="anonymous", auth_type=AuthType.NONE), + ], + ) + async def test_supports_manage_accounts_permission_accounts( + self, user: AccountSession, db: InfrahubDatabase, permissions_helper: PermissionsHelper + ): + checker = AccountManagerPermissionChecker() + is_supported = await checker.supports(db=db, account_session=user, branch=permissions_helper.default_branch) + assert is_supported == user.authenticated + + @pytest.mark.parametrize("operation", [MUTATION_ACCOUNT, MUTATION_ACCOUNT_GROUP, MUTATION_ACCOUNT_ROLE]) + async def test_account_with_permission( + self, db: InfrahubDatabase, permissions_helper: PermissionsHelper, operation: str + ): + checker = AccountManagerPermissionChecker() + session = AccountSession( + authenticated=True, account_id=permissions_helper.first.id, session_id=str(uuid4()), auth_type=AuthType.JWT + ) + + gql_params = prepare_graphql_params(db=db, include_mutation=True, branch=permissions_helper.default_branch) + analyzed_query = InfrahubGraphQLQueryAnalyzer( + query=operation, schema=gql_params.schema, branch=permissions_helper.default_branch + ) + + resolution = await checker.check( + db=db, + account_session=session, + analyzed_query=analyzed_query, + query_parameters=gql_params, + branch=permissions_helper.default_branch, + ) + assert resolution == CheckerResolution.NEXT_CHECKER + + @pytest.mark.parametrize( + "operation,must_raise", + [ + (MUTATION_ACCOUNT, True), + (MUTATION_ACCOUNT_GROUP, True), + (MUTATION_ACCOUNT_ROLE, True), + (QUERY_TAGS, False), + (QUERY_ACCOUNT_PROFILE, False), + ], + ) + async def test_account_without_permission( + self, db: InfrahubDatabase, permissions_helper: PermissionsHelper, operation: str, must_raise: bool + ): + checker = AccountManagerPermissionChecker() + session = AccountSession( + authenticated=True, account_id=permissions_helper.second.id, session_id=str(uuid4()), auth_type=AuthType.JWT + ) + + gql_params = prepare_graphql_params(db=db, include_mutation=True, branch=permissions_helper.default_branch) + analyzed_query = InfrahubGraphQLQueryAnalyzer( + query=operation, schema=gql_params.schema, branch=permissions_helper.default_branch + ) + + if not must_raise: + resolution = await checker.check( + db=db, + account_session=session, + analyzed_query=analyzed_query, + query_parameters=gql_params, + branch=permissions_helper.default_branch, + ) + assert resolution == CheckerResolution.NEXT_CHECKER + else: + with pytest.raises( + PermissionDeniedError, match=r"You do not have the permission to manage user accounts, groups or roles" + ): + await checker.check( + db=db, + account_session=session, + analyzed_query=analyzed_query, + query_parameters=gql_params, + branch=permissions_helper.default_branch, + ) + + +class TestPermissionManagerPermissions: + async def test_setup( + self, + db: InfrahubDatabase, + register_core_models_schema: None, + default_branch: Branch, + permissions_helper: PermissionsHelper, + first_account: CoreAccount, + second_account: CoreAccount, + ): + registry.permission_backends = [LocalPermissionBackend()] + permissions_helper._default_branch = default_branch + + permission = await Node.init(db=db, schema=InfrahubKind.GLOBALPERMISSION) + await permission.new( + db=db, name=GlobalPermissions.MANAGE_PERMISSIONS.value, action=GlobalPermissions.MANAGE_PERMISSIONS.value + ) + await permission.save(db=db) + + role = await Node.init(db=db, schema=InfrahubKind.ACCOUNTROLE) + await role.new(db=db, name="admin", permissions=[permission]) + await role.save(db=db) + + group = await Node.init(db=db, schema=InfrahubKind.ACCOUNTGROUP) + await group.new(db=db, name="admin", roles=[role]) + await group.save(db=db) + + await group.members.add(db=db, data={"id": first_account.id}) + await group.members.save(db=db) + + permissions_helper._first = first_account + permissions_helper._second = second_account + + @pytest.mark.parametrize( + "user", + [ + AccountSession(account_id="abc", auth_type=AuthType.JWT, role=AccountRole.ADMIN), + AccountSession(authenticated=False, account_id="anonymous", auth_type=AuthType.NONE), + ], + ) + async def test_supports_manage_accounts_permission_accounts( + self, user: AccountSession, db: InfrahubDatabase, permissions_helper: PermissionsHelper + ): + checker = PermissionManagerPermissionChecker() + is_supported = await checker.supports(db=db, account_session=user, branch=permissions_helper.default_branch) + assert is_supported == user.authenticated + + @pytest.mark.parametrize( + "operation", [MUTATION_GLOBAL_PERMISSION, MUTATION_OBJECT_PERMISSION, QUERY_ACCOUNT_PERMISSIONS] + ) + async def test_account_with_permission( + self, db: InfrahubDatabase, permissions_helper: PermissionsHelper, operation: str + ): + checker = PermissionManagerPermissionChecker() + session = AccountSession( + authenticated=True, account_id=permissions_helper.first.id, session_id=str(uuid4()), auth_type=AuthType.JWT + ) + + gql_params = prepare_graphql_params(db=db, include_mutation=True, branch=permissions_helper.default_branch) + analyzed_query = InfrahubGraphQLQueryAnalyzer( + query=operation, schema=gql_params.schema, branch=permissions_helper.default_branch + ) + + resolution = await checker.check( + db=db, + account_session=session, + analyzed_query=analyzed_query, + query_parameters=gql_params, + branch=permissions_helper.default_branch, + ) + assert resolution == CheckerResolution.NEXT_CHECKER + + @pytest.mark.parametrize( + "operation,must_raise", + [ + (MUTATION_GLOBAL_PERMISSION, True), + (MUTATION_OBJECT_PERMISSION, True), + (QUERY_TAGS, False), + (QUERY_ACCOUNT_PERMISSIONS, False), + ], + ) + async def test_account_without_permission( + self, db: InfrahubDatabase, permissions_helper: PermissionsHelper, operation: str, must_raise: bool + ): + checker = PermissionManagerPermissionChecker() + session = AccountSession( + authenticated=True, account_id=permissions_helper.second.id, session_id=str(uuid4()), auth_type=AuthType.JWT + ) + + gql_params = prepare_graphql_params(db=db, include_mutation=True, branch=permissions_helper.default_branch) + analyzed_query = InfrahubGraphQLQueryAnalyzer( + query=operation, schema=gql_params.schema, branch=permissions_helper.default_branch + ) + + if not must_raise: + resolution = await checker.check( + db=db, + account_session=session, + analyzed_query=analyzed_query, + query_parameters=gql_params, + branch=permissions_helper.default_branch, + ) + assert resolution == CheckerResolution.NEXT_CHECKER + else: + with pytest.raises(PermissionDeniedError, match=r"You do not have the permission to manage permissions"): + await checker.check( + db=db, + account_session=session, + analyzed_query=analyzed_query, + query_parameters=gql_params, + branch=permissions_helper.default_branch, + ) + + +class TestRepositoryManagerPermissions: + async def test_setup( + self, + db: InfrahubDatabase, + register_core_models_schema: None, + default_branch: Branch, + permissions_helper: PermissionsHelper, + first_account: CoreAccount, + second_account: CoreAccount, + ): + registry.permission_backends = [LocalPermissionBackend()] + permissions_helper._default_branch = default_branch + + permission = await Node.init(db=db, schema=InfrahubKind.GLOBALPERMISSION) + await permission.new( + db=db, name=GlobalPermissions.MANAGE_REPOSITORIES.value, action=GlobalPermissions.MANAGE_REPOSITORIES.value + ) + await permission.save(db=db) + + role = await Node.init(db=db, schema=InfrahubKind.ACCOUNTROLE) + await role.new(db=db, name="admin", permissions=[permission]) + await role.save(db=db) + + group = await Node.init(db=db, schema=InfrahubKind.ACCOUNTGROUP) + await group.new(db=db, name="admin", roles=[role]) + await group.save(db=db) + + await group.members.add(db=db, data={"id": first_account.id}) + await group.members.save(db=db) + + permissions_helper._first = first_account + permissions_helper._second = second_account + + @pytest.mark.parametrize( + "user", + [ + AccountSession(account_id="abc", auth_type=AuthType.JWT, role=AccountRole.ADMIN), + AccountSession(authenticated=False, account_id="anonymous", auth_type=AuthType.NONE), + ], + ) + async def test_supports_manage_repositories_permission_accounts( + self, user: AccountSession, db: InfrahubDatabase, permissions_helper: PermissionsHelper + ): + checker = AccountManagerPermissionChecker() + is_supported = await checker.supports(db=db, account_session=user, branch=permissions_helper.default_branch) + assert is_supported == user.authenticated + + @pytest.mark.parametrize( + "operation", [MUTATION_REPOSITORY, MUTATION_READONLY_REPOSITORY, MUTATION_GENERIC_REPOSITORY] + ) + async def test_account_with_permission( + self, db: InfrahubDatabase, permissions_helper: PermissionsHelper, operation: str + ): + checker = RepositoryManagerPermissionChecker() + session = AccountSession( + authenticated=True, account_id=permissions_helper.first.id, session_id=str(uuid4()), auth_type=AuthType.JWT + ) + + gql_params = prepare_graphql_params(db=db, include_mutation=True, branch=permissions_helper.default_branch) + analyzed_query = InfrahubGraphQLQueryAnalyzer( + query=operation, schema=gql_params.schema, branch=permissions_helper.default_branch + ) + + resolution = await checker.check( + db=db, + account_session=session, + analyzed_query=analyzed_query, + query_parameters=gql_params, + branch=permissions_helper.default_branch, + ) + assert resolution == CheckerResolution.NEXT_CHECKER + + @pytest.mark.parametrize( + "operation,must_raise", + [ + (MUTATION_REPOSITORY, True), + (MUTATION_READONLY_REPOSITORY, True), + (MUTATION_GENERIC_REPOSITORY, True), + (QUERY_TAGS, False), + ], + ) + async def test_account_without_permission( + self, db: InfrahubDatabase, permissions_helper: PermissionsHelper, operation: str, must_raise: bool + ): + checker = RepositoryManagerPermissionChecker() + session = AccountSession( + authenticated=True, account_id=permissions_helper.second.id, session_id=str(uuid4()), auth_type=AuthType.JWT + ) + + gql_params = prepare_graphql_params(db=db, include_mutation=True, branch=permissions_helper.default_branch) + analyzed_query = InfrahubGraphQLQueryAnalyzer( + query=operation, schema=gql_params.schema, branch=permissions_helper.default_branch + ) + + if not must_raise: + resolution = await checker.check( + db=db, + account_session=session, + analyzed_query=analyzed_query, + query_parameters=gql_params, + branch=permissions_helper.default_branch, + ) + assert resolution == CheckerResolution.NEXT_CHECKER + else: + with pytest.raises(PermissionDeniedError, match=r"You do not have the permission to manage repositories"): + await checker.check( + db=db, + account_session=session, + analyzed_query=analyzed_query, + query_parameters=gql_params, + branch=permissions_helper.default_branch, + ) diff --git a/backend/tests/unit/graphql/auth/query_permission_checker/test_super_admin_checker.py b/backend/tests/unit/graphql/auth/query_permission_checker/test_super_admin_checker.py new file mode 100644 index 0000000000..e4f715f4a8 --- /dev/null +++ b/backend/tests/unit/graphql/auth/query_permission_checker/test_super_admin_checker.py @@ -0,0 +1,99 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING +from unittest.mock import MagicMock +from uuid import uuid4 + +import pytest + +from infrahub.auth import AccountSession, AuthType +from infrahub.core.constants import AccountRole, GlobalPermissions, InfrahubKind +from infrahub.core.node import Node +from infrahub.core.registry import registry +from infrahub.graphql.analyzer import InfrahubGraphQLQueryAnalyzer +from infrahub.graphql.auth.query_permission_checker.interface import CheckerResolution +from infrahub.graphql.auth.query_permission_checker.super_admin_checker import SuperAdminPermissionChecker +from infrahub.graphql.initialization import GraphqlParams +from infrahub.permissions.local_backend import LocalPermissionBackend + +if TYPE_CHECKING: + from infrahub.core.branch import Branch + from infrahub.core.protocols import CoreAccount + from infrahub.database import InfrahubDatabase + from tests.unit.graphql.conftest import PermissionsHelper + + +class TestSuperAdminPermission: + async def test_setup( + self, + db: InfrahubDatabase, + register_core_models_schema: None, + default_branch: Branch, + first_account: CoreAccount, + second_account: CoreAccount, + permissions_helper: PermissionsHelper, + ): + registry.permission_backends = [LocalPermissionBackend()] + permissions_helper._default_branch = default_branch + + permission = await Node.init(db=db, schema=InfrahubKind.GLOBALPERMISSION) + await permission.new( + db=db, name=GlobalPermissions.SUPER_ADMIN.value, action=GlobalPermissions.SUPER_ADMIN.value + ) + await permission.save(db=db) + + role = await Node.init(db=db, schema=InfrahubKind.ACCOUNTROLE) + await role.new(db=db, name="admin", permissions=[permission]) + await role.save(db=db) + + group = await Node.init(db=db, schema=InfrahubKind.ACCOUNTGROUP) + await group.new(db=db, name="admin", roles=[role]) + await group.save(db=db) + + await group.members.add(db=db, data={"id": first_account.id}) + await group.members.save(db=db) + + permissions_helper._first = first_account + permissions_helper._second = second_account + + @pytest.mark.parametrize( + "user", + [ + AccountSession(account_id="abc", auth_type=AuthType.JWT, role=AccountRole.ADMIN), + AccountSession(authenticated=False, account_id="anonymous", auth_type=AuthType.NONE), + ], + ) + async def test_supports_super_admin_permission_accounts( + self, user: AccountSession, db: InfrahubDatabase, permissions_helper: PermissionsHelper + ): + checker = SuperAdminPermissionChecker() + is_supported = await checker.supports(db=db, account_session=user, branch=permissions_helper.default_branch) + assert is_supported == user.authenticated + + async def test_account_with_permission(self, db: InfrahubDatabase, permissions_helper: PermissionsHelper): + checker = SuperAdminPermissionChecker() + session = AccountSession( + authenticated=True, account_id=permissions_helper.first.id, session_id=str(uuid4()), auth_type=AuthType.JWT + ) + resolution = await checker.check( + db=db, + account_session=session, + analyzed_query=MagicMock(spec=InfrahubGraphQLQueryAnalyzer), + query_parameters=MagicMock(spec=GraphqlParams), + branch=permissions_helper.default_branch, + ) + assert resolution == CheckerResolution.TERMINATE + + async def test_account_without_permission(self, db: InfrahubDatabase, permissions_helper: PermissionsHelper): + checker = SuperAdminPermissionChecker() + session = AccountSession( + authenticated=True, account_id=permissions_helper.second.id, session_id=str(uuid4()), auth_type=AuthType.JWT + ) + resolution = await checker.check( + db=db, + account_session=session, + analyzed_query=MagicMock(spec=InfrahubGraphQLQueryAnalyzer), + query_parameters=MagicMock(spec=GraphqlParams), + branch=permissions_helper.default_branch, + ) + assert resolution == CheckerResolution.NEXT_CHECKER diff --git a/backend/tests/unit/graphql/auth/test_anonymous_checker.py b/backend/tests/unit/graphql/auth/test_anonymous_checker.py index 89b1555314..e06900cb64 100644 --- a/backend/tests/unit/graphql/auth/test_anonymous_checker.py +++ b/backend/tests/unit/graphql/auth/test_anonymous_checker.py @@ -3,36 +3,56 @@ import pytest from infrahub.auth import AccountSession, AuthType +from infrahub.core.branch import Branch +from infrahub.database import InfrahubDatabase from infrahub.exceptions import AuthorizationError from infrahub.graphql.analyzer import InfrahubGraphQLQueryAnalyzer from infrahub.graphql.auth.query_permission_checker.anonymous_checker import AnonymousGraphQLPermissionChecker +from infrahub.graphql.initialization import GraphqlParams class TestAnonymousAuthChecker: def setup_method(self): self.account_session = AccountSession(account_id="abc", auth_type=AuthType.JWT) self.graphql_query = AsyncMock(spec=InfrahubGraphQLQueryAnalyzer) + self.query_parameters = MagicMock(spec=GraphqlParams) self.mock_anonymous_setting_get = MagicMock(return_value=True) self.checker = AnonymousGraphQLPermissionChecker(self.mock_anonymous_setting_get) @pytest.mark.parametrize("is_authenticated,is_supported", [(True, False), (False, True)]) - async def test_supports_unauthenticated_accounts(self, is_authenticated, is_supported): + async def test_supports_unauthenticated_accounts( + self, db: InfrahubDatabase, branch: Branch, is_authenticated, is_supported + ): self.account_session.authenticated = is_authenticated - has_support = await self.checker.supports(self.account_session) + has_support = await self.checker.supports(db=db, account_session=self.account_session, branch=branch) assert is_supported is has_support @pytest.mark.parametrize("anonymous_setting,query_has_mutations", [(False, False), (False, True), (True, True)]) - async def test_failures_raise_error(self, anonymous_setting, query_has_mutations): + async def test_failures_raise_error( + self, db: InfrahubDatabase, branch: Branch, anonymous_setting, query_has_mutations + ): self.mock_anonymous_setting_get.return_value = anonymous_setting self.graphql_query.contains_mutation = query_has_mutations with pytest.raises(AuthorizationError): - await self.checker.check(self.graphql_query) - - async def test_check_passes(self): + await self.checker.check( + db=db, + account_session=self.account_session, + analyzed_query=self.graphql_query, + query_parameters=self.query_parameters, + branch=branch, + ) + + async def test_check_passes(self, db: InfrahubDatabase, branch: Branch): self.mock_anonymous_setting_get.return_value = True self.graphql_query.contains_mutation = False - await self.checker.check(self.graphql_query) + await self.checker.check( + db=db, + account_session=self.account_session, + analyzed_query=self.graphql_query, + query_parameters=self.query_parameters, + branch=branch, + ) diff --git a/backend/tests/unit/graphql/auth/test_default_checker.py b/backend/tests/unit/graphql/auth/test_default_checker.py index 53fd65efbb..d90d1e3f62 100644 --- a/backend/tests/unit/graphql/auth/test_default_checker.py +++ b/backend/tests/unit/graphql/auth/test_default_checker.py @@ -1,12 +1,15 @@ -from unittest.mock import AsyncMock +from unittest.mock import AsyncMock, MagicMock import pytest from infrahub.auth import AccountSession, AuthType +from infrahub.core.branch import Branch from infrahub.core.constants import AccountRole +from infrahub.database import InfrahubDatabase from infrahub.exceptions import AuthorizationError from infrahub.graphql.analyzer import InfrahubGraphQLQueryAnalyzer from infrahub.graphql.auth.query_permission_checker.default_checker import DefaultGraphQLPermissionChecker +from infrahub.graphql.initialization import GraphqlParams class TestDefaultAuthChecker: @@ -16,13 +19,19 @@ def setup_method(self): self.checker = DefaultGraphQLPermissionChecker() @pytest.mark.parametrize("role", [x.value for x in AccountRole]) - async def test_supports_all_accounts(self, role): + async def test_supports_all_accounts(self, db: InfrahubDatabase, branch: Branch, role): self.account_session.role = role - is_supported = await self.checker.supports(self.account_session) + is_supported = await self.checker.supports(db=db, account_session=self.account_session, branch=branch) assert is_supported is True - async def test_always_raises_error(self): + async def test_always_raises_error(self, db: InfrahubDatabase, branch: Branch): with pytest.raises(AuthorizationError): - await self.checker.check(self.graphql_query) + await self.checker.check( + db=db, + account_session=self.account_session, + analyzed_query=self.graphql_query, + query_parameters=MagicMock(spec=GraphqlParams), + branch=branch, + ) diff --git a/backend/tests/unit/graphql/auth/test_parent_checker.py b/backend/tests/unit/graphql/auth/test_parent_checker.py index 89e537b735..84f2c1d0ff 100644 --- a/backend/tests/unit/graphql/auth/test_parent_checker.py +++ b/backend/tests/unit/graphql/auth/test_parent_checker.py @@ -1,41 +1,68 @@ -from unittest.mock import AsyncMock +from unittest.mock import AsyncMock, MagicMock import pytest from infrahub.auth import AccountSession, AuthType +from infrahub.core.branch import Branch from infrahub.core.constants import AccountRole +from infrahub.database import InfrahubDatabase from infrahub.exceptions import PermissionDeniedError from infrahub.graphql.analyzer import InfrahubGraphQLQueryAnalyzer from infrahub.graphql.auth.query_permission_checker.checker import GraphQLQueryPermissionChecker -from infrahub.graphql.auth.query_permission_checker.interface import GraphQLQueryPermissionCheckerInterface +from infrahub.graphql.auth.query_permission_checker.interface import ( + CheckerResolution, + GraphQLQueryPermissionCheckerInterface, +) +from infrahub.graphql.initialization import GraphqlParams class TestParentAuthChecker: def setup_method(self): self.account_session = AccountSession(account_id="abc", auth_type=AuthType.JWT, role=AccountRole.ADMIN) self.graphql_query = AsyncMock(spec=InfrahubGraphQLQueryAnalyzer) + self.query_parameters = MagicMock(spec=GraphqlParams) self.sub_auth_checker_one = AsyncMock(spec=GraphQLQueryPermissionCheckerInterface) self.sub_auth_checker_two = AsyncMock(spec=GraphQLQueryPermissionCheckerInterface) self.sub_auth_checker_one.supports.return_value = False self.sub_auth_checker_two.supports.return_value = True + self.sub_auth_checker_one.check = AsyncMock() + self.sub_auth_checker_one.check.return_value = CheckerResolution.TERMINATE + self.sub_auth_checker_two.check = AsyncMock() + self.sub_auth_checker_two.check.return_value = CheckerResolution.TERMINATE self.parent_checker = GraphQLQueryPermissionChecker([self.sub_auth_checker_one, self.sub_auth_checker_two]) - async def __call_system_under_test(self): - await self.parent_checker.check(self.account_session, self.graphql_query) + async def __call_system_under_test(self, db: InfrahubDatabase, branch: Branch): + await self.parent_checker.check( + db=db, + account_session=self.account_session, + analyzed_query=self.graphql_query, + query_parameters=self.query_parameters, + branch=branch, + ) - async def test_only_checks_one(self): - await self.__call_system_under_test() + async def test_only_checks_one(self, db: InfrahubDatabase, branch: Branch): + await self.__call_system_under_test(db=db, branch=branch) - self.sub_auth_checker_one.supports.assert_awaited_once_with(self.account_session) - self.sub_auth_checker_two.supports.assert_awaited_once_with(self.account_session) + self.sub_auth_checker_one.supports.assert_awaited_once_with( + db=db, account_session=self.account_session, branch=branch + ) + self.sub_auth_checker_two.supports.assert_awaited_once_with( + db=db, account_session=self.account_session, branch=branch + ) self.sub_auth_checker_one.check.assert_not_awaited() - self.sub_auth_checker_two.check.assert_awaited_once_with(self.graphql_query) + self.sub_auth_checker_two.check.assert_awaited_once_with( + db=db, + account_session=self.account_session, + analyzed_query=self.graphql_query, + query_parameters=self.query_parameters, + branch=branch, + ) - async def test_error_if_no_support(self): + async def test_error_if_no_support(self, db: InfrahubDatabase, branch: Branch): self.sub_auth_checker_two.supports.return_value = False with pytest.raises(PermissionDeniedError): - await self.__call_system_under_test() + await self.__call_system_under_test(db=db, branch=branch) self.sub_auth_checker_one.check.assert_not_awaited() self.sub_auth_checker_two.check.assert_not_awaited() diff --git a/backend/tests/unit/graphql/auth/test_read_only_checker.py b/backend/tests/unit/graphql/auth/test_read_only_checker.py index 92f848f08d..e1a13b921c 100644 --- a/backend/tests/unit/graphql/auth/test_read_only_checker.py +++ b/backend/tests/unit/graphql/auth/test_read_only_checker.py @@ -1,55 +1,77 @@ -from unittest.mock import AsyncMock +from unittest.mock import AsyncMock, MagicMock import pytest from graphql import OperationType from infrahub_sdk.analyzer import GraphQLOperation from infrahub.auth import AccountSession, AuthType +from infrahub.core.branch import Branch from infrahub.core.constants import AccountRole +from infrahub.database import InfrahubDatabase from infrahub.exceptions import PermissionDeniedError from infrahub.graphql.analyzer import InfrahubGraphQLQueryAnalyzer from infrahub.graphql.auth.query_permission_checker.read_only_checker import ReadOnlyGraphQLPermissionChecker +from infrahub.graphql.initialization import GraphqlParams class TestReadOnlyAuthChecker: def setup_method(self): self.account_session = AccountSession(account_id="abc", auth_type=AuthType.JWT, role=AccountRole.READ_ONLY) self.graphql_query = AsyncMock(spec=InfrahubGraphQLQueryAnalyzer) + self.query_parameters = MagicMock(spec=GraphqlParams) self.checker = ReadOnlyGraphQLPermissionChecker() @pytest.mark.parametrize("role", [AccountRole.ADMIN, AccountRole.READ_WRITE]) - async def test_doesnt_supports_other_accounts(self, role): + async def test_doesnt_supports_other_accounts(self, db: InfrahubDatabase, branch: Branch, role): self.account_session.role = role - is_supported = await self.checker.supports(self.account_session) + is_supported = await self.checker.supports(db=db, account_session=self.account_session, branch=branch) assert is_supported is False - async def test_supports_read_only_accounts(self): + async def test_supports_read_only_accounts(self, db: InfrahubDatabase, branch: Branch): self.account_session.role = AccountRole.READ_ONLY - is_supported = await self.checker.supports(self.account_session) + is_supported = await self.checker.supports(db=db, account_session=self.account_session, branch=branch) assert is_supported is True - async def test_illegal_mutation_raises_error(self): + async def test_illegal_mutation_raises_error(self, db: InfrahubDatabase, branch: Branch): self.graphql_query.contains_mutation = True self.graphql_query.operations = [ GraphQLOperation(name="ThisIsNotAllowed", operation_type=OperationType.MUTATION) ] with pytest.raises(PermissionDeniedError): - await self.checker.check(self.graphql_query) - - async def test_legal_mutation_is_okay(self): + await self.checker.check( + db=db, + account_session=self.account_session, + analyzed_query=self.graphql_query, + query_parameters=self.query_parameters, + branch=branch, + ) + + async def test_legal_mutation_is_okay(self, db: InfrahubDatabase, branch: Branch): self.checker.allowed_readonly_mutations = ["ThisIsAllowed"] self.graphql_query.contains_mutation = True self.graphql_query.operations = [GraphQLOperation(name="ThisIsAllowed", operation_type=OperationType.MUTATION)] - await self.checker.check(self.graphql_query) + await self.checker.check( + db=db, + account_session=self.account_session, + analyzed_query=self.graphql_query, + query_parameters=self.query_parameters, + branch=branch, + ) - async def test_query_is_okay(self): + async def test_query_is_okay(self, db: InfrahubDatabase, branch: Branch): self.graphql_query.contains_mutation = False self.graphql_query.operations = [GraphQLOperation(name="ThisIsAQuery", operation_type=OperationType.QUERY)] - await self.checker.check(self.graphql_query) + await self.checker.check( + db=db, + account_session=self.account_session, + analyzed_query=self.graphql_query, + query_parameters=self.query_parameters, + branch=branch, + ) diff --git a/backend/tests/unit/graphql/auth/test_read_write_checker.py b/backend/tests/unit/graphql/auth/test_read_write_checker.py index 3dbbf38f8c..1f9f59bceb 100644 --- a/backend/tests/unit/graphql/auth/test_read_write_checker.py +++ b/backend/tests/unit/graphql/auth/test_read_write_checker.py @@ -1,11 +1,14 @@ -from unittest.mock import AsyncMock +from unittest.mock import AsyncMock, MagicMock import pytest from infrahub.auth import AccountSession, AuthType +from infrahub.core.branch import Branch from infrahub.core.constants import AccountRole +from infrahub.database import InfrahubDatabase from infrahub.graphql.analyzer import InfrahubGraphQLQueryAnalyzer from infrahub.graphql.auth.query_permission_checker.read_write_checker import ReadWriteGraphQLPermissionChecker +from infrahub.graphql.initialization import GraphqlParams class TestReadWriteAuthChecker: @@ -15,22 +18,28 @@ def setup_method(self): self.checker = ReadWriteGraphQLPermissionChecker() @pytest.mark.parametrize("role", [AccountRole.ADMIN, AccountRole.READ_WRITE]) - async def test_supports_readwrite_accounts(self, role): + async def test_supports_readwrite_accounts(self, db: InfrahubDatabase, branch: Branch, role): self.account_session.role = role - is_supported = await self.checker.supports(self.account_session) + is_supported = await self.checker.supports(db=db, account_session=self.account_session, branch=branch) assert is_supported is True - async def test_doesnt_support_readonly_accounts(self): + async def test_doesnt_support_readonly_accounts(self, db: InfrahubDatabase, branch: Branch): self.account_session.role = AccountRole.READ_ONLY - is_supported = await self.checker.supports(self.account_session) + is_supported = await self.checker.supports(db=db, account_session=self.account_session, branch=branch) assert is_supported is False @pytest.mark.parametrize("contains_mutations", [True, False]) - async def test_never_raises_error(self, contains_mutations): + async def test_never_raises_error(self, db: InfrahubDatabase, branch: Branch, contains_mutations): self.graphql_query.contains_mutations = contains_mutations - await self.checker.check(self.graphql_query) + await self.checker.check( + db=db, + account_session=self.account_session, + analyzed_query=self.graphql_query, + query_parameters=MagicMock(spec=GraphqlParams), + branch=branch, + ) diff --git a/backend/tests/unit/graphql/conftest.py b/backend/tests/unit/graphql/conftest.py index 5f4f7ac9d4..dd22ff44d6 100644 --- a/backend/tests/unit/graphql/conftest.py +++ b/backend/tests/unit/graphql/conftest.py @@ -1,13 +1,52 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + import pytest from infrahub.dependencies.registry import build_component_registry +if TYPE_CHECKING: + from infrahub.core.branch import Branch + from infrahub.core.protocols import CoreAccount + @pytest.fixture(scope="module", autouse=True) def load_component_dependency_registry(): build_component_registry() +class PermissionsHelper: + def __init__(self) -> None: + self._first: None | CoreAccount = None + self._second: None | CoreAccount = None + self._default_branch: None | Branch = None + + @property + def first(self) -> CoreAccount: + if self._first: + return self._first + + raise NotImplementedError() + + @property + def second(self) -> CoreAccount: + if self._second: + return self._second + + raise NotImplementedError() + + @property + def default_branch(self) -> Branch: + if self._default_branch: + return self._default_branch + + +@pytest.fixture(scope="module") +def permissions_helper() -> PermissionsHelper: + return PermissionsHelper() + + @pytest.fixture def query_01() -> str: """Simple query with one document""" diff --git a/backend/tests/unit/graphql/mutations/test_branch.py b/backend/tests/unit/graphql/mutations/test_branch.py index 32650624f3..11cd8a8f39 100644 --- a/backend/tests/unit/graphql/mutations/test_branch.py +++ b/backend/tests/unit/graphql/mutations/test_branch.py @@ -7,7 +7,7 @@ from infrahub.core.initialization import create_branch from infrahub.core.node import Node from infrahub.database import InfrahubDatabase -from infrahub.graphql import prepare_graphql_params +from infrahub.graphql.initialization import prepare_graphql_params from infrahub.message_bus import messages from infrahub.services import InfrahubServices from tests.adapters.message_bus import BusRecorder diff --git a/backend/tests/unit/graphql/mutations/test_ipam.py b/backend/tests/unit/graphql/mutations/test_ipam.py index bf9401d8c8..98b8942ff2 100644 --- a/backend/tests/unit/graphql/mutations/test_ipam.py +++ b/backend/tests/unit/graphql/mutations/test_ipam.py @@ -6,9 +6,9 @@ from infrahub.core.branch import Branch from infrahub.core.constants import InfrahubKind from infrahub.core.node import Node -from infrahub.core.schema_manager import SchemaBranch +from infrahub.core.schema.schema_branch import SchemaBranch from infrahub.database import InfrahubDatabase -from infrahub.graphql import prepare_graphql_params +from infrahub.graphql.initialization import prepare_graphql_params CREATE_IPPREFIX = """ mutation CreatePrefix($prefix: String!) { diff --git a/backend/tests/unit/graphql/mutations/test_menu.py b/backend/tests/unit/graphql/mutations/test_menu.py new file mode 100644 index 0000000000..857d8f6072 --- /dev/null +++ b/backend/tests/unit/graphql/mutations/test_menu.py @@ -0,0 +1,115 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from infrahub.menu.constants import MenuSection +from infrahub.menu.models import MenuItemDefinition +from infrahub.services import InfrahubServices +from tests.helpers.graphql import graphql_mutation + +if TYPE_CHECKING: + from infrahub.core.branch import Branch + from infrahub.database import InfrahubDatabase + + +async def test_menu_create(db: InfrahubDatabase, register_core_models_schema: None, default_branch: Branch): + service = InfrahubServices(database=db) + + CREATE_MENU = """ + mutation CoreMenuItemCreate { + CoreMenuItemCreate( + data: { + namespace: { value: "Builtin" } + name: { value: "TestCar" } + } + ) { + ok + } + } + """ + + result = await graphql_mutation( + query=CREATE_MENU, + db=db, + service=service, + ) + assert "Builtin is not valid" in result.errors[0].args[0] + + +async def test_menu_update_protected(db: InfrahubDatabase, register_core_models_schema: None, default_branch: Branch): + service = InfrahubServices(database=db) + + menu_item = MenuItemDefinition( + namespace="Builtin", + name="Branches", + label="Branches", + path="/branche", + icon="mdi:layers-triple", + protected=True, + section=MenuSection.INTERNAL, + order_weight=1000, + ) + obj = await menu_item.to_node(db=db) + await obj.save(db=db) + + UPDATE_MENU = """ + mutation CoreMenuItemUpdate($id: String!) { + CoreMenuItemUpdate( + data: { + id: $id + name: { value: "TestCar" } + } + ) { + ok + } + } + """ + + result = await graphql_mutation( + query=UPDATE_MENU, + db=db, + variables={"id": obj.id}, + service=service, + ) + + assert result.errors + assert "This object is protected" in result.errors[0].args[0] + + +async def test_menu_delete_protected(db: InfrahubDatabase, register_core_models_schema: None, default_branch: Branch): + service = InfrahubServices(database=db) + + menu_item = MenuItemDefinition( + namespace="Builtin", + name="Branches", + label="Branches", + path="/branche", + icon="mdi:layers-triple", + protected=True, + section=MenuSection.INTERNAL, + order_weight=1000, + ) + obj = await menu_item.to_node(db=db) + await obj.save(db=db) + + DELETE_MENU = """ + mutation CoreMenuItemDelete($id: String!) { + CoreMenuItemDelete( + data: { + id: $id + } + ) { + ok + } + } + """ + + result = await graphql_mutation( + query=DELETE_MENU, + db=db, + variables={"id": obj.id}, + service=service, + ) + + assert result.errors + assert "This object is protected" in result.errors[0].args[0] diff --git a/backend/tests/unit/graphql/mutations/test_proposed_change.py b/backend/tests/unit/graphql/mutations/test_proposed_change.py index 136fd5c983..c43ae7303c 100644 --- a/backend/tests/unit/graphql/mutations/test_proposed_change.py +++ b/backend/tests/unit/graphql/mutations/test_proposed_change.py @@ -2,12 +2,16 @@ from graphql import graphql +from infrahub.auth import AccountSession from infrahub.core.branch import Branch from infrahub.core.constants import CheckType, InfrahubKind +from infrahub.core.initialization import create_branch from infrahub.core.node import Node +from infrahub.core.registry import registry from infrahub.database import InfrahubDatabase -from infrahub.graphql import prepare_graphql_params +from infrahub.graphql.initialization import prepare_graphql_params from infrahub.message_bus import messages +from infrahub.permissions.local_backend import LocalPermissionBackend from infrahub.services import InfrahubServices from tests.adapters.message_bus import BusRecorder from tests.helpers.graphql import graphql_mutation @@ -190,3 +194,40 @@ async def test_update_merged_proposed_change(db: InfrahubDatabase, register_core assert update_status.errors assert "A proposed change in the merged state is not allowed to be updated" in str(update_status.errors[0]) + + +async def test_merge_proposed_change_permission_failure( + db: InfrahubDatabase, + register_core_models_schema: None, + session_first_account: AccountSession, + session_admin: AccountSession, +): + registry.permission_backends = [LocalPermissionBackend()] + + branch_name = "merge-proposed-change-perm" + await create_branch(branch_name=branch_name, db=db) + + proposed_change = await Node.init(db=db, schema=InfrahubKind.PROPOSEDCHANGE) + await proposed_change.new( + db=db, name="pc-merge-perm-1234", destination_branch="main", source_branch=branch_name, state="open" + ) + await proposed_change.save(db=db) + + update_status = await graphql_mutation( + query=UPDATE_PROPOSED_CHANGE, + db=db, + variables={"proposed_change": proposed_change.id, "state": "merged"}, + account_session=session_first_account, + ) + + assert update_status.errors + assert "You do not have the permission to merge proposed changes" == update_status.errors[0].message + + update_status = await graphql_mutation( + query=UPDATE_PROPOSED_CHANGE, + db=db, + variables={"proposed_change": proposed_change.id, "state": "merged"}, + account_session=session_admin, + ) + + assert not update_status.errors diff --git a/backend/tests/unit/graphql/mutations/test_resource_manager.py b/backend/tests/unit/graphql/mutations/test_resource_manager.py index 99b0dee5fc..89bebf72a0 100644 --- a/backend/tests/unit/graphql/mutations/test_resource_manager.py +++ b/backend/tests/unit/graphql/mutations/test_resource_manager.py @@ -8,9 +8,9 @@ from infrahub.core.node.resource_manager.ip_address_pool import CoreIPAddressPool from infrahub.core.node.resource_manager.ip_prefix_pool import CoreIPPrefixPool from infrahub.core.schema import SchemaRoot -from infrahub.core.schema_manager import SchemaBranch +from infrahub.core.schema.schema_branch import SchemaBranch from infrahub.database import InfrahubDatabase -from infrahub.graphql import prepare_graphql_params +from infrahub.graphql.initialization import prepare_graphql_params from tests.helpers.schema import TICKET, load_schema diff --git a/backend/tests/unit/graphql/mutations/test_schema.py b/backend/tests/unit/graphql/mutations/test_schema.py index 8768a836f6..daeffb8219 100644 --- a/backend/tests/unit/graphql/mutations/test_schema.py +++ b/backend/tests/unit/graphql/mutations/test_schema.py @@ -4,7 +4,7 @@ from infrahub.core.node import Node from infrahub.database import InfrahubDatabase from infrahub.exceptions import ValidationError -from infrahub.graphql import prepare_graphql_params +from infrahub.graphql.initialization import prepare_graphql_params from infrahub.graphql.mutations.schema import validate_kind, validate_kind_dropdown, validate_kind_enum diff --git a/backend/tests/unit/graphql/mutations/test_task.py b/backend/tests/unit/graphql/mutations/test_task.py index fdb3d116f5..858c169ddd 100644 --- a/backend/tests/unit/graphql/mutations/test_task.py +++ b/backend/tests/unit/graphql/mutations/test_task.py @@ -2,7 +2,7 @@ from infrahub.core.node import Node from infrahub.database import InfrahubDatabase -from infrahub.graphql import prepare_graphql_params +from infrahub.graphql.initialization import prepare_graphql_params CREATE_TASK = """ mutation CreateTask( diff --git a/backend/tests/unit/graphql/mutations/test_update_generic.py b/backend/tests/unit/graphql/mutations/test_update_generic.py index 54eeefe2b5..e2a9b2d1f9 100644 --- a/backend/tests/unit/graphql/mutations/test_update_generic.py +++ b/backend/tests/unit/graphql/mutations/test_update_generic.py @@ -3,7 +3,7 @@ from infrahub.core.branch import Branch from infrahub.core.node import Node from infrahub.database import InfrahubDatabase -from infrahub.graphql import prepare_graphql_params +from infrahub.graphql.initialization import prepare_graphql_params async def test_display_label_generic(db: InfrahubDatabase, animal_person_schema, branch: Branch): diff --git a/backend/tests/unit/graphql/profiles/test_mutation_create.py b/backend/tests/unit/graphql/profiles/test_mutation_create.py index 8e936d7949..d128f8870e 100644 --- a/backend/tests/unit/graphql/profiles/test_mutation_create.py +++ b/backend/tests/unit/graphql/profiles/test_mutation_create.py @@ -2,7 +2,7 @@ from infrahub.core.manager import NodeManager from infrahub.database import InfrahubDatabase -from infrahub.graphql import prepare_graphql_params +from infrahub.graphql.initialization import prepare_graphql_params async def test_create_profile(db: InfrahubDatabase, default_branch, car_person_schema): diff --git a/backend/tests/unit/graphql/profiles/test_query.py b/backend/tests/unit/graphql/profiles/test_query.py index 5e7b9b49ce..4e0a4bc700 100644 --- a/backend/tests/unit/graphql/profiles/test_query.py +++ b/backend/tests/unit/graphql/profiles/test_query.py @@ -9,7 +9,7 @@ from infrahub.core.schema import NodeSchema from infrahub.core.schema.generic_schema import GenericSchema from infrahub.database import InfrahubDatabase -from infrahub.graphql import prepare_graphql_params +from infrahub.graphql.initialization import prepare_graphql_params @pytest.fixture diff --git a/backend/tests/unit/graphql/queries/test_branch.py b/backend/tests/unit/graphql/queries/test_branch.py index 0d31fcd2f1..bf7bc3eea0 100644 --- a/backend/tests/unit/graphql/queries/test_branch.py +++ b/backend/tests/unit/graphql/queries/test_branch.py @@ -4,7 +4,7 @@ from infrahub.core.branch import Branch from infrahub.database import InfrahubDatabase -from infrahub.graphql import prepare_graphql_params +from infrahub.graphql.initialization import prepare_graphql_params async def test_branch_query(db: InfrahubDatabase, default_branch: Branch, register_core_models_schema, session_admin): diff --git a/backend/tests/unit/graphql/queries/test_ipam.py b/backend/tests/unit/graphql/queries/test_ipam.py index b203c72442..e54bb4c000 100644 --- a/backend/tests/unit/graphql/queries/test_ipam.py +++ b/backend/tests/unit/graphql/queries/test_ipam.py @@ -5,9 +5,9 @@ from infrahub.core.branch import Branch from infrahub.core.constants import InfrahubKind from infrahub.core.node import Node -from infrahub.core.schema_manager import SchemaBranch +from infrahub.core.schema.schema_branch import SchemaBranch from infrahub.database import InfrahubDatabase -from infrahub.graphql import prepare_graphql_params +from infrahub.graphql.initialization import prepare_graphql_params @pytest.fixture diff --git a/backend/tests/unit/graphql/queries/test_list_permissions.py b/backend/tests/unit/graphql/queries/test_list_permissions.py new file mode 100644 index 0000000000..d309a744c4 --- /dev/null +++ b/backend/tests/unit/graphql/queries/test_list_permissions.py @@ -0,0 +1,240 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING +from uuid import uuid4 + +from graphql import graphql + +from infrahub.auth import AccountSession, AuthType +from infrahub.core.account import ObjectPermission +from infrahub.core.constants import ( + InfrahubKind, + PermissionAction, + PermissionDecision, +) +from infrahub.core.initialization import create_branch +from infrahub.core.node import Node +from infrahub.core.registry import registry +from infrahub.graphql.initialization import prepare_graphql_params +from infrahub.permissions.local_backend import LocalPermissionBackend + +if TYPE_CHECKING: + from infrahub.core.branch import Branch + from infrahub.core.protocols import CoreAccount + from infrahub.database import InfrahubDatabase + from tests.unit.graphql.conftest import PermissionsHelper + + +QUERY_TAGS = """ +query { + BuiltinTag { + permissions { + count + edges { + node { + kind + create + update + delete + view + } + } + } + } +} +""" + +REPOSITORY_QUERY = """ +query { + CoreGenericRepository { + permissions { + count + edges { + node { + kind + create + update + delete + view + } + } + } + } +} +""" + + +class TestObjectPermissions: + async def test_setup( + self, + db: InfrahubDatabase, + register_core_models_schema: None, + default_branch: Branch, + permissions_helper: PermissionsHelper, + first_account: CoreAccount, + ): + permissions_helper._first = first_account + permissions_helper._default_branch = default_branch + registry.permission_backends = [LocalPermissionBackend()] + + permissions = [] + for object_permission in [ + ObjectPermission( + id="", + branch="*", + namespace="Builtin", + name="*", + action=PermissionAction.VIEW.value, + decision=PermissionDecision.ALLOW.value, + ), + ObjectPermission( + id="", + branch="*", + namespace="Builtin", + name="*", + action=PermissionAction.ADD.value, + decision=PermissionDecision.ALLOW.value, + ), + ObjectPermission( + id="", + branch="*", + namespace="Builtin", + name="*", + action=PermissionAction.DELETE.value, + decision=PermissionDecision.ALLOW.value, + ), + ObjectPermission( + id="", + branch="*", + namespace="Core", + name="*", + action=PermissionAction.ANY.value, + decision=PermissionDecision.ALLOW.value, + ), + ]: + obj = await Node.init(db=db, schema=InfrahubKind.OBJECTPERMISSION) + await obj.new( + db=db, + branch=object_permission.branch, + namespace=object_permission.namespace, + name=object_permission.name, + action=object_permission.action, + decision=object_permission.decision, + ) + await obj.save(db=db) + permissions.append(obj) + + role = await Node.init(db=db, schema=InfrahubKind.ACCOUNTROLE) + await role.new(db=db, name="admin", permissions=permissions) + await role.save(db=db) + + group = await Node.init(db=db, schema=InfrahubKind.ACCOUNTGROUP) + await group.new(db=db, name="admin", roles=[role]) + await group.save(db=db) + + await group.members.add(db=db, data={"id": first_account.id}) + await group.members.save(db=db) + + async def test_first_account_tags_main_branch( + self, db: InfrahubDatabase, permissions_helper: PermissionsHelper + ) -> None: + """In the main branch the first account doesn't have the permission to make changes""" + session = AccountSession( + authenticated=True, + account_id=permissions_helper.first.id, + session_id=str(uuid4()), + auth_type=AuthType.JWT, + ) + gql_params = prepare_graphql_params( + db=db, include_mutation=True, branch=permissions_helper.default_branch, account_session=session + ) + + result = await graphql( + schema=gql_params.schema, + source=QUERY_TAGS, + context_value=gql_params.context, + ) + + assert not result.errors + assert result.data + assert result.data["BuiltinTag"]["permissions"]["count"] == 1 + assert result.data["BuiltinTag"]["permissions"]["edges"][0] == { + "node": {"kind": "BuiltinTag", "create": "DENY", "update": "DENY", "delete": "DENY", "view": "ALLOW"} + } + + async def test_first_account_tags_non_main_branch( + self, db: InfrahubDatabase, permissions_helper: PermissionsHelper + ) -> None: + """In other branches the permissions for the first account is less restrictive""" + branch2 = await create_branch(branch_name="pr-12345", db=db) + session = AccountSession( + authenticated=True, + account_id=permissions_helper.first.id, + session_id=str(uuid4()), + auth_type=AuthType.JWT, + ) + gql_params = prepare_graphql_params(db=db, include_mutation=True, branch=branch2, account_session=session) + + result = await graphql( + schema=gql_params.schema, + source=QUERY_TAGS, + context_value=gql_params.context, + ) + + assert not result.errors + assert result.data + assert result.data["BuiltinTag"]["permissions"]["count"] == 1 + assert result.data["BuiltinTag"]["permissions"]["edges"][0] == { + "node": {"kind": "BuiltinTag", "create": "ALLOW", "update": "DENY", "delete": "ALLOW", "view": "ALLOW"} + } + + async def test_first_account_list_permissions_for_generics( + self, db: InfrahubDatabase, permissions_helper: PermissionsHelper + ) -> None: + """In the main branch the first account doesn't have the permission to make changes""" + session = AccountSession( + authenticated=True, + account_id=permissions_helper.first.id, + session_id=str(uuid4()), + auth_type=AuthType.JWT, + ) + gql_params = prepare_graphql_params( + db=db, include_mutation=True, branch=permissions_helper.default_branch, account_session=session + ) + + result = await graphql( + schema=gql_params.schema, + source=REPOSITORY_QUERY, + context_value=gql_params.context, + ) + + assert not result.errors + assert result.data + assert result.data["CoreGenericRepository"]["permissions"]["count"] == 3 + assert { + "node": { + "kind": "CoreGenericRepository", + "create": "DENY", + "update": "DENY", + "delete": "DENY", + "view": "ALLOW", + } + } in result.data["CoreGenericRepository"]["permissions"]["edges"] + assert { + "node": { + "kind": "CoreRepository", + "create": "DENY", + "update": "DENY", + "delete": "DENY", + "view": "ALLOW", + } + } in result.data["CoreGenericRepository"]["permissions"]["edges"] + assert { + "node": { + "kind": "CoreReadOnlyRepository", + "create": "DENY", + "update": "DENY", + "delete": "DENY", + "view": "ALLOW", + } + } in result.data["CoreGenericRepository"]["permissions"]["edges"] diff --git a/backend/tests/unit/graphql/queries/test_relationship.py b/backend/tests/unit/graphql/queries/test_relationship.py index 8196772027..0b6f9e1273 100644 --- a/backend/tests/unit/graphql/queries/test_relationship.py +++ b/backend/tests/unit/graphql/queries/test_relationship.py @@ -2,7 +2,7 @@ from infrahub.core.branch import Branch from infrahub.database import InfrahubDatabase -from infrahub.graphql import prepare_graphql_params +from infrahub.graphql.initialization import prepare_graphql_params async def test_relationship( diff --git a/backend/tests/unit/graphql/queries/test_resource_pool.py b/backend/tests/unit/graphql/queries/test_resource_pool.py index d1458314c0..82a398c295 100644 --- a/backend/tests/unit/graphql/queries/test_resource_pool.py +++ b/backend/tests/unit/graphql/queries/test_resource_pool.py @@ -10,9 +10,9 @@ from infrahub.core.node.resource_manager.ip_address_pool import CoreIPAddressPool from infrahub.core.node.resource_manager.ip_prefix_pool import CoreIPPrefixPool from infrahub.core.schema import SchemaRoot -from infrahub.core.schema_manager import SchemaBranch +from infrahub.core.schema.schema_branch import SchemaBranch from infrahub.database import InfrahubDatabase -from infrahub.graphql import prepare_graphql_params +from infrahub.graphql.initialization import prepare_graphql_params from tests.helpers.schema import TICKET, load_schema diff --git a/backend/tests/unit/graphql/queries/test_search.py b/backend/tests/unit/graphql/queries/test_search.py index 48124788cd..844dd56966 100644 --- a/backend/tests/unit/graphql/queries/test_search.py +++ b/backend/tests/unit/graphql/queries/test_search.py @@ -3,7 +3,7 @@ from infrahub.core.branch import Branch from infrahub.core.node import Node from infrahub.database import InfrahubDatabase -from infrahub.graphql import prepare_graphql_params +from infrahub.graphql.initialization import prepare_graphql_params SEARCH_QUERY = """ query ($search: String!) { diff --git a/backend/tests/unit/graphql/queries/test_task.py b/backend/tests/unit/graphql/queries/test_task.py index b302835fc9..e76575b2e1 100644 --- a/backend/tests/unit/graphql/queries/test_task.py +++ b/backend/tests/unit/graphql/queries/test_task.py @@ -7,7 +7,7 @@ from infrahub.core.constants import InfrahubKind from infrahub.core.node import Node from infrahub.database import InfrahubDatabase -from infrahub.graphql import prepare_graphql_params +from infrahub.graphql.initialization import prepare_graphql_params CREATE_TASK = """ mutation CreateTask( diff --git a/backend/tests/unit/graphql/test_app.py b/backend/tests/unit/graphql/test_app.py index a7557b095a..5c5b78a3d8 100644 --- a/backend/tests/unit/graphql/test_app.py +++ b/backend/tests/unit/graphql/test_app.py @@ -1,3 +1,4 @@ +import pytest from fastapi.testclient import TestClient from infrahub.core.branch import Branch @@ -6,7 +7,15 @@ from infrahub.database import InfrahubDatabase -async def test_websocket(db: InfrahubDatabase, default_branch: Branch, register_core_models_schema): +@pytest.fixture +def client(nats, redis): + # In order to mock some methods later we can't load app by default because it will automatically load all import in main.py as well + from infrahub.server import app + + return TestClient(app) + + +async def test_websocket(db: InfrahubDatabase, client: TestClient, default_branch: Branch, register_core_models_schema): t2 = await Node.init(db=db, schema=InfrahubKind.TAG, branch=default_branch) await t2.new(db=db, name="Red", description="The Red tag") await t2.save(db=db) @@ -15,10 +24,6 @@ async def test_websocket(db: InfrahubDatabase, default_branch: Branch, register_ await q1.new(db=db, name="query01", query="query { BuiltinTag { count }}") await q1.save(db=db) - from infrahub.server import app - - client = TestClient(app) - with client: with client.websocket_connect("/graphql") as websocket: websocket.send_json( diff --git a/backend/tests/unit/graphql/test_core_account.py b/backend/tests/unit/graphql/test_core_account.py new file mode 100644 index 0000000000..b48d485d43 --- /dev/null +++ b/backend/tests/unit/graphql/test_core_account.py @@ -0,0 +1,124 @@ +import bcrypt +import pytest +from graphql import graphql + +from infrahub.auth import AccountSession, AuthType +from infrahub.core.account import GlobalPermission, ObjectPermission +from infrahub.core.branch import Branch +from infrahub.core.constants import AccountRole, GlobalPermissions, PermissionAction, PermissionDecision +from infrahub.core.manager import NodeManager +from infrahub.database import InfrahubDatabase +from infrahub.graphql.initialization import prepare_graphql_params + + +@pytest.mark.parametrize("role", [e.value for e in AccountRole]) +async def test_everyone_can_update_password(db: InfrahubDatabase, default_branch: Branch, first_account, role): + new_password = "NewP@ssw0rd" + new_description = "what a cool description" + query = """ + mutation { + InfrahubAccountSelfUpdate(data: {password: "%s", description: "%s"}) { + ok + } + } + """ % (new_password, new_description) + + gql_params = prepare_graphql_params( + db=db, + include_subscription=False, + branch=default_branch, + account_session=AccountSession( + authenticated=True, account_id=first_account.id, role=role, auth_type=AuthType.JWT + ), + ) + + result = await graphql( + schema=gql_params.schema, + source=query, + context_value=gql_params.context, + root_value=None, + variable_values={}, + ) + + assert result.errors is None + assert result.data["InfrahubAccountSelfUpdate"]["ok"] is True + + updated_account = await NodeManager.get_one(db=db, id=first_account.id, branch=default_branch) + assert bcrypt.checkpw(new_password.encode("UTF-8"), updated_account.password.value.encode("UTF-8")) + assert updated_account.description.value == new_description + + +async def test_permissions( + db: InfrahubDatabase, default_branch: Branch, authentication_base, session_admin, first_account +): + query = """ + query { + InfrahubPermissions { + global_permissions { + edges { + node { + identifier + } + } + } + object_permissions { + edges { + node { + identifier + } + } + } + } + } + """ + + gql_params = prepare_graphql_params( + db=db, include_subscription=False, branch=default_branch, account_session=session_admin + ) + + result = await graphql( + schema=gql_params.schema, source=query, context_value=gql_params.context, root_value=None, variable_values={} + ) + + assert result.errors is None + perms = [edge["node"]["identifier"] for edge in result.data["InfrahubPermissions"]["global_permissions"]["edges"]] + assert perms == [ + str( + GlobalPermission( + id="", + name=GlobalPermissions.SUPER_ADMIN.value, + action=GlobalPermissions.SUPER_ADMIN.value, + decision=PermissionDecision.ALLOW.value, + ) + ) + ] + + perms = [edge["node"]["identifier"] for edge in result.data["InfrahubPermissions"]["object_permissions"]["edges"]] + assert perms == [ + str( + ObjectPermission( + id="", + branch="*", + namespace="*", + name="*", + action=PermissionAction.ANY.value, + decision=PermissionDecision.ALLOW.value, + ) + ) + ] + + gql_params = prepare_graphql_params( + db=db, + include_subscription=False, + branch=default_branch, + account_session=AccountSession( + authenticated=True, account_id=first_account.id, role=first_account.role.value, auth_type=AuthType.JWT + ), + ) + + result = await graphql( + schema=gql_params.schema, source=query, context_value=gql_params.context, root_value=None, variable_values={} + ) + + assert result.errors is None + assert not result.data["InfrahubPermissions"]["global_permissions"]["edges"] diff --git a/backend/tests/unit/graphql/test_core_account_self_update.py b/backend/tests/unit/graphql/test_core_account_self_update.py deleted file mode 100644 index 8b903c880a..0000000000 --- a/backend/tests/unit/graphql/test_core_account_self_update.py +++ /dev/null @@ -1,47 +0,0 @@ -import bcrypt -import pytest -from graphql import graphql - -from infrahub.auth import AccountSession, AuthType -from infrahub.core.branch import Branch -from infrahub.core.constants import AccountRole -from infrahub.core.manager import NodeManager -from infrahub.database import InfrahubDatabase -from infrahub.graphql import prepare_graphql_params - - -@pytest.mark.parametrize("role", [e.value for e in AccountRole]) -async def test_everyone_can_update_password(db: InfrahubDatabase, default_branch: Branch, first_account, role): - new_password = "NewP@ssw0rd" - new_description = "what a cool description" - query = """ - mutation { - InfrahubAccountSelfUpdate(data: {password: "%s", description: "%s"}) { - ok - } - } - """ % (new_password, new_description) - - gql_params = prepare_graphql_params( - db=db, - include_subscription=False, - branch=default_branch, - account_session=AccountSession( - authenticated=True, account_id=first_account.id, role=role, auth_type=AuthType.JWT - ), - ) - - result = await graphql( - schema=gql_params.schema, - source=query, - context_value=gql_params.context, - root_value=None, - variable_values={}, - ) - - assert result.errors is None - assert result.data["InfrahubAccountSelfUpdate"]["ok"] is True - - updated_account = await NodeManager.get_one(db=db, id=first_account.id, branch=default_branch) - assert bcrypt.checkpw(new_password.encode("UTF-8"), updated_account.password.value.encode("UTF-8")) - assert updated_account.description.value == new_description diff --git a/backend/tests/unit/graphql/test_diff_tree_query.py b/backend/tests/unit/graphql/test_diff_tree_query.py index b8159b38f9..fd861efeb8 100644 --- a/backend/tests/unit/graphql/test_diff_tree_query.py +++ b/backend/tests/unit/graphql/test_diff_tree_query.py @@ -14,12 +14,12 @@ from infrahub.core.manager import NodeManager from infrahub.core.node import Node from infrahub.core.schema import NodeSchema -from infrahub.core.schema_manager import SchemaBranch +from infrahub.core.schema.schema_branch import SchemaBranch from infrahub.core.timestamp import Timestamp from infrahub.database import InfrahubDatabase from infrahub.dependencies.registry import get_component_registry -from infrahub.graphql import prepare_graphql_params from infrahub.graphql.enums import ConflictSelection as GraphQLConfictSelection +from infrahub.graphql.initialization import prepare_graphql_params ADDED_ACTION = "ADDED" UPDATED_ACTION = "UPDATED" diff --git a/backend/tests/unit/graphql/test_graphql_query.py b/backend/tests/unit/graphql/test_graphql_query.py index f33cfb9713..9ece69ad92 100644 --- a/backend/tests/unit/graphql/test_graphql_query.py +++ b/backend/tests/unit/graphql/test_graphql_query.py @@ -12,10 +12,10 @@ from infrahub.core.manager import NodeManager from infrahub.core.node import Node from infrahub.core.schema import NodeSchema -from infrahub.core.schema_manager import SchemaBranch +from infrahub.core.schema.schema_branch import SchemaBranch from infrahub.core.timestamp import Timestamp from infrahub.database import InfrahubDatabase -from infrahub.graphql import prepare_graphql_params +from infrahub.graphql.initialization import prepare_graphql_params async def test_info_query(db: InfrahubDatabase, default_branch: Branch, criticality_schema: NodeSchema): diff --git a/backend/tests/unit/graphql/test_graphql_utils.py b/backend/tests/unit/graphql/test_graphql_utils.py index 11ad5f7b4d..a30ae10d01 100644 --- a/backend/tests/unit/graphql/test_graphql_utils.py +++ b/backend/tests/unit/graphql/test_graphql_utils.py @@ -4,8 +4,8 @@ from infrahub.core.branch import Branch from infrahub.core.constants import InfrahubKind from infrahub.database import InfrahubDatabase -from infrahub.graphql import generate_graphql_schema from infrahub.graphql.analyzer import extract_schema_models +from infrahub.graphql.initialization import generate_graphql_schema async def test_schema_models(db: InfrahubDatabase, default_branch: Branch, car_person_schema_generics, query_01: str): @@ -38,6 +38,7 @@ async def test_schema_models_generics( InfrahubKind.GRAPHQLQUERYGROUP, InfrahubKind.GENERICGROUP, InfrahubKind.STANDARDGROUP, + InfrahubKind.ACCOUNTGROUP, "EdgedTestPerson", "NestedEdgedCoreGroup", "NestedEdgedTestCar", diff --git a/backend/tests/unit/graphql/test_mutation_artifact_definition.py b/backend/tests/unit/graphql/test_mutation_artifact_definition.py index 25b07d8779..f9318415fd 100644 --- a/backend/tests/unit/graphql/test_mutation_artifact_definition.py +++ b/backend/tests/unit/graphql/test_mutation_artifact_definition.py @@ -8,7 +8,7 @@ from infrahub.core.manager import NodeManager from infrahub.core.node import Node from infrahub.database import InfrahubDatabase -from infrahub.graphql import prepare_graphql_params +from infrahub.graphql.initialization import prepare_graphql_params from infrahub.message_bus import messages from infrahub.services import InfrahubServices from tests.adapters.message_bus import BusRecorder diff --git a/backend/tests/unit/graphql/test_mutation_create.py b/backend/tests/unit/graphql/test_mutation_create.py index 1d1b1958d6..958ba0c4c0 100644 --- a/backend/tests/unit/graphql/test_mutation_create.py +++ b/backend/tests/unit/graphql/test_mutation_create.py @@ -8,7 +8,7 @@ from infrahub.core.manager import NodeManager from infrahub.core.node import Node from infrahub.database import InfrahubDatabase -from infrahub.graphql import prepare_graphql_params +from infrahub.graphql.initialization import prepare_graphql_params async def test_create_simple_object(db: InfrahubDatabase, default_branch, car_person_schema): diff --git a/backend/tests/unit/graphql/test_mutation_delete.py b/backend/tests/unit/graphql/test_mutation_delete.py index 59063bc2bd..0c5769b7be 100644 --- a/backend/tests/unit/graphql/test_mutation_delete.py +++ b/backend/tests/unit/graphql/test_mutation_delete.py @@ -3,7 +3,7 @@ from infrahub.core.manager import NodeManager from infrahub.core.node import Node from infrahub.database import InfrahubDatabase -from infrahub.graphql import prepare_graphql_params +from infrahub.graphql.initialization import prepare_graphql_params async def test_delete_object(db: InfrahubDatabase, default_branch, car_person_schema): diff --git a/backend/tests/unit/graphql/test_mutation_graphqlquery.py b/backend/tests/unit/graphql/test_mutation_graphqlquery.py index f45c4e5900..a7831378e5 100644 --- a/backend/tests/unit/graphql/test_mutation_graphqlquery.py +++ b/backend/tests/unit/graphql/test_mutation_graphqlquery.py @@ -4,7 +4,7 @@ from infrahub.core.constants import InfrahubKind from infrahub.core.node import Node from infrahub.database import InfrahubDatabase -from infrahub.graphql import prepare_graphql_params +from infrahub.graphql.initialization import prepare_graphql_params async def test_create_query_no_vars(db: InfrahubDatabase, default_branch, register_core_models_schema): diff --git a/backend/tests/unit/graphql/test_mutation_relationship.py b/backend/tests/unit/graphql/test_mutation_relationship.py index 7b7f4b7b4f..eed5daa260 100644 --- a/backend/tests/unit/graphql/test_mutation_relationship.py +++ b/backend/tests/unit/graphql/test_mutation_relationship.py @@ -7,7 +7,7 @@ from infrahub.core.node import Node from infrahub.core.utils import count_relationships from infrahub.database import InfrahubDatabase -from infrahub.graphql import prepare_graphql_params +from infrahub.graphql.initialization import prepare_graphql_params async def test_relationship_add( diff --git a/backend/tests/unit/graphql/test_mutation_update.py b/backend/tests/unit/graphql/test_mutation_update.py index 2abdf97645..ed266fa7c2 100644 --- a/backend/tests/unit/graphql/test_mutation_update.py +++ b/backend/tests/unit/graphql/test_mutation_update.py @@ -7,7 +7,7 @@ from infrahub.core.manager import NodeManager from infrahub.core.node import Node from infrahub.database import InfrahubDatabase -from infrahub.graphql import prepare_graphql_params +from infrahub.graphql.initialization import prepare_graphql_params async def test_update_simple_object(db: InfrahubDatabase, person_john_main: Node, branch: Branch): diff --git a/backend/tests/unit/graphql/test_mutation_upsert.py b/backend/tests/unit/graphql/test_mutation_upsert.py index 7156a91213..e51e394fd9 100644 --- a/backend/tests/unit/graphql/test_mutation_upsert.py +++ b/backend/tests/unit/graphql/test_mutation_upsert.py @@ -8,7 +8,7 @@ from infrahub.core.registry import registry from infrahub.core.schema import SchemaRoot from infrahub.database import InfrahubDatabase -from infrahub.graphql import prepare_graphql_params +from infrahub.graphql.initialization import prepare_graphql_params from tests.constants import TestKind from tests.helpers.schema import TICKET diff --git a/backend/tests/unit/graphql/test_parser.py b/backend/tests/unit/graphql/test_parser.py index e5df6f8ac3..a29c357f0f 100644 --- a/backend/tests/unit/graphql/test_parser.py +++ b/backend/tests/unit/graphql/test_parser.py @@ -3,7 +3,7 @@ from infrahub.core.branch import Branch from infrahub.core.node import Node from infrahub.database import InfrahubDatabase -from infrahub.graphql import prepare_graphql_params +from infrahub.graphql.initialization import prepare_graphql_params async def test_simple_directive(db: InfrahubDatabase, default_branch: Branch, criticality_schema): diff --git a/backend/tests/unit/graphql/test_query_analyzer.py b/backend/tests/unit/graphql/test_query_analyzer.py index 9adbbe6ca6..3323c84204 100644 --- a/backend/tests/unit/graphql/test_query_analyzer.py +++ b/backend/tests/unit/graphql/test_query_analyzer.py @@ -3,8 +3,8 @@ from infrahub.core.branch import Branch from infrahub.core.constants import InfrahubKind from infrahub.database import InfrahubDatabase -from infrahub.graphql import prepare_graphql_params from infrahub.graphql.analyzer import InfrahubGraphQLQueryAnalyzer +from infrahub.graphql.initialization import prepare_graphql_params async def test_analyzer_init_with_schema( @@ -98,6 +98,7 @@ async def test_get_models_in_use( InfrahubKind.GRAPHQLQUERYGROUP, InfrahubKind.GENERICGROUP, InfrahubKind.STANDARDGROUP, + InfrahubKind.ACCOUNTGROUP, "TestCar", "TestElectricCar", "TestGazCar", diff --git a/sync/examples/nautobot-v2_to_infrahub/infrahub/__init__.py b/backend/tests/unit/menu/__init__.py similarity index 100% rename from sync/examples/nautobot-v2_to_infrahub/infrahub/__init__.py rename to backend/tests/unit/menu/__init__.py diff --git a/backend/tests/unit/menu/test_generator.py b/backend/tests/unit/menu/test_generator.py new file mode 100644 index 0000000000..1781ae4c02 --- /dev/null +++ b/backend/tests/unit/menu/test_generator.py @@ -0,0 +1,46 @@ +from infrahub.core import registry +from infrahub.core.branch import Branch +from infrahub.core.initialization import create_default_menu +from infrahub.core.protocols import CoreMenuItem +from infrahub.core.schema import SchemaRoot +from infrahub.database import InfrahubDatabase +from infrahub.menu.constants import MenuSection +from infrahub.menu.generator import generate_menu +from infrahub.menu.models import MenuItemDefinition + + +async def test_generate_menu( + db: InfrahubDatabase, + default_branch: Branch, + car_person_schema_generics: SchemaRoot, +): + schema_branch = registry.schema.get_schema_branch(name=default_branch.name) + + schema_electriccar = schema_branch.get(name="TestElectricCar") + schema_electriccar.menu_placement = "Builtin:ObjectManagement" + schema_branch.set(name="TestElectricCar", schema=schema_electriccar) + + await create_default_menu(db=db) + + new_menu_items = [ + MenuItemDefinition( + namespace="Test", + name="CarGaz", + label="Car Gaz", + kind="TestCarGaz", + section=MenuSection.OBJECT, + order_weight=1500, + ) + ] + + for item in new_menu_items: + obj = await item.to_node(db=db) + await obj.save(db=db) + + menu_items = await registry.manager.query( + db=db, schema=CoreMenuItem, branch=default_branch, prefetch_relationships=True + ) + menu = await generate_menu(db=db, branch=default_branch, menu_items=menu_items) + + assert menu + assert "Test:CarGaz" in menu.data.keys() diff --git a/backend/tests/unit/message_bus/operations/requests/test_graphql_query_group.py b/backend/tests/unit/message_bus/operations/requests/test_graphql_query_group.py index abbdc32c3b..b3045a2293 100644 --- a/backend/tests/unit/message_bus/operations/requests/test_graphql_query_group.py +++ b/backend/tests/unit/message_bus/operations/requests/test_graphql_query_group.py @@ -63,4 +63,4 @@ async def test_graphql_group_update(db: InfrahubDatabase, httpx_mock: HTTPXMock, match_headers={"X-Infrahub-Tracker": "mutation-relationshipadd"}, ) - await update(message=message, service=service) + await update.fn(message=message, service=service) diff --git a/backend/tests/unit/message_bus/operations/requests/test_proposed_change.py b/backend/tests/unit/message_bus/operations/requests/test_proposed_change.py index bfa8a00adc..cb8c6c6371 100644 --- a/backend/tests/unit/message_bus/operations/requests/test_proposed_change.py +++ b/backend/tests/unit/message_bus/operations/requests/test_proposed_change.py @@ -128,8 +128,8 @@ async def test_get_proposed_change_schema_integrity_constraints( ) non_generate_profile_constraints = [c for c in constraints if c.constraint_name != "node.generate_profile.update"] # should be updated/removed when ConstraintValidatorDeterminer is updated (#2592) - assert len(constraints) == 159 - assert len(non_generate_profile_constraints) == 94 + assert len(constraints) == 181 + assert len(non_generate_profile_constraints) == 109 dumped_constraints = [c.model_dump() for c in non_generate_profile_constraints] assert { "constraint_name": "relationship.optional.update", @@ -288,8 +288,8 @@ async def test_schema_integrity( branch2_schema.set(name="TestPerson", schema=person_schema) # Ignore creation of Task Report response - httpx_mock.add_response(method="POST", url="http://mock/graphql", json={"data": {}}) - await proposed_change.schema_integrity(message=schema_integrity_01, service=service_all) + httpx_mock.add_response(method="POST", url="http://mock/graphql/main", json={"data": {}}) + await proposed_change.schema_integrity.fn(message=schema_integrity_01, service=service_all) checks = await registry.manager.query(db=db, schema=InfrahubKind.SCHEMACHECK) assert len(checks) == 1 diff --git a/backend/tests/unit/message_bus/test_mappings.py b/backend/tests/unit/message_bus/test_mappings.py index ffae6c3ce4..7624b3ba7b 100644 --- a/backend/tests/unit/message_bus/test_mappings.py +++ b/backend/tests/unit/message_bus/test_mappings.py @@ -1,3 +1,10 @@ +from __future__ import annotations + +from typing import Callable + +import pytest +from prefect import Flow + from infrahub.message_bus.messages import MESSAGE_MAP from infrahub.message_bus.operations import COMMAND_MAP @@ -7,3 +14,14 @@ def test_message_command_overlap(): messages = sorted(list(MESSAGE_MAP.keys())) commands = sorted(list(COMMAND_MAP.keys())) assert messages == commands + + +@pytest.mark.parametrize( + "operation", + [pytest.param(function, id=key) for key, function in COMMAND_MAP.items() if not key.startswith("refresh.registry")], +) +def test_operations_decorated(operation: Callable): + if callable(operation) and hasattr(operation, "__name__") and "Flow" not in type(operation).__name__: + pytest.fail(f"{operation.__name__} is not decorated with @flow") + else: + assert isinstance(operation, Flow), f"{operation.__name__} is not a valid Prefect flow" diff --git a/sync/examples/nautobot-v2_to_infrahub/nautobot/__init__.py b/backend/tests/unit/permissions/__init__.py similarity index 100% rename from sync/examples/nautobot-v2_to_infrahub/nautobot/__init__.py rename to backend/tests/unit/permissions/__init__.py diff --git a/backend/tests/unit/permissions/test_backends.py b/backend/tests/unit/permissions/test_backends.py new file mode 100644 index 0000000000..ac8dde385f --- /dev/null +++ b/backend/tests/unit/permissions/test_backends.py @@ -0,0 +1,205 @@ +from infrahub.core.account import GlobalPermission, ObjectPermission +from infrahub.core.branch import Branch +from infrahub.core.constants import GlobalPermissions, InfrahubKind, PermissionAction, PermissionDecision +from infrahub.core.node import Node +from infrahub.core.protocols import CoreAccount +from infrahub.database import InfrahubDatabase +from infrahub.permissions import LocalPermissionBackend + + +async def test_load_permissions(db: InfrahubDatabase, default_branch: Branch, create_test_admin, first_account): + backend = LocalPermissionBackend() + + permissions = await backend.load_permissions(db=db, account_id=create_test_admin.id, branch=default_branch) + + assert "global_permissions" in permissions + assert permissions["global_permissions"][0].action == GlobalPermissions.SUPER_ADMIN.value + + assert "object_permissions" in permissions + assert str(permissions["object_permissions"][0]) == str( + ObjectPermission( + id="", + branch="*", + namespace="*", + name="*", + action=PermissionAction.ANY.value, + decision=PermissionDecision.ALLOW.value, + ) + ) + + permissions = await backend.load_permissions(db=db, account_id=first_account.id, branch=default_branch) + + assert "global_permissions" in permissions + assert not permissions["global_permissions"] + + assert "object_permissions" in permissions + assert not permissions["object_permissions"] + + +async def test_has_permission_global( + db: InfrahubDatabase, + default_branch: Branch, + register_core_models_schema: None, + create_test_admin: CoreAccount, + first_account: CoreAccount, + second_account: CoreAccount, +): + backend = LocalPermissionBackend() + + allow_default_branch_edition = GlobalPermission( + id="", + action=GlobalPermissions.EDIT_DEFAULT_BRANCH.value, + decision=PermissionDecision.ALLOW.value, + name="Edit default branch", + ) + + role1_permissions = [] + obj = await Node.init(db=db, schema=InfrahubKind.GLOBALPERMISSION) + await obj.new( + db=db, + name=allow_default_branch_edition.name, + action=allow_default_branch_edition.action, + decision=allow_default_branch_edition.decision, + ) + await obj.save(db=db) + role1_permissions.append(obj) + + role1 = await Node.init(db=db, schema=InfrahubKind.ACCOUNTROLE) + await role1.new(db=db, name="anything but tags", permissions=role1_permissions) + await role1.save(db=db) + + group1 = await Node.init(db=db, schema=InfrahubKind.ACCOUNTGROUP) + await group1.new(db=db, name="group1", roles=[role1]) + await group1.save(db=db) + + await group1.members.add(db=db, data={"id": first_account.id}) + await group1.members.save(db=db) + + role2_permissions = [] + for p in [ + allow_default_branch_edition, + GlobalPermission( + id="", + action=GlobalPermissions.EDIT_DEFAULT_BRANCH.value, + decision=PermissionDecision.DENY.value, + name="Edit default branch", + ), + ]: + obj = await Node.init(db=db, schema=InfrahubKind.GLOBALPERMISSION) + await obj.new(db=db, name=p.name, action=p.action, decision=p.decision) + await obj.save(db=db) + role2_permissions.append(obj) + + role2 = await Node.init(db=db, schema=InfrahubKind.ACCOUNTROLE) + await role2.new(db=db, name="only tags", permissions=role2_permissions) + await role2.save(db=db) + + group2 = await Node.init(db=db, schema=InfrahubKind.ACCOUNTGROUP) + await group2.new(db=db, name="group2", roles=[role2]) + await group2.save(db=db) + + await group2.members.add(db=db, data={"id": second_account.id}) + await group2.members.save(db=db) + + assert await backend.has_permission( + db=db, account_id=first_account.id, permission=str(allow_default_branch_edition), branch=default_branch + ) + assert not await backend.has_permission( + db=db, account_id=second_account.id, permission=str(allow_default_branch_edition), branch=default_branch + ) + + +async def test_has_permission_object( + db: InfrahubDatabase, + default_branch: Branch, + register_core_models_schema: None, + create_test_admin: CoreAccount, + first_account: CoreAccount, + second_account: CoreAccount, +): + backend = LocalPermissionBackend() + + role1_permissions = [] + for p in [ + ObjectPermission( + id="", + branch="*", + namespace="*", + name="*", + action=PermissionAction.ANY.value, + decision=PermissionDecision.ALLOW.value, + ), + ObjectPermission( + id="", + branch="*", + namespace="Builtin", + name="Tag", + action=PermissionAction.ANY.value, + decision=PermissionDecision.DENY.value, + ), + ]: + obj = await Node.init(db=db, schema=InfrahubKind.OBJECTPERMISSION) + await obj.new(db=db, branch=p.branch, namespace=p.namespace, name=p.name, action=p.action, decision=p.decision) + await obj.save(db=db) + role1_permissions.append(obj) + + role1 = await Node.init(db=db, schema=InfrahubKind.ACCOUNTROLE) + await role1.new(db=db, name="anything but tags", permissions=role1_permissions) + await role1.save(db=db) + + group1 = await Node.init(db=db, schema=InfrahubKind.ACCOUNTGROUP) + await group1.new(db=db, name="group1", roles=[role1]) + await group1.save(db=db) + + await group1.members.add(db=db, data={"id": first_account.id}) + await group1.members.save(db=db) + + role2_permissions = [] + for p in [ + ObjectPermission( + id="", + branch="*", + namespace="*", + name="*", + action=PermissionAction.ANY.value, + decision=PermissionDecision.DENY.value, + ), + ObjectPermission( + id="", + branch="*", + namespace="Builtin", + name="Tag", + action=PermissionAction.ANY.value, + decision=PermissionDecision.ALLOW.value, + ), + ]: + obj = await Node.init(db=db, schema=InfrahubKind.OBJECTPERMISSION) + await obj.new(db=db, branch=p.branch, namespace=p.namespace, name=p.name, action=p.action, decision=p.decision) + await obj.save(db=db) + role2_permissions.append(obj) + + role2 = await Node.init(db=db, schema=InfrahubKind.ACCOUNTROLE) + await role2.new(db=db, name="only tags", permissions=role2_permissions) + await role2.save(db=db) + + group2 = await Node.init(db=db, schema=InfrahubKind.ACCOUNTGROUP) + await group2.new(db=db, name="group2", roles=[role2]) + await group2.save(db=db) + + await group2.members.add(db=db, data={"id": second_account.id}) + await group2.members.save(db=db) + + permission = ObjectPermission( + id="", + branch="*", + namespace="Builtin", + name="Tag", + action=PermissionAction.ADD.value, + decision=PermissionDecision.ALLOW.value, + ) + assert not await backend.has_permission( + db=db, account_id=first_account.id, permission=str(permission), branch=default_branch + ) + assert await backend.has_permission( + db=db, account_id=second_account.id, permission=str(permission), branch=default_branch + ) diff --git a/backend/tests/unit/services/adapters/redis/test_redis.py b/backend/tests/unit/services/adapters/redis/test_redis.py index a7348438f1..e2c36cca48 100644 --- a/backend/tests/unit/services/adapters/redis/test_redis.py +++ b/backend/tests/unit/services/adapters/redis/test_redis.py @@ -8,7 +8,7 @@ from infrahub.services.adapters.cache.redis import RedisCache -async def test_get(): +async def test_get(redis): if config.SETTINGS.cache.driver != config.CacheDriver.Redis: pytest.skip("Must use Redis to run this test") @@ -26,7 +26,7 @@ async def test_get(): assert after_expiry is None -async def test_list_keys(): +async def test_list_keys(redis): if config.SETTINGS.cache.driver != config.CacheDriver.Redis: pytest.skip("Must use Redis to run this test") diff --git a/sync/examples/netbox_to_infrahub/infrahub/__init__.py b/backend/tests/unit/workflows/__init__.py similarity index 100% rename from sync/examples/netbox_to_infrahub/infrahub/__init__.py rename to backend/tests/unit/workflows/__init__.py diff --git a/backend/tests/unit/workflows/test_catalogue.py b/backend/tests/unit/workflows/test_catalogue.py new file mode 100644 index 0000000000..f219c442a1 --- /dev/null +++ b/backend/tests/unit/workflows/test_catalogue.py @@ -0,0 +1,16 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +import pytest + +from infrahub.workflows.catalogue import workflows + +if TYPE_CHECKING: + from infrahub.workflows.models import WorkflowDefinition + + +@pytest.mark.parametrize("workflow", [pytest.param(workflow, id=workflow.name) for workflow in workflows]) +def test_workflow_definition(workflow: WorkflowDefinition) -> None: + """Validate that we can import the function for each workflow.""" + workflow.validate_workflow() diff --git a/changelog/+compose-ansi.fixed.md b/changelog/+compose-ansi.fixed.md new file mode 100644 index 0000000000..bdbe0f923a --- /dev/null +++ b/changelog/+compose-ansi.fixed.md @@ -0,0 +1 @@ +Fixes an issue where docker compose would output ANSI control characters that don't support it diff --git a/changelog/1075.fixed.md b/changelog/1075.fixed.md new file mode 100644 index 0000000000..02ee48e323 --- /dev/null +++ b/changelog/1075.fixed.md @@ -0,0 +1 @@ +The `infrahub-git` agent service has been renamed to `task-worker` in docker compose and the command to start it has been updated as well \ No newline at end of file diff --git a/changelog/3302.added.md b/changelog/3302.added.md new file mode 100644 index 0000000000..e7d414c329 --- /dev/null +++ b/changelog/3302.added.md @@ -0,0 +1 @@ +Add internal HTTP adapter to allow for generic access from Infrahub diff --git a/changelog/3435.fixed.md b/changelog/3435.fixed.md new file mode 100644 index 0000000000..d91c9c7382 --- /dev/null +++ b/changelog/3435.fixed.md @@ -0,0 +1 @@ +Add ability to import repositories with default branch other than 'main'. diff --git a/changelog/3908.added.md b/changelog/3908.added.md new file mode 100644 index 0000000000..8612bf2e8d --- /dev/null +++ b/changelog/3908.added.md @@ -0,0 +1 @@ +Add support to search a node by human friendly ID within a GraphQL query \ No newline at end of file diff --git a/changelog/4062.fixed.md b/changelog/4062.fixed.md new file mode 100644 index 0000000000..dfbbe0e503 --- /dev/null +++ b/changelog/4062.fixed.md @@ -0,0 +1 @@ +Allow users to run artifacts and generators on nodes without name attribute \ No newline at end of file diff --git a/development/Dockerfile b/development/Dockerfile index f53f6f0486..71d8d6e99f 100644 --- a/development/Dockerfile +++ b/development/Dockerfile @@ -72,10 +72,12 @@ RUN git config --global user.name "Infrahub" && \ git config --global credential.usehttppath true && \ git config --global credential.helper /usr/local/bin/infrahub-git-credential -RUN mkdir -p /opt/infrahub/git /opt/infrahub/storage /opt/infrahub/source /opt/infrahub/frontend/app/dist +RUN mkdir -p /opt/infrahub/git /opt/infrahub/storage /opt/infrahub/source /opt/infrahub/frontend/app/dist /opt/infrahub/workflow WORKDIR /source +ENV PREFECT_WORKER_QUERY_SECONDS="2" + # -------------------------------------------- # Import Frontend Build # -------------------------------------------- diff --git a/development/docker-compose-database-memgraph.yml b/development/docker-compose-database-memgraph.yml index abc4bc01ee..917e585568 100644 --- a/development/docker-compose-database-memgraph.yml +++ b/development/docker-compose-database-memgraph.yml @@ -1,6 +1,7 @@ --- services: database: + profiles: [demo, dev] image: "${DATABASE_DOCKER_IMAGE:-memgraph/memgraph-mage:latest}" init: true volumes: diff --git a/development/docker-compose-database-neo4j.yml b/development/docker-compose-database-neo4j.yml index e0ab7e00eb..b5ffcb80b0 100644 --- a/development/docker-compose-database-neo4j.yml +++ b/development/docker-compose-database-neo4j.yml @@ -1,6 +1,7 @@ --- services: database: + profiles: [demo, dev] image: "${DATABASE_DOCKER_IMAGE:-neo4j:enterprise}" environment: - "NEO4J_AUTH=neo4j/admin" diff --git a/development/docker-compose-deps-nats.yml b/development/docker-compose-deps-nats.yml index 947daa2ad9..c686a31694 100644 --- a/development/docker-compose-deps-nats.yml +++ b/development/docker-compose-deps-nats.yml @@ -2,6 +2,7 @@ # yamllint disable rule:line-length services: message-queue: + profiles: [demo, dev] image: "${MESSAGE_QUEUE_DOCKER_IMAGE:-nats:alpine}" # need alpine since latest is based on scratch (no wget available) volumes: - ./nats-server.conf:/etc/nats/nats-server.conf @@ -13,6 +14,7 @@ services: start_period: 10s # This is actually unused but required to not break all the other compose files requiring this service... cache: + profiles: [demo, dev] image: "${MESSAGE_QUEUE_DOCKER_IMAGE:-nats:alpine}" healthcheck: test: wget -O /dev/null http://localhost:8222/varz || exit 1 @@ -20,3 +22,37 @@ services: timeout: 5s retries: 20 start_period: 10s + task-manager: + profiles: [dev] + image: "${TASK_MANAGER_DOCKER_IMAGE:-prefecthq/prefect:3.0.3-python3.12}" + command: prefect server start --host 0.0.0.0 --ui + environment: + PREFECT_API_DATABASE_CONNECTION_URL: postgresql+asyncpg://postgres:postgres@task-manager-db:5432/prefect + PREFECT_LOCAL_STORAGE_PATH: ${PREFECT_LOCAL_STORAGE_PATH:-/opt/prefect/} + PREFECT_WORKER_QUERY_SECONDS: 3 + PREFECT_AGENT_QUERY_INTERVAL: 3 + healthcheck: + test: /usr/local/bin/httpx http://localhost:4200/api/health || exit 1 + interval: 5s + timeout: 5s + retries: 20 + start_period: 10s + depends_on: + - task-manager-db + task-manager-db: + profiles: [dev] + image: postgres:16-alpine + environment: + - POSTGRES_USER=postgres + - POSTGRES_PASSWORD=postgres + - POSTGRES_DB=prefect + volumes: + - workflow_db:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready"] + interval: 10s + timeout: 5s + retries: 5 + +volumes: + workflow_db: diff --git a/development/docker-compose-deps.yml b/development/docker-compose-deps.yml index 00ed88f1ec..e19c321242 100644 --- a/development/docker-compose-deps.yml +++ b/development/docker-compose-deps.yml @@ -1,6 +1,7 @@ --- services: message-queue: + profiles: [demo, dev] user: rabbitmq image: "${MESSAGE_QUEUE_DOCKER_IMAGE:-rabbitmq:latest}" environment: @@ -13,9 +14,42 @@ services: retries: 10 start_period: 3s cache: + profiles: [demo, dev] image: "${CACHE_DOCKER_IMAGE:-redis:latest}" healthcheck: test: ["CMD-SHELL", "redis-cli ping | grep PONG"] interval: 5s timeout: 5s retries: 3 + task-manager: + profiles: [dev] + image: "${TASK_MANAGER_DOCKER_IMAGE:-prefecthq/prefect:3.0.3-python3.12}" + command: prefect server start --host 0.0.0.0 --ui + environment: + PREFECT_API_DATABASE_CONNECTION_URL: postgresql+asyncpg://postgres:postgres@task-manager-db:5432/prefect + PREFECT_LOCAL_STORAGE_PATH: ${PREFECT_LOCAL_STORAGE_PATH:-/opt/prefect/} + healthcheck: + test: /usr/local/bin/httpx http://localhost:4200/api/health || exit 1 + interval: 5s + timeout: 5s + retries: 20 + start_period: 10s + depends_on: + - task-manager-db + task-manager-db: + profiles: [dev] + image: postgres:16-alpine + environment: + - POSTGRES_USER=postgres + - POSTGRES_PASSWORD=postgres + - POSTGRES_DB=prefect + volumes: + - workflow_db:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready"] + interval: 10s + timeout: 5s + retries: 5 + +volumes: + workflow_db: diff --git a/development/docker-compose-test-cache.yml b/development/docker-compose-test-cache.yml deleted file mode 100644 index 8306345976..0000000000 --- a/development/docker-compose-test-cache.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -# yamllint disable rule:line-length -services: - cache: - deploy: - mode: replicated - replicas: "${NBR_WORKERS}" diff --git a/development/docker-compose-test-deps.yml b/development/docker-compose-test-deps.yml new file mode 100644 index 0000000000..df0c6ec7b8 --- /dev/null +++ b/development/docker-compose-test-deps.yml @@ -0,0 +1,15 @@ +--- +# yamllint disable rule:line-length +services: + cache: + deploy: + mode: replicated + replicas: "${NBR_WORKERS}" + message-queue: + deploy: + mode: replicated + replicas: "${NBR_WORKERS}" + task-manager: + deploy: + mode: replicated + replicas: "${NBR_WORKERS}" diff --git a/development/docker-compose-test-scale.yml b/development/docker-compose-test-scale.yml index cbb82c9590..4c7d7180f8 100644 --- a/development/docker-compose-test-scale.yml +++ b/development/docker-compose-test-scale.yml @@ -84,7 +84,7 @@ x-infrahub-config: &infrahub_config OTEL_RESOURCE_ATTRIBUTES: services: - infrahub-server: + server: build: context: ../ dockerfile: development/Dockerfile diff --git a/development/docker-compose.default.yml b/development/docker-compose.default.yml index 02281d6fe6..5457942a84 100644 --- a/development/docker-compose.default.yml +++ b/development/docker-compose.default.yml @@ -1,5 +1,5 @@ --- services: - infrahub-server: + server: ports: - "${INFRAHUB_SERVER_PORT:-8000}:8000" diff --git a/development/docker-compose.dev-override.yml.tmp b/development/docker-compose.dev-override.yml.tmp index 59348e84a8..6ca6ee23be 100644 --- a/development/docker-compose.dev-override.yml.tmp +++ b/development/docker-compose.dev-override.yml.tmp @@ -12,3 +12,6 @@ services: cache: ports: - "6379:6379" + task-manager: + ports: + - "4200:4200" \ No newline at end of file diff --git a/development/docker-compose.local-build.yml b/development/docker-compose.local-build.yml index 928bc2f6dd..f136a71abb 100644 --- a/development/docker-compose.local-build.yml +++ b/development/docker-compose.local-build.yml @@ -1,10 +1,11 @@ --- services: - infrahub-server: + server: volumes: - ../:/source - "storage_data:/opt/infrahub/storage" - infrahub-git: + + task-worker: volumes: - ../:/source - "git_data:/opt/infrahub/git" diff --git a/development/docker-compose.override.yml.tmp b/development/docker-compose.override.yml.tmp index 0967decb3d..0e0edada6f 100644 --- a/development/docker-compose.override.yml.tmp +++ b/development/docker-compose.override.yml.tmp @@ -95,7 +95,7 @@ services: # -------------------------------------------------------------------------------- # Infrahub Server # -------------------------------------------------------------------------------- - infrahub-server: + server: ports: - "8000:8000" diff --git a/development/docker-compose.yml b/development/docker-compose.yml index 4f311c59c6..3080404e5a 100644 --- a/development/docker-compose.yml +++ b/development/docker-compose.yml @@ -92,10 +92,13 @@ x-infrahub-config: &infrahub_config INFRAHUB_TRACE_EXPORTER_PROTOCOL: INFRAHUB_TRACE_EXPORTER_TYPE: INFRAHUB_TRACE_INSECURE: + INFRAHUB_WORKFLOW_ADDRESS: + INFRAHUB_WORKFLOW_PORT: OTEL_RESOURCE_ATTRIBUTES: services: - infrahub-server: + server: + profiles: [demo, dev] build: context: ../ dockerfile: development/Dockerfile @@ -111,6 +114,9 @@ services: condition: service_healthy cache: condition: service_healthy + task-manager: + condition: service_healthy + required: false environment: <<: *infrahub_config INFRAHUB_CONFIG: /source/development/infrahub.toml @@ -119,8 +125,12 @@ services: INFRAHUB_SECURITY_SECRET_KEY: 327f747f-efac-42be-9e73-999f08f86b92 INFRAHUB_BROKER_ADDRESS: message-queue INFRAHUB_CACHE_ADDRESS: "${INFRAHUB_CACHE_ADDRESS:-cache}" + INFRAHUB_WORKFLOW_ADDRESS: task-manager + INFRAHUB_WORKFLOW_PORT: 4200 + PREFECT_API_URL: "http://task-manager:4200/api" volumes: - "storage_data:/opt/infrahub/storage" + - "workflow_data:/opt/infrahub/workflow" tty: true labels: com.github.run_id: "${GITHUB_RUN_ID:-unknown}" @@ -132,6 +142,7 @@ services: retries: 20 start_period: 10s infrahub-git: + profiles: [demo] deploy: mode: replicated replicas: 2 @@ -144,11 +155,11 @@ services: command: infrahub git-agent start --debug restart: unless-stopped depends_on: - - infrahub-server + - server environment: <<: *infrahub_config INFRAHUB_CONFIG: /source/development/infrahub.toml - INFRAHUB_ADDRESS: http://infrahub-server:8000 + INFRAHUB_ADDRESS: http://server:8000 INFRAHUB_PRODUCTION: false INFRAHUB_LOG_LEVEL: DEBUG INFRAHUB_API_TOKEN: 06438eb2-8019-4776-878c-0941b1f1d1ec @@ -163,7 +174,45 @@ services: com.github.run_id: "${GITHUB_RUN_ID:-unknown}" com.github.job: "${JOB_NAME:-unknown}" + task-worker: + profiles: [dev] + deploy: + mode: replicated + replicas: 2 + build: + context: ../ + dockerfile: development/Dockerfile + target: backend + image: "${IMAGE_NAME}:${IMAGE_VER}" + pull_policy: always + command: prefect worker start --type infrahubasync --pool infrahub-worker --with-healthcheck + restart: unless-stopped + depends_on: + - server + environment: + <<: *infrahub_config + INFRAHUB_CONFIG: /source/development/infrahub.toml + INFRAHUB_ADDRESS: http://server:8000 + INFRAHUB_INTERNAL_ADDRESS: "http://server:8000" + INFRAHUB_DB_ADDRESS: "database" + INFRAHUB_PRODUCTION: false + INFRAHUB_API_TOKEN: 06438eb2-8019-4776-878c-0941b1f1d1ec + INFRAHUB_TIMEOUT: 20 + INFRAHUB_BROKER_ADDRESS: message-queue + INFRAHUB_CACHE_ADDRESS: "${INFRAHUB_CACHE_ADDRESS:-cache}" + INFRAHUB_WORKFLOW_ADDRESS: "${INFRAHUB_WORKFLOW_ADDRESS:-task-manager}" + INFRAHUB_WORKFLOW_PORT: "${INFRAHUB_WORKFLOW_PORT:-4200}" + PREFECT_API_URL: "http://task-manager:4200/api" + volumes: + - "git_data:/opt/infrahub/git" + - "git_remote_data:/remote" + tty: true + labels: + com.github.run_id: "${GITHUB_RUN_ID:-unknown}" + com.github.job: "${JOB_NAME:-unknown}" + volumes: git_data: git_remote_data: storage_data: + workflow_data: diff --git a/development/infrahub.toml b/development/infrahub.toml index a2024a1bac..fdc7cbe407 100644 --- a/development/infrahub.toml +++ b/development/infrahub.toml @@ -1,5 +1,5 @@ [main] -internal_address = "http://infrahub-server:8000" +internal_address = "http://server:8000" [git] repositories_directory = "/opt/infrahub/git" diff --git a/development/prometheus/config.yml b/development/prometheus/config.yml index 0c2fb6c0ff..c08f2b736d 100644 --- a/development/prometheus/config.yml +++ b/development/prometheus/config.yml @@ -9,8 +9,8 @@ scrape_configs: - job_name: 'infrahub' static_configs: - - targets: ['infrahub-server:8000'] - - targets: ['infrahub-git:8000'] + - targets: ['server:8000'] + - targets: ['task-worker:8000'] - job_name: 'message-queue' static_configs: diff --git a/development/vmagent.yml b/development/vmagent.yml index c09179cf08..c8dd0ddf38 100644 --- a/development/vmagent.yml +++ b/development/vmagent.yml @@ -8,5 +8,5 @@ scrape_configs: - targets: - "database:2004" # - "memgraph-exporter:2004" # requires enterprise license - - "infrahub-server:8000/metrics" - - "infrahub-git:8000" + - "server:8000/metrics" + - "task-worker:8000" diff --git a/docker-compose.yml b/docker-compose.yml index cd8afa3169..09fc28786d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -126,7 +126,7 @@ services: retries: 3 database: - image: ${NEO4J_DOCKER_IMAGE:-neo4j:5.19.0-community} + image: ${NEO4J_DOCKER_IMAGE:-neo4j:5.20.0-community} restart: unless-stopped environment: NEO4J_AUTH: neo4j/${INFRAHUB_DB_PASSWORD:-admin} @@ -145,8 +145,40 @@ services: - 2004:2004 - 6362:6362 + task-manager: + image: "${TASK_MANAGER_DOCKER_IMAGE:-prefecthq/prefect:3.0.3-python3.12}" + command: prefect server start --host 0.0.0.0 --ui + restart: unless-stopped + environment: + PREFECT_API_DATABASE_CONNECTION_URL: postgresql+asyncpg://postgres:postgres@task-manager-db:5432/prefect + PREFECT_WORKER_QUERY_SECONDS: 3 + PREFECT_AGENT_QUERY_INTERVAL: 3 + healthcheck: + test: /usr/local/bin/httpx http://localhost:4200/api/health || exit 1 + interval: 5s + timeout: 5s + retries: 20 + start_period: 10s + depends_on: + - task-manager-db + + task-manager-db: + image: postgres:16-alpine + restart: unless-stopped + environment: + - POSTGRES_USER=postgres + - POSTGRES_PASSWORD=postgres + - POSTGRES_DB=prefect + volumes: + - workflow_db:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready"] + interval: 10s + timeout: 5s + retries: 5 + infrahub-server: - image: "registry.opsmill.io/opsmill/infrahub:${VERSION:-0.16.2}" + image: "${INFRAHUB_DOCKER_IMAGE:-registry.opsmill.io/opsmill/infrahub}:${VERSION:-0.16.2}" restart: unless-stopped command: > gunicorn --config backend/infrahub/serve/gunicorn_config.py @@ -185,7 +217,7 @@ services: deploy: mode: replicated replicas: 2 - image: "registry.opsmill.io/opsmill/infrahub:${VERSION:-0.16.2}" + image: "${INFRAHUB_DOCKER_IMAGE:-registry.opsmill.io/opsmill/infrahub}:${VERSION:-0.16.2}" command: infrahub git-agent start --debug restart: unless-stopped depends_on: @@ -213,3 +245,4 @@ volumes: git_data: git_remote_data: storage_data: + workflow_db: diff --git a/docs/docs/development/backend.mdx b/docs/docs/development/backend.mdx index 9ba0a09faf..f32b116fb2 100644 --- a/docs/docs/development/backend.mdx +++ b/docs/docs/development/backend.mdx @@ -33,7 +33,6 @@ export INFRAHUB_DB_TYPE=neo4j # Accepts neo4j or memgraph export INFRAHUB_INITIAL_ADMIN_TOKEN="${ADMIN_TOKEN}" # Random string which can be generated using: openssl rand -hex 16 export INFRAHUB_STORAGE_LOCAL_PATH="${HOME}/Development/infrahub-storage" export INFRAHUB_API_CORS_ALLOW_ORIGINS='["http://localhost:8080"]' # Allow frontend/backend communications without CORS issues -export INFRAHUB_IMAGE_VER=local # Force building a local image instead of pulling one from the registry ``` The exported environment variables are very important and must be set before moving to another step. Without these, you will likely face some errors or issues later. @@ -59,7 +58,14 @@ cd infrahub # or the directory of your choice poetry install ``` -Now it's time to bring the required services up, and for that we have demo commands: +Some tests require some services to work. By default, they are automatically started by pytest before tests run. +It is also possible to disable the automatic startup of services and to rely on existing services using an environment variable: + +```bash +export INFRAHUB_USE_TEST_CONTAINERS=false +``` + +The required services now need to be started using dedicated commands: ```shell invoke dev.destroy dev.deps @@ -72,6 +78,7 @@ poetry run invoke dev.status ``` This should yield a Docker output like the following: +docs ```shell NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS diff --git a/docs/docs/guides/installation.mdx b/docs/docs/guides/installation.mdx index 5b22ce085a..0e2b33d674 100644 --- a/docs/docs/guides/installation.mdx +++ b/docs/docs/guides/installation.mdx @@ -64,7 +64,7 @@ cd ~/source/infrahub/ Next, clone the Infrahub GitHub repository into the current directory. ```shell -git clone --recursive --depth 1 https://github.com/opsmill/infrahub.git +git clone --recursive -b stable --depth 1 https://github.com/opsmill/infrahub.git ``` :::note diff --git a/docs/docs/guides/jinja2-transform.mdx b/docs/docs/guides/jinja2-transform.mdx index a3a3938f18..3d38a0920f 100644 --- a/docs/docs/guides/jinja2-transform.mdx +++ b/docs/docs/guides/jinja2-transform.mdx @@ -162,6 +162,10 @@ jinja2_transforms: description: "short description" # (optional) query: "tags_query" # Name or ID of the GraphQLQuery template_path: "tags_tpl.j2" # Path to the main Jinja2 template + +queries: + - name: tags_query # Name of the GraphQLQuery + file_path: "tags_query.gql" # Path to the main Jinja2 template ``` > The main Jinja2 template can import other templates diff --git a/docs/docs/guides/repository.mdx b/docs/docs/guides/repository.mdx index dba099193e..f963da5a90 100644 --- a/docs/docs/guides/repository.mdx +++ b/docs/docs/guides/repository.mdx @@ -83,14 +83,14 @@ Using the GraphQL Interface, it is possible to add a `Repository` or `Read-only mutation { CorePasswordCredentialCreate( data: { - name: {value: "My Git Credential"}, + name: {value: "my-git-credential"}, username: {value: "MY_USERNAME"}, password: {value: "MY_TOKEN_OR_PASSWORD"} } ) { ok object { - id + hfid } } } @@ -108,7 +108,8 @@ Using the GraphQL Interface, it is possible to add a `Repository` or `Read-only data: { name: {value: "My Git Repository"}, location: {value: "https://GIT_SERVER/YOUR_GIT_USERNAME/YOUR_REPOSITORY_NAME.git"}, - # credential: {id: "CREDENTIAL_ID_FROM_PREVIOUS_REQUEST"} <-- optional: This needs to be the credential id created at step 2 + # The HFID returned during step 2 will be the name of the credentials + credential: {hfid: "my-git-credential"} } ) { ok @@ -131,7 +132,8 @@ Using the GraphQL Interface, it is possible to add a `Repository` or `Read-only name: {value: "My Git Repository"}, location: {value: "https://GIT_SERVER/YOUR_GIT_USERNAME/YOUR_REPOSITORY_NAME.git"}, ref: { value: "BRANCH/TAG/COMMIT_TO_TRACK" }, - # credential: {id: "CREDENTIAL_ID_FROM_PREVIOUS_REQUEST"} <-- optional: This needs to be the credential id created at step 2 + # Optional: the HFID returned during step 2 will be the name of the credentials + credential: {hfid: "my-git-credential"} } ) { ok @@ -154,7 +156,7 @@ The repository should be visible under [Unified Storage / Repository](http://loc ### Via the Infrahub SDK 1. Install and setup the [Infrahub SDK](/python-sdk) -2. If needed, i.e., your repository is private, create a Credential object to hold your username / password. +2. If needed, i.e., your repository is private, create a Credential object to hold your username / password. We highly recommend to provide credentials for Repository to allow branch creation from Infrahub. ```python # Create credential object ... @@ -180,7 +182,8 @@ The repository should be visible under [Unified Storage / Repository](http://loc "CoreRepository", name="My Git repository", location="https://GIT_SERVER/YOUR_GIT_USERNAME/YOUR_REPOSITORY_NAME.git", - # credential=credential, <-- optional: This needs to be the credential object created at step 2 + # The credential object created at step 2 + credential=credential, ) # and save it ... @@ -193,11 +196,12 @@ The repository should be visible under [Unified Storage / Repository](http://loc ```python # Create repository object ... repository = client.create( - "CoreRepository", + "CoreReadOnlyRepository", name="My Git repository", location="https://GIT_SERVER/YOUR_GIT_USERNAME/YOUR_REPOSITORY_NAME.git", ref="BRANCH/TAG/COMMIT_TO_TRACK", - # credential=credential, <-- optional: This needs to be the credential object created at step 2 + # Optional: the credential object created at step 2 + credential=credential, ) # and save it ... diff --git a/docs/docs/guides/sso.mdx b/docs/docs/guides/sso.mdx new file mode 100644 index 0000000000..e48cfde6a0 --- /dev/null +++ b/docs/docs/guides/sso.mdx @@ -0,0 +1,139 @@ +--- +title: Configuring Single sign-on +--- +# Configuring Single sign-on + +In Infrahub you can configure SSO using either Open ID Connect (OIDC) or can use OAuth2. + +The SSO system in Infrahub allows for the configuration of one or more identity providers. While most organizations will only use one provider a reason to have two could be that the providers manage different security domains where one of them might be for regular users the other identity provider could be for administrative accounts. +Infrahub supports three different OIDC providers: + +* PROVIDER1 +* PROVIDER2 +* GOOGLE + +All of them work in the same way the main difference is that the one for Google includes some predefined settings that limit the amount of configuration you have to do yourself. + +When configuring Infrahub, setting up OAuth2 or OIDC is fairly similar, though there are some slight differences with regards to the settings you need to have in place. Both options are provided below. + +## Setting up OAuth2 in Infrahub + +In this case we are going to focus on PROVIDER1 which allows you to connect Infrahub to your first OAuth2 provider. Configuring the first provider uses environment variables with the `INFRAHUB_OAUTH2_PROVIDER1_` prefix, the others follow suite so it would be `INFRAHUB_OAUTH2_PROVIDER2_` and `INFRAHUB_OAUTH2_GOOGLE_`. + +| Variable | Type | Description | Mandatory | +| ---- | ---- | ----------- | --------- | +| INFRAHUB_OAUTH2_PROVIDER1_CLIENT_ID | `Text` | The client ID from the IDP | `true` | +| INFRAHUB_OAUTH2_PROVIDER1_CLIENT_SECRET | `Text` | The client secret from the IDP | `true` | +| INFRAHUB_OAUTH2_PROVIDER1_AUTHORIZATION_URL | `Url` | The authorization URL on the IDP | `true` | +| INFRAHUB_OAUTH2_PROVIDER1_TOKEN_URL | `Url` | The token URL on the IDP | `true` | +| INFRAHUB_OAUTH2_PROVIDER1_USERINFO_URL | `Url` | The userinfo URL on the IDP | `true` | +| INFRAHUB_OAUTH2_PROVIDER1_SCOPES | `Array[Text]` | The scopes to request from the IDP | `false` | +| INFRAHUB_OAUTH2_PROVIDER1_DISPLAY_LABEL | `Text` | Display label for the provider on the login screen | `false` | +| INFRAHUB_OAUTH2_PROVIDER1_ICON | `Text` | MDI icon to display on the login screen (ex: mdi:key) | `false` | + +:::note + +A difference between this provider and one for Google is that the Google provider only requires `client_id` and `client_secret` to be set, other than that they are currently identical. + +::: + +Aside from the display label and icon all the other entries will be provided by your OAuth2 provider. + +An example of what the configuration could look like: + +```bash +export INFRAHUB_OAUTH2_PROVIDER1_CLIENT_ID=infrahub-sso +export INFRAHUB_OAUTH2_PROVIDER1_CLIENT_SECRET=edPf4IaquQaqns7t3s95mLhKKYdwL1up +export INFRAHUB_OAUTH2_PROVIDER1_AUTHORIZATION_URL=http://localhost:8180/realms/infrahub/protocol/openid-connect/auth +export INFRAHUB_OAUTH2_PROVIDER1_TOKEN_URL=http://localhost:8180/realms/infrahub/protocol/openid-connect/token +export INFRAHUB_OAUTH2_PROVIDER1_USERINFO_URL=http://localhost:8180/realms/infrahub/protocol/openid-connect/userinfo +export INFRAHUB_OAUTH2_PROVIDER1_DISPLAY_LABEL="Internal Server (Keycloak)" +export INFRAHUB_OAUTH2_PROVIDER1_ICON="mdi:security-lock-outline" +``` + +This could be the configuration of a Keycloak provider, please refer to the documentation of your intended provider for guides on how to create a client and access the required information. + +## Activating the OAuth2 provider + +In order to activate the above provider we need to add it to the list of active OAuth2 providers. + +```bash +export INFRAHUB_SECURITY_OAUTH2_PROVIDERS='["provider1"]' +``` + +Alternatively if you are setting up multiple providers each with their different settings: + +```bash +export INFRAHUB_SECURITY_OAUTH2_PROVIDERS='["provider1","provider2"]' +``` + +## Setting up OIDC in Infrahub + +In this case we are going to focus on PROVIDER1 which allows you to connect Infrahub to your first OIDC provider. Configuring the first provider uses environment variables with the `INFRAHUB_OIDC_PROVIDER1_` prefix, the others follow suite so it would be `INFRAHUB_OIDC_PROVIDER2_` and `INFRAHUB_OIDC_GOOGLE_`. + +| Variable | Type | Description | Mandatory | +| ---- | ---- | ----------- | --------- | +| INFRAHUB_OIDC_PROVIDER1_CLIENT_ID | `Text` | The client ID from the IDP | `true` | +| INFRAHUB_OIDC_PROVIDER1_CLIENT_SECRET | `Text` | The client secret from the IDP | `true` | +| INFRAHUB_OIDC_PROVIDER1_DISCOVERY_URL | `Url` | The discovery URL on the IDP | `true` | +| INFRAHUB_OAUTH2_PROVIDER1_SCOPES | `Array[Text]` | The scopes to request from the IDP | `false` | +| INFRAHUB_OAUTH2_PROVIDER1_DISPLAY_LABEL | `Text` | Display label for the provider on the login screen | `false` | +| INFRAHUB_OAUTH2_PROVIDER1_ICON | `Text` | MDI icon to display on the login screen (ex: mdi:key) | `false` | + +:::note + +A difference between this provider and one for Google is that the Google provider only requires `client_id` and `client_secret` to be set, other than that they are currently identical. + +::: + +Aside from the display label and icon all the other entries will be provided by from your OIDC provider. + +An example of what the configuration could look like: + +```bash +export INFRAHUB_OIDC_PROVIDER1_CLIENT_ID=infrahub-sso +export INFRAHUB_OIDC_PROVIDER1_CLIENT_SECRET=edPf4IaquQaqns7t3s95mLhKKYdwL1up +export INFRAHUB_OIDC_PROVIDER1_DISCOVERY_URL=http://localhost:8180/realms/infrahub/.well-known/openid-configuration +export INFRAHUB_OIDC_PROVIDER1_DISPLAY_LABEL="Internal Server (Keycloak)" +export INFRAHUB_OIDC_PROVIDER1_ICON="mdi:security-lock-outline" +``` + +This could be the configuration of a Keycloak provider, please refer to the documentation of your intended provider for guides on how to create a client and access the required information. + +## Activating the OIDC provider + +In order to activate the above provider we need to add it to the list of active OIDC providers. + +```bash +export INFRAHUB_SECURITY_OIDC_PROVIDERS='["provider1"]' +``` + +Alternatively if you are setting up multiple providers each with their different settings: + +```bash +export INFRAHUB_SECURITY_OIDC_PROVIDERS='["provider1","provider2"]' +``` + +## On configuring the redirect URI + +Within your identity provider when configuring the client you will need to configure a redirect URI that defines an allowed URI. The convention used for Infrahub is that it should point back to the Infrahub host on `/auth/{protocol}/{provider_name}/callback`. + +If we were to setup the above provider on a server called infrahub.example.com to use OIDC the redirect URI would be: + +* `https://infrahub.example.com/auth/oidc/provider1/callback` + +If we instead use OAuth2 the redirect URI would be: + +* `https://infrahub.example.com/auth/oauth2/provider1/callback` + +:::note + +If you get the redirect URI incorrect this will typically be displayed as an error message on the IDP after Infrahub has redirected the session there. + +::: + +## Mapping users to groups within Infrahub + +After signing in Infrahub will try to collect the groups that the user is member of. The current requirement around this is that the identity provider has to return this information as a list of strings within a "groups" field in the payload returned from the `USERINFO_URL`. This is not something that is supported using the Google provider today but should be configurable in other identity providers such as Keycloak. + +For any group that is returned by the IDP provider Infrahub will add the user to that group provided that the group in question exists within Infrahub. I.e. Infrahub will *not* create the groups. diff --git a/docs/docs/infrahubctl/infrahubctl-protocols.mdx b/docs/docs/infrahubctl/infrahubctl-protocols.mdx index ea2b8d0778..dd621f2141 100644 --- a/docs/docs/infrahubctl/infrahubctl-protocols.mdx +++ b/docs/docs/infrahubctl/infrahubctl-protocols.mdx @@ -10,7 +10,9 @@ $ infrahubctl protocols [OPTIONS] **Options**: +* `--schemas PATH`: List of schemas or directory to load. * `--branch TEXT`: Branch of schema to export Python protocols for. +* `--sync / --no-sync`: Generate for sync or async. [default: no-sync] * `--config-file TEXT`: [env var: INFRAHUBCTL_CONFIG; default: infrahubctl.toml] * `--out TEXT`: Path to a file to save the result. [default: schema_protocols.py] * `--install-completion`: Install completion for the current shell. diff --git a/docs/docs/reference/configuration.mdx b/docs/docs/reference/configuration.mdx index d5b5e660e3..f3556c1ad9 100644 --- a/docs/docs/reference/configuration.mdx +++ b/docs/docs/reference/configuration.mdx @@ -30,7 +30,7 @@ Here are a few common methods of setting environmental variables: | AWS_ACCESS_KEY_ID | | | | | | AWS_SECRET_ACCESS_KEY | | | | | | DB_TYPE | | | | | -| INFRAHUB_ADDRESS | "HTTP endpoint of the API Server, used by the git agent for internal communication" | http://infrahub-server:8000 | | | +| INFRAHUB_ADDRESS | "HTTP endpoint of the API Server, used by the git agent for internal communication" | http://server:8000 | | | | INFRAHUB_ALLOW_ANONYMOUS_ACCESS | Indicates if the system allows anonymous read access | TRUE | | | | INFRAHUB_ANALYTICS_ADDRESS | | | | | | INFRAHUB_ANALYTICS_API_KEY | | | | | diff --git a/docs/docs/topics/repository.mdx b/docs/docs/topics/repository.mdx index 58a2dbae2d..f8c3322234 100644 --- a/docs/docs/topics/repository.mdx +++ b/docs/docs/topics/repository.mdx @@ -117,3 +117,16 @@ Sync status keeps track of the synchronisation operation's output. Sync operation is ongoing. + +## Repository actions + +From the repository detailed view you can open a "More" menu containing advanced actions to interact with the repository. + + + + Infrahub will try to reach the repository and authenticate. + + + Infrahub will fetch files on the remote repository with the existing commit identifier. This action won't change the reference on Infrahub side (for example pull the latest commit). This might be useful to force an import after having fixed data on Infrahub side. + + diff --git a/docs/package-lock.json b/docs/package-lock.json index d7f990e744..4195e12381 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -8,25 +8,25 @@ "name": "infrahub-docs", "version": "0.0.0", "dependencies": { - "@docusaurus/core": "^3.2.1", - "@docusaurus/preset-classic": "^3.2.1", - "@easyops-cn/docusaurus-search-local": "^0.40.1", - "@mdx-js/react": "^3.0.0", - "clsx": "^2.0.0", - "prism-react-renderer": "^2.3.0", + "@docusaurus/core": "^3.5.2", + "@docusaurus/preset-classic": "^3.5.2", + "@easyops-cn/docusaurus-search-local": "^0.44.5", + "@mdx-js/react": "^3.0.1", + "clsx": "^2.1.1", + "prism-react-renderer": "^2.4.0", "raw-loader": "^4.0.2", - "react": "^18.0.0", - "react-dom": "^18.0.0" + "react": "^18.3.1", + "react-dom": "^18.3.1" }, "devDependencies": { - "@docusaurus/eslint-plugin": "^3.2.1", - "@docusaurus/module-type-aliases": "^3.2.1", - "@docusaurus/tsconfig": "^3.2.1", - "@docusaurus/types": "^3.2.1", + "@docusaurus/eslint-plugin": "^3.5.2", + "@docusaurus/module-type-aliases": "^3.5.2", + "@docusaurus/tsconfig": "^3.5.2", + "@docusaurus/types": "^3.5.2", "@typescript-eslint/eslint-plugin": "^6.20.0", "@typescript-eslint/parser": "^6.20.0", "eslint": "^8.56.0", - "typescript": "~5.2.2" + "typescript": "~5.6.2" }, "engines": { "node": ">=18.0" @@ -45,6 +45,7 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/@algolia/autocomplete-core/-/autocomplete-core-1.9.3.tgz", "integrity": "sha512-009HdfugtGCdC4JdXUbVJClA0q0zh24yyePn+KUGk3rP7j8FEe/m5Yo/z65gn6nP/cM39PxpzqKrL7A6fP6PPw==", + "license": "MIT", "dependencies": { "@algolia/autocomplete-plugin-algolia-insights": "1.9.3", "@algolia/autocomplete-shared": "1.9.3" @@ -54,6 +55,7 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/@algolia/autocomplete-plugin-algolia-insights/-/autocomplete-plugin-algolia-insights-1.9.3.tgz", "integrity": "sha512-a/yTUkcO/Vyy+JffmAnTWbr4/90cLzw+CC3bRbhnULr/EM0fGNvM13oQQ14f2moLMcVDyAx/leczLlAOovhSZg==", + "license": "MIT", "dependencies": { "@algolia/autocomplete-shared": "1.9.3" }, @@ -65,6 +67,7 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.9.3.tgz", "integrity": "sha512-d4qlt6YmrLMYy95n5TB52wtNDr6EgAIPH81dvvvW8UmuWRgxEtY0NJiPwl/h95JtG2vmRM804M0DSwMCNZlzRA==", + "license": "MIT", "dependencies": { "@algolia/autocomplete-shared": "1.9.3" }, @@ -77,147 +80,281 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/@algolia/autocomplete-shared/-/autocomplete-shared-1.9.3.tgz", "integrity": "sha512-Wnm9E4Ye6Rl6sTTqjoymD+l8DjSTHsHboVRYrKgEt8Q7UHm9nYbqhN/i0fhUYA3OAEH7WA8x3jfpnmJm3rKvaQ==", + "license": "MIT", "peerDependencies": { "@algolia/client-search": ">= 4.9.1 < 6", "algoliasearch": ">= 4.9.1 < 6" } }, "node_modules/@algolia/cache-browser-local-storage": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.23.3.tgz", - "integrity": "sha512-vRHXYCpPlTDE7i6UOy2xE03zHF2C8MEFjPN2v7fRbqVpcOvAUQK81x3Kc21xyb5aSIpYCjWCZbYZuz8Glyzyyg==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.24.0.tgz", + "integrity": "sha512-t63W9BnoXVrGy9iYHBgObNXqYXM3tYXCjDSHeNwnsc324r4o5UiVKUiAB4THQ5z9U5hTj6qUvwg/Ez43ZD85ww==", + "license": "MIT", "dependencies": { - "@algolia/cache-common": "4.23.3" + "@algolia/cache-common": "4.24.0" } }, "node_modules/@algolia/cache-common": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.23.3.tgz", - "integrity": "sha512-h9XcNI6lxYStaw32pHpB1TMm0RuxphF+Ik4o7tcQiodEdpKK+wKufY6QXtba7t3k8eseirEMVB83uFFF3Nu54A==" + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@algolia/cache-common/-/cache-common-4.24.0.tgz", + "integrity": "sha512-emi+v+DmVLpMGhp0V9q9h5CdkURsNmFC+cOS6uK9ndeJm9J4TiqSvPYVu+THUP8P/S08rxf5x2P+p3CfID0Y4g==", + "license": "MIT" }, "node_modules/@algolia/cache-in-memory": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.23.3.tgz", - "integrity": "sha512-yvpbuUXg/+0rbcagxNT7un0eo3czx2Uf0y4eiR4z4SD7SiptwYTpbuS0IHxcLHG3lq22ukx1T6Kjtk/rT+mqNg==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@algolia/cache-in-memory/-/cache-in-memory-4.24.0.tgz", + "integrity": "sha512-gDrt2so19jW26jY3/MkFg5mEypFIPbPoXsQGQWAi6TrCPsNOSEYepBMPlucqWigsmEy/prp5ug2jy/N3PVG/8w==", + "license": "MIT", "dependencies": { - "@algolia/cache-common": "4.23.3" + "@algolia/cache-common": "4.24.0" } }, "node_modules/@algolia/client-account": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.23.3.tgz", - "integrity": "sha512-hpa6S5d7iQmretHHF40QGq6hz0anWEHGlULcTIT9tbUssWUriN9AUXIFQ8Ei4w9azD0hc1rUok9/DeQQobhQMA==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@algolia/client-account/-/client-account-4.24.0.tgz", + "integrity": "sha512-adcvyJ3KjPZFDybxlqnf+5KgxJtBjwTPTeyG2aOyoJvx0Y8dUQAEOEVOJ/GBxX0WWNbmaSrhDURMhc+QeevDsA==", + "license": "MIT", "dependencies": { - "@algolia/client-common": "4.23.3", - "@algolia/client-search": "4.23.3", - "@algolia/transporter": "4.23.3" + "@algolia/client-common": "4.24.0", + "@algolia/client-search": "4.24.0", + "@algolia/transporter": "4.24.0" + } + }, + "node_modules/@algolia/client-account/node_modules/@algolia/client-common": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.24.0.tgz", + "integrity": "sha512-bc2ROsNL6w6rqpl5jj/UywlIYC21TwSSoFHKl01lYirGMW+9Eek6r02Tocg4gZ8HAw3iBvu6XQiM3BEbmEMoiA==", + "license": "MIT", + "dependencies": { + "@algolia/requester-common": "4.24.0", + "@algolia/transporter": "4.24.0" + } + }, + "node_modules/@algolia/client-account/node_modules/@algolia/client-search": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.24.0.tgz", + "integrity": "sha512-uRW6EpNapmLAD0mW47OXqTP8eiIx5F6qN9/x/7HHO6owL3N1IXqydGwW5nhDFBrV+ldouro2W1VX3XlcUXEFCA==", + "license": "MIT", + "dependencies": { + "@algolia/client-common": "4.24.0", + "@algolia/requester-common": "4.24.0", + "@algolia/transporter": "4.24.0" } }, "node_modules/@algolia/client-analytics": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.23.3.tgz", - "integrity": "sha512-LBsEARGS9cj8VkTAVEZphjxTjMVCci+zIIiRhpFun9jGDUlS1XmhCW7CTrnaWeIuCQS/2iPyRqSy1nXPjcBLRA==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-4.24.0.tgz", + "integrity": "sha512-y8jOZt1OjwWU4N2qr8G4AxXAzaa8DBvyHTWlHzX/7Me1LX8OayfgHexqrsL4vSBcoMmVw2XnVW9MhL+Y2ZDJXg==", + "license": "MIT", "dependencies": { - "@algolia/client-common": "4.23.3", - "@algolia/client-search": "4.23.3", - "@algolia/requester-common": "4.23.3", - "@algolia/transporter": "4.23.3" + "@algolia/client-common": "4.24.0", + "@algolia/client-search": "4.24.0", + "@algolia/requester-common": "4.24.0", + "@algolia/transporter": "4.24.0" } }, - "node_modules/@algolia/client-common": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.23.3.tgz", - "integrity": "sha512-l6EiPxdAlg8CYhroqS5ybfIczsGUIAC47slLPOMDeKSVXYG1n0qGiz4RjAHLw2aD0xzh2EXZ7aRguPfz7UKDKw==", + "node_modules/@algolia/client-analytics/node_modules/@algolia/client-common": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.24.0.tgz", + "integrity": "sha512-bc2ROsNL6w6rqpl5jj/UywlIYC21TwSSoFHKl01lYirGMW+9Eek6r02Tocg4gZ8HAw3iBvu6XQiM3BEbmEMoiA==", + "license": "MIT", + "dependencies": { + "@algolia/requester-common": "4.24.0", + "@algolia/transporter": "4.24.0" + } + }, + "node_modules/@algolia/client-analytics/node_modules/@algolia/client-search": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.24.0.tgz", + "integrity": "sha512-uRW6EpNapmLAD0mW47OXqTP8eiIx5F6qN9/x/7HHO6owL3N1IXqydGwW5nhDFBrV+ldouro2W1VX3XlcUXEFCA==", + "license": "MIT", "dependencies": { - "@algolia/requester-common": "4.23.3", - "@algolia/transporter": "4.23.3" + "@algolia/client-common": "4.24.0", + "@algolia/requester-common": "4.24.0", + "@algolia/transporter": "4.24.0" + } + }, + "node_modules/@algolia/client-common": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.6.1.tgz", + "integrity": "sha512-4MGqXqiAyqsUJw+KamKWZO2Gxn9iMpc05vC0vy8+iQRjKRZEDB1a+3Da6CnkWzXa162pJb7a/chDAAKA9rye8A==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 14.0.0" } }, "node_modules/@algolia/client-personalization": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.23.3.tgz", - "integrity": "sha512-3E3yF3Ocr1tB/xOZiuC3doHQBQ2zu2MPTYZ0d4lpfWads2WTKG7ZzmGnsHmm63RflvDeLK/UVx7j2b3QuwKQ2g==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-4.24.0.tgz", + "integrity": "sha512-l5FRFm/yngztweU0HdUzz1rC4yoWCFo3IF+dVIVTfEPg906eZg5BOd1k0K6rZx5JzyyoP4LdmOikfkfGsKVE9w==", + "license": "MIT", "dependencies": { - "@algolia/client-common": "4.23.3", - "@algolia/requester-common": "4.23.3", - "@algolia/transporter": "4.23.3" + "@algolia/client-common": "4.24.0", + "@algolia/requester-common": "4.24.0", + "@algolia/transporter": "4.24.0" + } + }, + "node_modules/@algolia/client-personalization/node_modules/@algolia/client-common": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.24.0.tgz", + "integrity": "sha512-bc2ROsNL6w6rqpl5jj/UywlIYC21TwSSoFHKl01lYirGMW+9Eek6r02Tocg4gZ8HAw3iBvu6XQiM3BEbmEMoiA==", + "license": "MIT", + "dependencies": { + "@algolia/requester-common": "4.24.0", + "@algolia/transporter": "4.24.0" } }, "node_modules/@algolia/client-search": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.23.3.tgz", - "integrity": "sha512-P4VAKFHqU0wx9O+q29Q8YVuaowaZ5EM77rxfmGnkHUJggh28useXQdopokgwMeYw2XUht49WX5RcTQ40rZIabw==", + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.6.1.tgz", + "integrity": "sha512-HloeR0Ef29vf2yJc1lhjw1OYial3YgB0f3TQaqqMlSnM/IkAw9TnX1IOYLurnI91apMKggFpA9t8lRp7TGEKEg==", + "license": "MIT", + "peer": true, "dependencies": { - "@algolia/client-common": "4.23.3", - "@algolia/requester-common": "4.23.3", - "@algolia/transporter": "4.23.3" + "@algolia/client-common": "5.6.1", + "@algolia/requester-browser-xhr": "5.6.1", + "@algolia/requester-fetch": "5.6.1", + "@algolia/requester-node-http": "5.6.1" + }, + "engines": { + "node": ">= 14.0.0" } }, "node_modules/@algolia/events": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@algolia/events/-/events-4.0.1.tgz", - "integrity": "sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ==" + "integrity": "sha512-FQzvOCgoFXAbf5Y6mYozw2aj5KCJoA3m4heImceldzPSMbdyS4atVjJzXKMsfX3wnZTFYwkkt8/z8UesLHlSBQ==", + "license": "MIT" }, "node_modules/@algolia/logger-common": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.23.3.tgz", - "integrity": "sha512-y9kBtmJwiZ9ZZ+1Ek66P0M68mHQzKRxkW5kAAXYN/rdzgDN0d2COsViEFufxJ0pb45K4FRcfC7+33YB4BLrZ+g==" + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@algolia/logger-common/-/logger-common-4.24.0.tgz", + "integrity": "sha512-LLUNjkahj9KtKYrQhFKCzMx0BY3RnNP4FEtO+sBybCjJ73E8jNdaKJ/Dd8A/VA4imVHP5tADZ8pn5B8Ga/wTMA==", + "license": "MIT" }, "node_modules/@algolia/logger-console": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.23.3.tgz", - "integrity": "sha512-8xoiseoWDKuCVnWP8jHthgaeobDLolh00KJAdMe9XPrWPuf1by732jSpgy2BlsLTaT9m32pHI8CRfrOqQzHv3A==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@algolia/logger-console/-/logger-console-4.24.0.tgz", + "integrity": "sha512-X4C8IoHgHfiUROfoRCV+lzSy+LHMgkoEEU1BbKcsfnV0i0S20zyy0NLww9dwVHUWNfPPxdMU+/wKmLGYf96yTg==", + "license": "MIT", "dependencies": { - "@algolia/logger-common": "4.23.3" + "@algolia/logger-common": "4.24.0" } }, "node_modules/@algolia/recommend": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-4.23.3.tgz", - "integrity": "sha512-9fK4nXZF0bFkdcLBRDexsnGzVmu4TSYZqxdpgBW2tEyfuSSY54D4qSRkLmNkrrz4YFvdh2GM1gA8vSsnZPR73w==", - "dependencies": { - "@algolia/cache-browser-local-storage": "4.23.3", - "@algolia/cache-common": "4.23.3", - "@algolia/cache-in-memory": "4.23.3", - "@algolia/client-common": "4.23.3", - "@algolia/client-search": "4.23.3", - "@algolia/logger-common": "4.23.3", - "@algolia/logger-console": "4.23.3", - "@algolia/requester-browser-xhr": "4.23.3", - "@algolia/requester-common": "4.23.3", - "@algolia/requester-node-http": "4.23.3", - "@algolia/transporter": "4.23.3" + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-4.24.0.tgz", + "integrity": "sha512-P9kcgerfVBpfYHDfVZDvvdJv0lEoCvzNlOy2nykyt5bK8TyieYyiD0lguIJdRZZYGre03WIAFf14pgE+V+IBlw==", + "license": "MIT", + "dependencies": { + "@algolia/cache-browser-local-storage": "4.24.0", + "@algolia/cache-common": "4.24.0", + "@algolia/cache-in-memory": "4.24.0", + "@algolia/client-common": "4.24.0", + "@algolia/client-search": "4.24.0", + "@algolia/logger-common": "4.24.0", + "@algolia/logger-console": "4.24.0", + "@algolia/requester-browser-xhr": "4.24.0", + "@algolia/requester-common": "4.24.0", + "@algolia/requester-node-http": "4.24.0", + "@algolia/transporter": "4.24.0" + } + }, + "node_modules/@algolia/recommend/node_modules/@algolia/client-common": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.24.0.tgz", + "integrity": "sha512-bc2ROsNL6w6rqpl5jj/UywlIYC21TwSSoFHKl01lYirGMW+9Eek6r02Tocg4gZ8HAw3iBvu6XQiM3BEbmEMoiA==", + "license": "MIT", + "dependencies": { + "@algolia/requester-common": "4.24.0", + "@algolia/transporter": "4.24.0" + } + }, + "node_modules/@algolia/recommend/node_modules/@algolia/client-search": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.24.0.tgz", + "integrity": "sha512-uRW6EpNapmLAD0mW47OXqTP8eiIx5F6qN9/x/7HHO6owL3N1IXqydGwW5nhDFBrV+ldouro2W1VX3XlcUXEFCA==", + "license": "MIT", + "dependencies": { + "@algolia/client-common": "4.24.0", + "@algolia/requester-common": "4.24.0", + "@algolia/transporter": "4.24.0" + } + }, + "node_modules/@algolia/recommend/node_modules/@algolia/requester-browser-xhr": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.24.0.tgz", + "integrity": "sha512-Z2NxZMb6+nVXSjF13YpjYTdvV3032YTBSGm2vnYvYPA6mMxzM3v5rsCiSspndn9rzIW4Qp1lPHBvuoKJV6jnAA==", + "license": "MIT", + "dependencies": { + "@algolia/requester-common": "4.24.0" + } + }, + "node_modules/@algolia/recommend/node_modules/@algolia/requester-node-http": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.24.0.tgz", + "integrity": "sha512-JF18yTjNOVYvU/L3UosRcvbPMGT9B+/GQWNWnenIImglzNVGpyzChkXLnrSf6uxwVNO6ESGu6oN8MqcGQcjQJw==", + "license": "MIT", + "dependencies": { + "@algolia/requester-common": "4.24.0" } }, "node_modules/@algolia/requester-browser-xhr": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.23.3.tgz", - "integrity": "sha512-jDWGIQ96BhXbmONAQsasIpTYWslyjkiGu0Quydjlowe+ciqySpiDUrJHERIRfELE5+wFc7hc1Q5hqjGoV7yghw==", + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.6.1.tgz", + "integrity": "sha512-tY1RW60sGF9sMpxbd8j53IqLLwnkNhrAarVhFfNZzDZNvI8WyzG78W5ZD/SFvtkgNPPSav3T/3LpBT8xBpzbGw==", + "license": "MIT", + "peer": true, "dependencies": { - "@algolia/requester-common": "4.23.3" + "@algolia/client-common": "5.6.1" + }, + "engines": { + "node": ">= 14.0.0" } }, "node_modules/@algolia/requester-common": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.23.3.tgz", - "integrity": "sha512-xloIdr/bedtYEGcXCiF2muajyvRhwop4cMZo+K2qzNht0CMzlRkm8YsDdj5IaBhshqfgmBb3rTg4sL4/PpvLYw==" + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-common/-/requester-common-4.24.0.tgz", + "integrity": "sha512-k3CXJ2OVnvgE3HMwcojpvY6d9kgKMPRxs/kVohrwF5WMr2fnqojnycZkxPoEg+bXm8fi5BBfFmOqgYztRtHsQA==", + "license": "MIT" + }, + "node_modules/@algolia/requester-fetch": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.6.1.tgz", + "integrity": "sha512-4TvR5IodrH+o+ji4ka+VBufWY0GfHr43nFqnDTStabtjspfo4rlcV16x534vvnbfp694oBxrz0SO/Ny8VemvXg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@algolia/client-common": "5.6.1" + }, + "engines": { + "node": ">= 14.0.0" + } }, "node_modules/@algolia/requester-node-http": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.23.3.tgz", - "integrity": "sha512-zgu++8Uj03IWDEJM3fuNl34s746JnZOWn1Uz5taV1dFyJhVM/kTNw9Ik7YJWiUNHJQXcaD8IXD1eCb0nq/aByA==", + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.6.1.tgz", + "integrity": "sha512-K7tlss87aq6UnWnU8+fPIe+Is9Mvyqwzysp6Ty/HpQ7YNKUU7opgkMOVKxzTwt3fm40NfNX4ENvVKHoYABL6vw==", + "license": "MIT", + "peer": true, "dependencies": { - "@algolia/requester-common": "4.23.3" + "@algolia/client-common": "5.6.1" + }, + "engines": { + "node": ">= 14.0.0" } }, "node_modules/@algolia/transporter": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.23.3.tgz", - "integrity": "sha512-Wjl5gttqnf/gQKJA+dafnD0Y6Yw97yvfY8R9h0dQltX1GXTgNs1zWgvtWW0tHl1EgMdhAyw189uWiZMnL3QebQ==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@algolia/transporter/-/transporter-4.24.0.tgz", + "integrity": "sha512-86nI7w6NzWxd1Zp9q3413dRshDqAzSbsQjhcDhPIatEFiZrL1/TjnHL8S7jVKFePlIMzDsZWXAXwXzcok9c5oA==", + "license": "MIT", "dependencies": { - "@algolia/cache-common": "4.23.3", - "@algolia/logger-common": "4.23.3", - "@algolia/requester-common": "4.23.3" + "@algolia/cache-common": "4.24.0", + "@algolia/logger-common": "4.24.0", + "@algolia/requester-common": "4.24.0" } }, "node_modules/@ampproject/remapping": { @@ -564,9 +701,10 @@ } }, "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==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz", + "integrity": "sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==", + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -1641,11 +1779,12 @@ } }, "node_modules/@babel/plugin-transform-react-constant-elements": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.23.3.tgz", - "integrity": "sha512-zP0QKq/p6O42OL94udMgSfKXyse4RyJ0JqbQ34zDAONWjyrEsghYEyTSK5FIpmXmCpB55SHokL1cRRKHv8L2Qw==", + "version": "7.25.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.25.1.tgz", + "integrity": "sha512-SLV/giH/V4SmloZ6Dt40HjTGTAIkxn33TVIHxNGNvo8ezMhrxBkzisj4op1KZYPIOHFLqhv60OHvX+YRu4xbmQ==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -2161,18 +2300,20 @@ } }, "node_modules/@docsearch/css": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.6.0.tgz", - "integrity": "sha512-+sbxb71sWre+PwDK7X2T8+bhS6clcVMLwBPznX45Qu6opJcgRjAp7gYSDzVFp187J+feSj5dNBN1mJoi6ckkUQ==" + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/@docsearch/css/-/css-3.6.2.tgz", + "integrity": "sha512-vKNZepO2j7MrYBTZIGXvlUOIR+v9KRf70FApRgovWrj3GTs1EITz/Xb0AOlm1xsQBp16clVZj1SY/qaOJbQtZw==", + "license": "MIT" }, "node_modules/@docsearch/react": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.6.0.tgz", - "integrity": "sha512-HUFut4ztcVNmqy9gp/wxNbC7pTOHhgVVkHVGCACTuLhUKUhKAF9KYHJtMiLUJxEqiFLQiuri1fWF8zqwM/cu1w==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/@docsearch/react/-/react-3.6.2.tgz", + "integrity": "sha512-rtZce46OOkVflCQH71IdbXSFK+S8iJZlUF56XBW5rIgx/eG5qoomC7Ag3anZson1bBac/JFQn7XOBfved/IMRA==", + "license": "MIT", "dependencies": { "@algolia/autocomplete-core": "1.9.3", "@algolia/autocomplete-preset-algolia": "1.9.3", - "@docsearch/css": "3.6.0", + "@docsearch/css": "3.6.2", "algoliasearch": "^4.19.1" }, "peerDependencies": { @@ -2197,9 +2338,10 @@ } }, "node_modules/@docusaurus/core": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-3.2.1.tgz", - "integrity": "sha512-ZeMAqNvy0eBv2dThEeMuNzzuu+4thqMQakhxsgT5s02A8LqRcdkg+rbcnuNqUIpekQ4GRx3+M5nj0ODJhBXo9w==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/core/-/core-3.5.2.tgz", + "integrity": "sha512-4Z1WkhCSkX4KO0Fw5m/Vuc7Q3NxBG53NE5u59Rs96fWkMPZVSrzEPP16/Nk6cWb/shK7xXPndTmalJtw7twL/w==", + "license": "MIT", "dependencies": { "@babel/core": "^7.23.3", "@babel/generator": "^7.23.3", @@ -2211,14 +2353,12 @@ "@babel/runtime": "^7.22.6", "@babel/runtime-corejs3": "^7.22.6", "@babel/traverse": "^7.22.8", - "@docusaurus/cssnano-preset": "3.2.1", - "@docusaurus/logger": "3.2.1", - "@docusaurus/mdx-loader": "3.2.1", - "@docusaurus/react-loadable": "5.5.2", - "@docusaurus/utils": "3.2.1", - "@docusaurus/utils-common": "3.2.1", - "@docusaurus/utils-validation": "3.2.1", - "@svgr/webpack": "^6.5.1", + "@docusaurus/cssnano-preset": "3.5.2", + "@docusaurus/logger": "3.5.2", + "@docusaurus/mdx-loader": "3.5.2", + "@docusaurus/utils": "3.5.2", + "@docusaurus/utils-common": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", "autoprefixer": "^10.4.14", "babel-loader": "^9.1.3", "babel-plugin-dynamic-import-node": "^2.3.3", @@ -2232,8 +2372,8 @@ "copy-webpack-plugin": "^11.0.0", "core-js": "^3.31.1", "css-loader": "^6.8.1", - "css-minimizer-webpack-plugin": "^4.2.2", - "cssnano": "^5.1.15", + "css-minimizer-webpack-plugin": "^5.0.1", + "cssnano": "^6.1.2", "del": "^6.1.1", "detect-port": "^1.5.1", "escape-html": "^1.0.3", @@ -2253,7 +2393,7 @@ "prompts": "^2.4.2", "react-dev-utils": "^12.0.1", "react-helmet-async": "^1.3.0", - "react-loadable": "npm:@docusaurus/react-loadable@5.5.2", + "react-loadable": "npm:@docusaurus/react-loadable@6.0.0", "react-loadable-ssr-addon-v5-slorber": "^1.0.1", "react-router": "^5.3.4", "react-router-config": "^5.1.1", @@ -2279,18 +2419,20 @@ "node": ">=18.0" }, "peerDependencies": { + "@mdx-js/react": "^3.0.0", "react": "^18.0.0", "react-dom": "^18.0.0" } }, "node_modules/@docusaurus/cssnano-preset": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-3.2.1.tgz", - "integrity": "sha512-wTL9KuSSbMJjKrfu385HZEzAoamUsbKqwscAQByZw4k6Ja/RWpqgVvt/CbAC+aYEH6inLzOt+MjuRwMOrD3VBA==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/cssnano-preset/-/cssnano-preset-3.5.2.tgz", + "integrity": "sha512-D3KiQXOMA8+O0tqORBrTOEQyQxNIfPm9jEaJoALjjSjc2M/ZAWcUfPQEnwr2JB2TadHw2gqWgpZckQmrVWkytA==", + "license": "MIT", "dependencies": { - "cssnano-preset-advanced": "^5.3.10", - "postcss": "^8.4.26", - "postcss-sort-media-queries": "^4.4.1", + "cssnano-preset-advanced": "^6.1.2", + "postcss": "^8.4.38", + "postcss-sort-media-queries": "^5.2.0", "tslib": "^2.6.0" }, "engines": { @@ -2298,10 +2440,11 @@ } }, "node_modules/@docusaurus/eslint-plugin": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@docusaurus/eslint-plugin/-/eslint-plugin-3.2.1.tgz", - "integrity": "sha512-DIhptlFPvSFZm7ZyisxorEzWK6SF0SvyHTR5NzljnGSTn8lln9mlc18xYhm0zYI/TLoDRGN/fkwCMWPkxB8YVQ==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/eslint-plugin/-/eslint-plugin-3.5.2.tgz", + "integrity": "sha512-9zBtXQwRgj2unY+peS5HIISvG7kDQDoWl8dZ+sN41B2qIctNUWpBFkFAPHZSPy2cvEDQwWshNpPYDjp+sv+CVA==", "dev": true, + "license": "MIT", "dependencies": { "@typescript-eslint/utils": "^5.62.0", "tslib": "^2.6.0" @@ -2414,9 +2557,10 @@ } }, "node_modules/@docusaurus/logger": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-3.2.1.tgz", - "integrity": "sha512-0voOKJCn9RaM3np6soqEfo7SsVvf2C+CDTWhW+H/1AyBhybASpExtDEz+7ECck9TwPzFQ5tt+I3zVugUJbJWDg==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/logger/-/logger-3.5.2.tgz", + "integrity": "sha512-LHC540SGkeLfyT3RHK3gAMK6aS5TRqOD4R72BEU/DE2M/TY8WwEUAMY576UUc/oNJXv8pGhBmQB6N9p3pt8LQw==", + "license": "MIT", "dependencies": { "chalk": "^4.1.2", "tslib": "^2.6.0" @@ -2426,13 +2570,14 @@ } }, "node_modules/@docusaurus/mdx-loader": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.2.1.tgz", - "integrity": "sha512-Fs8tXhXKZjNkdGaOy1xSLXSwfjCMT73J3Zfrju2U16uGedRFRjgK0ojpK5tiC7TnunsL3tOFgp1BSMBRflX9gw==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/mdx-loader/-/mdx-loader-3.5.2.tgz", + "integrity": "sha512-ku3xO9vZdwpiMIVd8BzWV0DCqGEbCP5zs1iHfKX50vw6jX8vQo0ylYo1YJMZyz6e+JFJ17HYHT5FzVidz2IflA==", + "license": "MIT", "dependencies": { - "@docusaurus/logger": "3.2.1", - "@docusaurus/utils": "3.2.1", - "@docusaurus/utils-validation": "3.2.1", + "@docusaurus/logger": "3.5.2", + "@docusaurus/utils": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", "@mdx-js/mdx": "^3.0.0", "@slorber/remark-comment": "^1.0.0", "escape-html": "^1.0.3", @@ -2464,18 +2609,18 @@ } }, "node_modules/@docusaurus/module-type-aliases": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.2.1.tgz", - "integrity": "sha512-FyViV5TqhL1vsM7eh29nJ5NtbRE6Ra6LP1PDcPvhwPSlA7eiWGRKAn3jWwMUcmjkos5SYY+sr0/feCdbM3eQHQ==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/module-type-aliases/-/module-type-aliases-3.5.2.tgz", + "integrity": "sha512-Z+Xu3+2rvKef/YKTMxZHsEXp1y92ac0ngjDiExRdqGTmEKtCUpkbNYH8v5eXo5Ls+dnW88n6WTa+Q54kLOkwPg==", + "license": "MIT", "dependencies": { - "@docusaurus/react-loadable": "5.5.2", - "@docusaurus/types": "3.2.1", + "@docusaurus/types": "3.5.2", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", "@types/react-router-dom": "*", "react-helmet-async": "*", - "react-loadable": "npm:@docusaurus/react-loadable@5.5.2" + "react-loadable": "npm:@docusaurus/react-loadable@6.0.0" }, "peerDependencies": { "react": "*", @@ -2483,18 +2628,20 @@ } }, "node_modules/@docusaurus/plugin-content-blog": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.2.1.tgz", - "integrity": "sha512-lOx0JfhlGZoZu6pEJfeEpSISZR5dQbJGGvb42IP13G5YThNHhG9R9uoWuo4IOimPqBC7sHThdLA3VLevk61Fsw==", - "dependencies": { - "@docusaurus/core": "3.2.1", - "@docusaurus/logger": "3.2.1", - "@docusaurus/mdx-loader": "3.2.1", - "@docusaurus/types": "3.2.1", - "@docusaurus/utils": "3.2.1", - "@docusaurus/utils-common": "3.2.1", - "@docusaurus/utils-validation": "3.2.1", - "cheerio": "^1.0.0-rc.12", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-blog/-/plugin-content-blog-3.5.2.tgz", + "integrity": "sha512-R7ghWnMvjSf+aeNDH0K4fjyQnt5L0KzUEnUhmf1e3jZrv3wogeytZNN6n7X8yHcMsuZHPOrctQhXWnmxu+IRRg==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "3.5.2", + "@docusaurus/logger": "3.5.2", + "@docusaurus/mdx-loader": "3.5.2", + "@docusaurus/theme-common": "3.5.2", + "@docusaurus/types": "3.5.2", + "@docusaurus/utils": "3.5.2", + "@docusaurus/utils-common": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", + "cheerio": "1.0.0-rc.12", "feed": "^4.2.2", "fs-extra": "^11.1.1", "lodash": "^4.17.21", @@ -2509,23 +2656,26 @@ "node": ">=18.0" }, "peerDependencies": { + "@docusaurus/plugin-content-docs": "*", "react": "^18.0.0", "react-dom": "^18.0.0" } }, "node_modules/@docusaurus/plugin-content-docs": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.2.1.tgz", - "integrity": "sha512-GHe5b/lCskAR8QVbfWAfPAApvRZgqk7FN3sOHgjCtjzQACZxkHmq6QqyqZ8Jp45V7lVck4wt2Xw2IzBJ7Cz3bA==", - "dependencies": { - "@docusaurus/core": "3.2.1", - "@docusaurus/logger": "3.2.1", - "@docusaurus/mdx-loader": "3.2.1", - "@docusaurus/module-type-aliases": "3.2.1", - "@docusaurus/types": "3.2.1", - "@docusaurus/utils": "3.2.1", - "@docusaurus/utils-common": "3.2.1", - "@docusaurus/utils-validation": "3.2.1", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.5.2.tgz", + "integrity": "sha512-Bt+OXn/CPtVqM3Di44vHjE7rPCEsRCB/DMo2qoOuozB9f7+lsdrHvD0QCHdBs0uhz6deYJDppAr2VgqybKPlVQ==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "3.5.2", + "@docusaurus/logger": "3.5.2", + "@docusaurus/mdx-loader": "3.5.2", + "@docusaurus/module-type-aliases": "3.5.2", + "@docusaurus/theme-common": "3.5.2", + "@docusaurus/types": "3.5.2", + "@docusaurus/utils": "3.5.2", + "@docusaurus/utils-common": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", "@types/react-router-config": "^5.0.7", "combine-promises": "^1.1.0", "fs-extra": "^11.1.1", @@ -2544,15 +2694,16 @@ } }, "node_modules/@docusaurus/plugin-content-pages": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.2.1.tgz", - "integrity": "sha512-TOqVfMVTAHqWNEGM94Drz+PUpHDbwFy6ucHFgyTx9zJY7wPNSG5EN+rd/mU7OvAi26qpOn2o9xTdUmb28QLjEQ==", - "dependencies": { - "@docusaurus/core": "3.2.1", - "@docusaurus/mdx-loader": "3.2.1", - "@docusaurus/types": "3.2.1", - "@docusaurus/utils": "3.2.1", - "@docusaurus/utils-validation": "3.2.1", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-pages/-/plugin-content-pages-3.5.2.tgz", + "integrity": "sha512-WzhHjNpoQAUz/ueO10cnundRz+VUtkjFhhaQ9jApyv1a46FPURO4cef89pyNIOMny1fjDz/NUN2z6Yi+5WUrCw==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "3.5.2", + "@docusaurus/mdx-loader": "3.5.2", + "@docusaurus/types": "3.5.2", + "@docusaurus/utils": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", "fs-extra": "^11.1.1", "tslib": "^2.6.0", "webpack": "^5.88.1" @@ -2566,13 +2717,14 @@ } }, "node_modules/@docusaurus/plugin-debug": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-3.2.1.tgz", - "integrity": "sha512-AMKq8NuUKf2sRpN1m/sIbqbRbnmk+rSA+8mNU1LNxEl9BW9F/Gng8m9HKlzeyMPrf5XidzS1jqkuTLDJ6KIrFw==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-debug/-/plugin-debug-3.5.2.tgz", + "integrity": "sha512-kBK6GlN0itCkrmHuCS6aX1wmoWc5wpd5KJlqQ1FyrF0cLDnvsYSnh7+ftdwzt7G6lGBho8lrVwkkL9/iQvaSOA==", + "license": "MIT", "dependencies": { - "@docusaurus/core": "3.2.1", - "@docusaurus/types": "3.2.1", - "@docusaurus/utils": "3.2.1", + "@docusaurus/core": "3.5.2", + "@docusaurus/types": "3.5.2", + "@docusaurus/utils": "3.5.2", "fs-extra": "^11.1.1", "react-json-view-lite": "^1.2.0", "tslib": "^2.6.0" @@ -2586,13 +2738,14 @@ } }, "node_modules/@docusaurus/plugin-google-analytics": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.2.1.tgz", - "integrity": "sha512-/rJ+9u+Px0eTCiF4TNcNtj3kHf8cp6K1HCwOTdbsSlz6Xn21syZYcy+f1VM9wF6HrvUkXUcbM5TDCvg2IRL6bQ==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-analytics/-/plugin-google-analytics-3.5.2.tgz", + "integrity": "sha512-rjEkJH/tJ8OXRE9bwhV2mb/WP93V441rD6XnM6MIluu7rk8qg38iSxS43ga2V2Q/2ib53PcqbDEJDG/yWQRJhQ==", + "license": "MIT", "dependencies": { - "@docusaurus/core": "3.2.1", - "@docusaurus/types": "3.2.1", - "@docusaurus/utils-validation": "3.2.1", + "@docusaurus/core": "3.5.2", + "@docusaurus/types": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", "tslib": "^2.6.0" }, "engines": { @@ -2604,13 +2757,14 @@ } }, "node_modules/@docusaurus/plugin-google-gtag": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.2.1.tgz", - "integrity": "sha512-XtuJnlMvYfppeVdUyKiDIJAa/gTJKCQU92z8CLZZ9ibJdgVjFOLS10s0hIC0eL5z0U2u2loJz2rZ63HOkNHbBA==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-3.5.2.tgz", + "integrity": "sha512-lm8XL3xLkTPHFKKjLjEEAHUrW0SZBSHBE1I+i/tmYMBsjCcUB5UJ52geS5PSiOCFVR74tbPGcPHEV/gaaxFeSA==", + "license": "MIT", "dependencies": { - "@docusaurus/core": "3.2.1", - "@docusaurus/types": "3.2.1", - "@docusaurus/utils-validation": "3.2.1", + "@docusaurus/core": "3.5.2", + "@docusaurus/types": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", "@types/gtag.js": "^0.0.12", "tslib": "^2.6.0" }, @@ -2623,13 +2777,14 @@ } }, "node_modules/@docusaurus/plugin-google-tag-manager": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.2.1.tgz", - "integrity": "sha512-wiS/kE0Ny5pnjTxVCs8ljRnkL1RVMj59t6jmSsgEX7piDOoaXSMIUaoIt9ogS/v132uO0xEsxHstkRUZHQyPcQ==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-google-tag-manager/-/plugin-google-tag-manager-3.5.2.tgz", + "integrity": "sha512-QkpX68PMOMu10Mvgvr5CfZAzZQFx8WLlOiUQ/Qmmcl6mjGK6H21WLT5x7xDmcpCoKA/3CegsqIqBR+nA137lQg==", + "license": "MIT", "dependencies": { - "@docusaurus/core": "3.2.1", - "@docusaurus/types": "3.2.1", - "@docusaurus/utils-validation": "3.2.1", + "@docusaurus/core": "3.5.2", + "@docusaurus/types": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", "tslib": "^2.6.0" }, "engines": { @@ -2641,16 +2796,17 @@ } }, "node_modules/@docusaurus/plugin-sitemap": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.2.1.tgz", - "integrity": "sha512-uWZ7AxzdeaQSTCwD2yZtOiEm9zyKU+wqCmi/Sf25kQQqqFSBZUStXfaQ8OHP9cecnw893ZpZ811rPhB/wfujJw==", - "dependencies": { - "@docusaurus/core": "3.2.1", - "@docusaurus/logger": "3.2.1", - "@docusaurus/types": "3.2.1", - "@docusaurus/utils": "3.2.1", - "@docusaurus/utils-common": "3.2.1", - "@docusaurus/utils-validation": "3.2.1", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/plugin-sitemap/-/plugin-sitemap-3.5.2.tgz", + "integrity": "sha512-DnlqYyRAdQ4NHY28TfHuVk414ft2uruP4QWCH//jzpHjqvKyXjj2fmDtI8RPUBh9K8iZKFMHRnLtzJKySPWvFA==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "3.5.2", + "@docusaurus/logger": "3.5.2", + "@docusaurus/types": "3.5.2", + "@docusaurus/utils": "3.5.2", + "@docusaurus/utils-common": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", "fs-extra": "^11.1.1", "sitemap": "^7.1.1", "tslib": "^2.6.0" @@ -2664,23 +2820,24 @@ } }, "node_modules/@docusaurus/preset-classic": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-3.2.1.tgz", - "integrity": "sha512-E3OHSmttpEBcSMhfPBq3EJMBxZBM01W1rnaCUTXy9EHvkmB5AwgTfW1PwGAybPAX579ntE03R+2zmXdizWfKnQ==", - "dependencies": { - "@docusaurus/core": "3.2.1", - "@docusaurus/plugin-content-blog": "3.2.1", - "@docusaurus/plugin-content-docs": "3.2.1", - "@docusaurus/plugin-content-pages": "3.2.1", - "@docusaurus/plugin-debug": "3.2.1", - "@docusaurus/plugin-google-analytics": "3.2.1", - "@docusaurus/plugin-google-gtag": "3.2.1", - "@docusaurus/plugin-google-tag-manager": "3.2.1", - "@docusaurus/plugin-sitemap": "3.2.1", - "@docusaurus/theme-classic": "3.2.1", - "@docusaurus/theme-common": "3.2.1", - "@docusaurus/theme-search-algolia": "3.2.1", - "@docusaurus/types": "3.2.1" + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/preset-classic/-/preset-classic-3.5.2.tgz", + "integrity": "sha512-3ihfXQ95aOHiLB5uCu+9PRy2gZCeSZoDcqpnDvf3B+sTrMvMTr8qRUzBvWkoIqc82yG5prCboRjk1SVILKx6sg==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "3.5.2", + "@docusaurus/plugin-content-blog": "3.5.2", + "@docusaurus/plugin-content-docs": "3.5.2", + "@docusaurus/plugin-content-pages": "3.5.2", + "@docusaurus/plugin-debug": "3.5.2", + "@docusaurus/plugin-google-analytics": "3.5.2", + "@docusaurus/plugin-google-gtag": "3.5.2", + "@docusaurus/plugin-google-tag-manager": "3.5.2", + "@docusaurus/plugin-sitemap": "3.5.2", + "@docusaurus/theme-classic": "3.5.2", + "@docusaurus/theme-common": "3.5.2", + "@docusaurus/theme-search-algolia": "3.5.2", + "@docusaurus/types": "3.5.2" }, "engines": { "node": ">=18.0" @@ -2690,39 +2847,28 @@ "react-dom": "^18.0.0" } }, - "node_modules/@docusaurus/react-loadable": { - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz", - "integrity": "sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ==", - "dependencies": { - "@types/react": "*", - "prop-types": "^15.6.2" - }, - "peerDependencies": { - "react": "*" - } - }, "node_modules/@docusaurus/theme-classic": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-3.2.1.tgz", - "integrity": "sha512-+vSbnQyoWjc6vRZi4vJO2dBU02wqzynsai15KK+FANZudrYaBHtkbLZAQhgmxzBGVpxzi87gRohlMm+5D8f4tA==", - "dependencies": { - "@docusaurus/core": "3.2.1", - "@docusaurus/mdx-loader": "3.2.1", - "@docusaurus/module-type-aliases": "3.2.1", - "@docusaurus/plugin-content-blog": "3.2.1", - "@docusaurus/plugin-content-docs": "3.2.1", - "@docusaurus/plugin-content-pages": "3.2.1", - "@docusaurus/theme-common": "3.2.1", - "@docusaurus/theme-translations": "3.2.1", - "@docusaurus/types": "3.2.1", - "@docusaurus/utils": "3.2.1", - "@docusaurus/utils-common": "3.2.1", - "@docusaurus/utils-validation": "3.2.1", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-classic/-/theme-classic-3.5.2.tgz", + "integrity": "sha512-XRpinSix3NBv95Rk7xeMF9k4safMkwnpSgThn0UNQNumKvmcIYjfkwfh2BhwYh/BxMXQHJ/PdmNh22TQFpIaYg==", + "license": "MIT", + "dependencies": { + "@docusaurus/core": "3.5.2", + "@docusaurus/mdx-loader": "3.5.2", + "@docusaurus/module-type-aliases": "3.5.2", + "@docusaurus/plugin-content-blog": "3.5.2", + "@docusaurus/plugin-content-docs": "3.5.2", + "@docusaurus/plugin-content-pages": "3.5.2", + "@docusaurus/theme-common": "3.5.2", + "@docusaurus/theme-translations": "3.5.2", + "@docusaurus/types": "3.5.2", + "@docusaurus/utils": "3.5.2", + "@docusaurus/utils-common": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", "copy-text-to-clipboard": "^3.2.0", - "infima": "0.2.0-alpha.43", + "infima": "0.2.0-alpha.44", "lodash": "^4.17.21", "nprogress": "^0.2.0", "postcss": "^8.4.26", @@ -2742,17 +2888,15 @@ } }, "node_modules/@docusaurus/theme-common": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.2.1.tgz", - "integrity": "sha512-d+adiD7L9xv6EvfaAwUqdKf4orsM3jqgeqAM+HAjgL/Ux0GkVVnfKr+tsoe+4ow4rHe6NUt+nkkW8/K8dKdilA==", - "dependencies": { - "@docusaurus/mdx-loader": "3.2.1", - "@docusaurus/module-type-aliases": "3.2.1", - "@docusaurus/plugin-content-blog": "3.2.1", - "@docusaurus/plugin-content-docs": "3.2.1", - "@docusaurus/plugin-content-pages": "3.2.1", - "@docusaurus/utils": "3.2.1", - "@docusaurus/utils-common": "3.2.1", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.5.2.tgz", + "integrity": "sha512-QXqlm9S6x9Ibwjs7I2yEDgsCocp708DrCrgHgKwg2n2AY0YQ6IjU0gAK35lHRLOvAoJUfCKpQAwUykB0R7+Eew==", + "license": "MIT", + "dependencies": { + "@docusaurus/mdx-loader": "3.5.2", + "@docusaurus/module-type-aliases": "3.5.2", + "@docusaurus/utils": "3.5.2", + "@docusaurus/utils-common": "3.5.2", "@types/history": "^4.7.11", "@types/react": "*", "@types/react-router-config": "*", @@ -2766,23 +2910,25 @@ "node": ">=18.0" }, "peerDependencies": { + "@docusaurus/plugin-content-docs": "*", "react": "^18.0.0", "react-dom": "^18.0.0" } }, "node_modules/@docusaurus/theme-search-algolia": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.2.1.tgz", - "integrity": "sha512-bzhCrpyXBXzeydNUH83II2akvFEGfhsNTPPWsk5N7e+odgQCQwoHhcF+2qILbQXjaoZ6B3c48hrvkyCpeyqGHw==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-search-algolia/-/theme-search-algolia-3.5.2.tgz", + "integrity": "sha512-qW53kp3VzMnEqZGjakaV90sst3iN1o32PH+nawv1uepROO8aEGxptcq2R5rsv7aBShSRbZwIobdvSYKsZ5pqvA==", + "license": "MIT", "dependencies": { "@docsearch/react": "^3.5.2", - "@docusaurus/core": "3.2.1", - "@docusaurus/logger": "3.2.1", - "@docusaurus/plugin-content-docs": "3.2.1", - "@docusaurus/theme-common": "3.2.1", - "@docusaurus/theme-translations": "3.2.1", - "@docusaurus/utils": "3.2.1", - "@docusaurus/utils-validation": "3.2.1", + "@docusaurus/core": "3.5.2", + "@docusaurus/logger": "3.5.2", + "@docusaurus/plugin-content-docs": "3.5.2", + "@docusaurus/theme-common": "3.5.2", + "@docusaurus/theme-translations": "3.5.2", + "@docusaurus/utils": "3.5.2", + "@docusaurus/utils-validation": "3.5.2", "algoliasearch": "^4.18.0", "algoliasearch-helper": "^3.13.3", "clsx": "^2.0.0", @@ -2801,9 +2947,10 @@ } }, "node_modules/@docusaurus/theme-translations": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-3.2.1.tgz", - "integrity": "sha512-jAUMkIkFfY+OAhJhv6mV8zlwY6J4AQxJPTgLdR2l+Otof9+QdJjHNh/ifVEu9q0lp3oSPlJj9l05AaP7Ref+cg==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/theme-translations/-/theme-translations-3.5.2.tgz", + "integrity": "sha512-GPZLcu4aT1EmqSTmbdpVrDENGR2yObFEX8ssEFYTCiAIVc0EihNSdOIBTazUvgNqwvnoU1A8vIs1xyzc3LITTw==", + "license": "MIT", "dependencies": { "fs-extra": "^11.1.1", "tslib": "^2.6.0" @@ -2813,15 +2960,17 @@ } }, "node_modules/@docusaurus/tsconfig": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@docusaurus/tsconfig/-/tsconfig-3.2.1.tgz", - "integrity": "sha512-+biUwtsYW3oChLxYezzA+NIgS3Q9KDRl7add/YT54RXs9Q4rKInebxdHdG6JFs5BaTg45gyjDu0rvNVcGeHODg==", - "dev": true + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/tsconfig/-/tsconfig-3.5.2.tgz", + "integrity": "sha512-rQ7toURCFnWAIn8ubcquDs0ewhPwviMzxh6WpRjBW7sJVCXb6yzwUaY3HMNa0VXCFw+qkIbFywrMTf+Pb4uHWQ==", + "dev": true, + "license": "MIT" }, "node_modules/@docusaurus/types": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-3.2.1.tgz", - "integrity": "sha512-n/toxBzL2oxTtRTOFiGKsHypzn/Pm+sXyw+VSk1UbqbXQiHOwHwts55bpKwbcUgA530Is6kix3ELiFOv9GAMfw==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/types/-/types-3.5.2.tgz", + "integrity": "sha512-N6GntLXoLVUwkZw7zCxwy9QiuEXIcTVzA9AkmNw16oc0AP3SXLrMmDMMBIfgqwuKWa6Ox6epHol9kMtJqekACw==", + "license": "MIT", "dependencies": { "@mdx-js/mdx": "^3.0.0", "@types/history": "^4.7.11", @@ -2839,13 +2988,14 @@ } }, "node_modules/@docusaurus/utils": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.2.1.tgz", - "integrity": "sha512-DPkIS/EPc+pGAV798PUXgNzJFM3HJouoQXgr0KDZuJVz1EkWbDLOcQwLIz8Qx7liI9ddfkN/TXTRQdsTPZNakw==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/utils/-/utils-3.5.2.tgz", + "integrity": "sha512-33QvcNFh+Gv+C2dP9Y9xWEzMgf3JzrpL2nW9PopidiohS1nDcyknKRx2DWaFvyVTTYIkkABVSr073VTj/NITNA==", + "license": "MIT", "dependencies": { - "@docusaurus/logger": "3.2.1", - "@docusaurus/utils-common": "3.2.1", - "@svgr/webpack": "^6.5.1", + "@docusaurus/logger": "3.5.2", + "@docusaurus/utils-common": "3.5.2", + "@svgr/webpack": "^8.1.0", "escape-string-regexp": "^4.0.0", "file-loader": "^6.2.0", "fs-extra": "^11.1.1", @@ -2861,6 +3011,7 @@ "shelljs": "^0.8.5", "tslib": "^2.6.0", "url-loader": "^4.1.1", + "utility-types": "^3.10.0", "webpack": "^5.88.1" }, "engines": { @@ -2876,9 +3027,10 @@ } }, "node_modules/@docusaurus/utils-common": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-3.2.1.tgz", - "integrity": "sha512-N5vadULnRLiqX2QfTjVEU3u5vo6RG2EZTdyXvJdzDOdrLCGIZAfnf/VkssinFZ922sVfaFfQ4FnStdhn5TWdVg==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-common/-/utils-common-3.5.2.tgz", + "integrity": "sha512-i0AZjHiRgJU6d7faQngIhuHKNrszpL/SHQPgF1zH4H+Ij6E9NBYGy6pkcGWToIv7IVPbs+pQLh1P3whn0gWXVg==", + "license": "MIT", "dependencies": { "tslib": "^2.6.0" }, @@ -2895,15 +3047,18 @@ } }, "node_modules/@docusaurus/utils-validation": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.2.1.tgz", - "integrity": "sha512-+x7IR9hNMXi62L1YAglwd0s95fR7+EtirjTxSN4kahYRWGqOi3jlQl1EV0az/yTEvKbxVvOPcdYicGu9dk4LJw==", - "dependencies": { - "@docusaurus/logger": "3.2.1", - "@docusaurus/utils": "3.2.1", - "@docusaurus/utils-common": "3.2.1", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/@docusaurus/utils-validation/-/utils-validation-3.5.2.tgz", + "integrity": "sha512-m+Foq7augzXqB6HufdS139PFxDC5d5q2QKZy8q0qYYvGdI6nnlNsGH4cIGsgBnV7smz+mopl3g4asbSDvMV0jA==", + "license": "MIT", + "dependencies": { + "@docusaurus/logger": "3.5.2", + "@docusaurus/utils": "3.5.2", + "@docusaurus/utils-common": "3.5.2", + "fs-extra": "^11.2.0", "joi": "^17.9.2", "js-yaml": "^4.1.0", + "lodash": "^4.17.21", "tslib": "^2.6.0" }, "engines": { @@ -2920,9 +3075,10 @@ } }, "node_modules/@easyops-cn/docusaurus-search-local": { - "version": "0.40.1", - "resolved": "https://registry.npmjs.org/@easyops-cn/docusaurus-search-local/-/docusaurus-search-local-0.40.1.tgz", - "integrity": "sha512-4HMFZMpKKdd5qq1nFB8cvrAkgzZ1kNxphVciI64YHtmDYGIthVGZVG6+Ci7AAhzCR+ixLJkYwtVekvuMLjr2ZQ==", + "version": "0.44.5", + "resolved": "https://registry.npmjs.org/@easyops-cn/docusaurus-search-local/-/docusaurus-search-local-0.44.5.tgz", + "integrity": "sha512-jT3wuYVzRoeB1gea+2iDtOMme0fD2h3M8HDVgs3garITO6vRxvEraFRVlYkfjLN9BkmzjMlz9nn7MI4qIx8utw==", + "license": "MIT", "dependencies": { "@docusaurus/plugin-content-docs": "^2 || ^3", "@docusaurus/theme-translations": "^2 || ^3", @@ -2931,7 +3087,7 @@ "@docusaurus/utils-validation": "^2 || ^3", "@easyops-cn/autocomplete.js": "^0.38.1", "@node-rs/jieba": "^1.6.0", - "cheerio": "^1.0.0-rc.3", + "cheerio": "^1.0.0", "clsx": "^1.1.1", "debug": "^4.2.0", "fs-extra": "^10.0.0", @@ -2950,6 +3106,31 @@ "react-dom": "^16.14.0 || 17 || ^18" } }, + "node_modules/@easyops-cn/docusaurus-search-local/node_modules/cheerio": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0.tgz", + "integrity": "sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww==", + "license": "MIT", + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.1.0", + "encoding-sniffer": "^0.2.0", + "htmlparser2": "^9.1.0", + "parse5": "^7.1.2", + "parse5-htmlparser2-tree-adapter": "^7.0.0", + "parse5-parser-stream": "^7.1.2", + "undici": "^6.19.5", + "whatwg-mimetype": "^4.0.0" + }, + "engines": { + "node": ">=18.17" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, "node_modules/@easyops-cn/docusaurus-search-local/node_modules/clsx": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", @@ -2971,6 +3152,25 @@ "node": ">=12" } }, + "node_modules/@easyops-cn/docusaurus-search-local/node_modules/htmlparser2": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", + "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.1.0", + "entities": "^4.5.0" + } + }, "node_modules/@emnapi/core": { "version": "0.45.0", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-0.45.0.tgz", @@ -3144,6 +3344,7 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "license": "MIT", "dependencies": { "@sinclair/typebox": "^0.27.8" }, @@ -3155,6 +3356,7 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "license": "MIT", "dependencies": { "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", @@ -3259,9 +3461,10 @@ } }, "node_modules/@mdx-js/react": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.0.0.tgz", - "integrity": "sha512-nDctevR9KyYFyV+m+/+S4cpzCWHqj+iHDHq3QrsWezcC+B17uZdIWgCguESUkwFhM3n/56KxWVE3V6EokrmONQ==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.0.1.tgz", + "integrity": "sha512-9ZrPIU4MGf6et1m1ov3zKf+q9+deetI51zprKB1D/z3NOb+rUxxtEl3mCjW5wTGh6VhRdwPueh1oRzi6ezkA8A==", + "license": "MIT", "dependencies": { "@types/mdx": "^2.0.0" }, @@ -3618,7 +3821,8 @@ "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==" + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "license": "MIT" }, "node_modules/@sindresorhus/is": { "version": "4.6.0", @@ -3642,11 +3846,12 @@ } }, "node_modules/@svgr/babel-plugin-add-jsx-attribute": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-6.5.1.tgz", - "integrity": "sha512-9PYGcXrAxitycIjRmZB+Q0JaN07GZIWaTBIGQzfaZv+qr1n8X1XUEJ5rZ/vx6OVD9RRYlrNnXWExQXcmZeD/BQ==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", + "integrity": "sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==", + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=14" }, "funding": { "type": "github", @@ -3660,6 +3865,7 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-8.0.0.tgz", "integrity": "sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==", + "license": "MIT", "engines": { "node": ">=14" }, @@ -3675,6 +3881,7 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-8.0.0.tgz", "integrity": "sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==", + "license": "MIT", "engines": { "node": ">=14" }, @@ -3687,11 +3894,12 @@ } }, "node_modules/@svgr/babel-plugin-replace-jsx-attribute-value": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-6.5.1.tgz", - "integrity": "sha512-8DPaVVE3fd5JKuIC29dqyMB54sA6mfgki2H2+swh+zNJoynC8pMPzOkidqHOSc6Wj032fhl8Z0TVn1GiPpAiJg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-8.0.0.tgz", + "integrity": "sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==", + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=14" }, "funding": { "type": "github", @@ -3702,11 +3910,12 @@ } }, "node_modules/@svgr/babel-plugin-svg-dynamic-title": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-6.5.1.tgz", - "integrity": "sha512-FwOEi0Il72iAzlkaHrlemVurgSQRDFbk0OC8dSvD5fSBPHltNh7JtLsxmZUhjYBZo2PpcU/RJvvi6Q0l7O7ogw==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-8.0.0.tgz", + "integrity": "sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==", + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=14" }, "funding": { "type": "github", @@ -3717,11 +3926,12 @@ } }, "node_modules/@svgr/babel-plugin-svg-em-dimensions": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-6.5.1.tgz", - "integrity": "sha512-gWGsiwjb4tw+ITOJ86ndY/DZZ6cuXMNE/SjcDRg+HLuCmwpcjOktwRF9WgAiycTqJD/QXqL2f8IzE2Rzh7aVXA==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-8.0.0.tgz", + "integrity": "sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==", + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=14" }, "funding": { "type": "github", @@ -3732,11 +3942,12 @@ } }, "node_modules/@svgr/babel-plugin-transform-react-native-svg": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-6.5.1.tgz", - "integrity": "sha512-2jT3nTayyYP7kI6aGutkyfJ7UMGtuguD72OjeGLwVNyfPRBD8zQthlvL+fAbAKk5n9ZNcvFkp/b1lZ7VsYqVJg==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-8.1.0.tgz", + "integrity": "sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==", + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=14" }, "funding": { "type": "github", @@ -3747,9 +3958,10 @@ } }, "node_modules/@svgr/babel-plugin-transform-svg-component": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-6.5.1.tgz", - "integrity": "sha512-a1p6LF5Jt33O3rZoVRBqdxL350oge54iZWHNI6LJB5tQ7EelvD/Mb1mfBiZNAan0dt4i3VArkFRjA4iObuNykQ==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-8.0.0.tgz", + "integrity": "sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==", + "license": "MIT", "engines": { "node": ">=12" }, @@ -3762,21 +3974,22 @@ } }, "node_modules/@svgr/babel-preset": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-6.5.1.tgz", - "integrity": "sha512-6127fvO/FF2oi5EzSQOAjo1LE3OtNVh11R+/8FXa+mHx1ptAaS4cknIjnUA7e6j6fwGGJ17NzaTJFUwOV2zwCw==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz", + "integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==", + "license": "MIT", "dependencies": { - "@svgr/babel-plugin-add-jsx-attribute": "^6.5.1", - "@svgr/babel-plugin-remove-jsx-attribute": "*", - "@svgr/babel-plugin-remove-jsx-empty-expression": "*", - "@svgr/babel-plugin-replace-jsx-attribute-value": "^6.5.1", - "@svgr/babel-plugin-svg-dynamic-title": "^6.5.1", - "@svgr/babel-plugin-svg-em-dimensions": "^6.5.1", - "@svgr/babel-plugin-transform-react-native-svg": "^6.5.1", - "@svgr/babel-plugin-transform-svg-component": "^6.5.1" + "@svgr/babel-plugin-add-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0", + "@svgr/babel-plugin-remove-jsx-empty-expression": "8.0.0", + "@svgr/babel-plugin-replace-jsx-attribute-value": "8.0.0", + "@svgr/babel-plugin-svg-dynamic-title": "8.0.0", + "@svgr/babel-plugin-svg-em-dimensions": "8.0.0", + "@svgr/babel-plugin-transform-react-native-svg": "8.1.0", + "@svgr/babel-plugin-transform-svg-component": "8.0.0" }, "engines": { - "node": ">=10" + "node": ">=14" }, "funding": { "type": "github", @@ -3787,18 +4000,19 @@ } }, "node_modules/@svgr/core": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@svgr/core/-/core-6.5.1.tgz", - "integrity": "sha512-/xdLSWxK5QkqG524ONSjvg3V/FkNyCv538OIBdQqPNaAta3AsXj/Bd2FbvR87yMbXO2hFSWiAe/Q6IkVPDw+mw==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", + "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", + "license": "MIT", "dependencies": { - "@babel/core": "^7.19.6", - "@svgr/babel-preset": "^6.5.1", - "@svgr/plugin-jsx": "^6.5.1", + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", "camelcase": "^6.2.0", - "cosmiconfig": "^7.0.1" + "cosmiconfig": "^8.1.3", + "snake-case": "^3.0.4" }, "engines": { - "node": ">=10" + "node": ">=14" }, "funding": { "type": "github", @@ -3806,15 +4020,16 @@ } }, "node_modules/@svgr/hast-util-to-babel-ast": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-6.5.1.tgz", - "integrity": "sha512-1hnUxxjd83EAxbL4a0JDJoD3Dao3hmjvyvyEV8PzWmLK3B9m9NPlW7GKjFyoWE8nM7HnXzPcmmSyOW8yOddSXw==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-8.0.0.tgz", + "integrity": "sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==", + "license": "MIT", "dependencies": { - "@babel/types": "^7.20.0", + "@babel/types": "^7.21.3", "entities": "^4.4.0" }, "engines": { - "node": ">=10" + "node": ">=14" }, "funding": { "type": "github", @@ -3822,37 +4037,39 @@ } }, "node_modules/@svgr/plugin-jsx": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-6.5.1.tgz", - "integrity": "sha512-+UdQxI3jgtSjCykNSlEMuy1jSRQlGC7pqBCPvkG/2dATdWo082zHTTK3uhnAju2/6XpE6B5mZ3z4Z8Ns01S8Gw==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz", + "integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==", + "license": "MIT", "dependencies": { - "@babel/core": "^7.19.6", - "@svgr/babel-preset": "^6.5.1", - "@svgr/hast-util-to-babel-ast": "^6.5.1", + "@babel/core": "^7.21.3", + "@svgr/babel-preset": "8.1.0", + "@svgr/hast-util-to-babel-ast": "8.0.0", "svg-parser": "^2.0.4" }, "engines": { - "node": ">=10" + "node": ">=14" }, "funding": { "type": "github", "url": "https://github.com/sponsors/gregberge" }, "peerDependencies": { - "@svgr/core": "^6.0.0" + "@svgr/core": "*" } }, "node_modules/@svgr/plugin-svgo": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-6.5.1.tgz", - "integrity": "sha512-omvZKf8ixP9z6GWgwbtmP9qQMPX4ODXi+wzbVZgomNFsUIlHA1sf4fThdwTWSsZGgvGAG6yE+b/F5gWUkcZ/iQ==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-8.1.0.tgz", + "integrity": "sha512-Ywtl837OGO9pTLIN/onoWLmDQ4zFUycI1g76vuKGEz6evR/ZTJlJuz3G/fIkb6OVBJ2g0o6CGJzaEjfmEo3AHA==", + "license": "MIT", "dependencies": { - "cosmiconfig": "^7.0.1", - "deepmerge": "^4.2.2", - "svgo": "^2.8.0" + "cosmiconfig": "^8.1.3", + "deepmerge": "^4.3.1", + "svgo": "^3.0.2" }, "engines": { - "node": ">=10" + "node": ">=14" }, "funding": { "type": "github", @@ -3863,21 +4080,22 @@ } }, "node_modules/@svgr/webpack": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-6.5.1.tgz", - "integrity": "sha512-cQ/AsnBkXPkEK8cLbv4Dm7JGXq2XrumKnL1dRpJD9rIO2fTIlJI9a1uCciYG1F2aUsox/hJQyNGbt3soDxSRkA==", + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@svgr/webpack/-/webpack-8.1.0.tgz", + "integrity": "sha512-LnhVjMWyMQV9ZmeEy26maJk+8HTIbd59cH4F2MJ439k9DqejRisfFNGAPvRYlKETuh9LrImlS8aKsBgKjMA8WA==", + "license": "MIT", "dependencies": { - "@babel/core": "^7.19.6", - "@babel/plugin-transform-react-constant-elements": "^7.18.12", - "@babel/preset-env": "^7.19.4", + "@babel/core": "^7.21.3", + "@babel/plugin-transform-react-constant-elements": "^7.21.3", + "@babel/preset-env": "^7.20.2", "@babel/preset-react": "^7.18.6", - "@babel/preset-typescript": "^7.18.6", - "@svgr/core": "^6.5.1", - "@svgr/plugin-jsx": "^6.5.1", - "@svgr/plugin-svgo": "^6.5.1" + "@babel/preset-typescript": "^7.21.0", + "@svgr/core": "8.1.0", + "@svgr/plugin-jsx": "8.1.0", + "@svgr/plugin-svgo": "8.1.0" }, "engines": { - "node": ">=10" + "node": ">=14" }, "funding": { "type": "github", @@ -3899,6 +4117,7 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==", + "license": "ISC", "engines": { "node": ">=10.13.0" } @@ -3962,24 +4181,6 @@ "@types/ms": "*" } }, - "node_modules/@types/eslint": { - "version": "8.56.2", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.2.tgz", - "integrity": "sha512-uQDwm1wFHmbBbCZCqAlq6Do9LYwByNZHWzXppSnay9SuwJ+VRbjkbLABer54kcPnMSlG6Fdiy2yaFXm/z9Z5gw==", - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, "node_modules/@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", @@ -4018,7 +4219,8 @@ "node_modules/@types/gtag.js": { "version": "0.0.12", "resolved": "https://registry.npmjs.org/@types/gtag.js/-/gtag.js-0.0.12.tgz", - "integrity": "sha512-YQV9bUsemkzG81Ea295/nF/5GijnD2Af7QhEofh7xu+kvCN6RdodgNwwGWXB5GMI3NoyvQo0odNctoH/qLMIpg==" + "integrity": "sha512-YQV9bUsemkzG81Ea295/nF/5GijnD2Af7QhEofh7xu+kvCN6RdodgNwwGWXB5GMI3NoyvQo0odNctoH/qLMIpg==", + "license": "MIT" }, "node_modules/@types/hast": { "version": "3.0.4", @@ -4059,12 +4261,14 @@ "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==" + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "license": "MIT" }, "node_modules/@types/istanbul-lib-report": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "license": "MIT", "dependencies": { "@types/istanbul-lib-coverage": "*" } @@ -4073,6 +4277,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "license": "MIT", "dependencies": { "@types/istanbul-lib-report": "*" } @@ -4194,6 +4399,7 @@ "version": "1.2.7", "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz", "integrity": "sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==", + "license": "MIT", "dependencies": { "@types/node": "*" } @@ -4258,9 +4464,10 @@ } }, "node_modules/@types/yargs": { - "version": "17.0.32", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", - "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "license": "MIT", "dependencies": { "@types/yargs-parser": "*" } @@ -4268,7 +4475,8 @@ "node_modules/@types/yargs-parser": { "version": "21.0.3", "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==" + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "6.20.0", @@ -4490,9 +4698,9 @@ "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==" }, "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==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", + "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", "dependencies": { "@webassemblyjs/helper-numbers": "1.11.6", "@webassemblyjs/helper-wasm-bytecode": "1.11.6" @@ -4509,9 +4717,9 @@ "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==" }, "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==" + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", + "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==" }, "node_modules/@webassemblyjs/helper-numbers": { "version": "1.11.6", @@ -4529,14 +4737,14 @@ "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==" }, "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==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", + "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6" + "@webassemblyjs/wasm-gen": "1.12.1" } }, "node_modules/@webassemblyjs/ieee754": { @@ -4561,26 +4769,26 @@ "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==" }, "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==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", + "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", "@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/helper-wasm-section": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-opt": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1", + "@webassemblyjs/wast-printer": "1.12.1" } }, "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", - "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", + "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", "dependencies": { - "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-wasm-bytecode": "1.11.6", "@webassemblyjs/ieee754": "1.11.6", "@webassemblyjs/leb128": "1.11.6", @@ -4588,22 +4796,22 @@ } }, "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==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", + "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", "dependencies": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6", - "@webassemblyjs/wasm-parser": "1.11.6" + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1" } }, "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", - "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", + "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", "dependencies": { - "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/ast": "1.12.1", "@webassemblyjs/helper-api-error": "1.11.6", "@webassemblyjs/helper-wasm-bytecode": "1.11.6", "@webassemblyjs/ieee754": "1.11.6", @@ -4612,11 +4820,11 @@ } }, "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==", + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", + "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", "dependencies": { - "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/ast": "1.12.1", "@xtuc/long": "4.2.2" } }, @@ -4672,10 +4880,10 @@ "node": ">=0.4.0" } }, - "node_modules/acorn-import-assertions": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", - "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", "peerDependencies": { "acorn": "^8" } @@ -4759,31 +4967,33 @@ } }, "node_modules/algoliasearch": { - "version": "4.23.3", - "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.23.3.tgz", - "integrity": "sha512-Le/3YgNvjW9zxIQMRhUHuhiUjAlKY/zsdZpfq4dlLqg6mEm0nL6yk+7f2hDOtLpxsgE4jSzDmvHL7nXdBp5feg==", - "dependencies": { - "@algolia/cache-browser-local-storage": "4.23.3", - "@algolia/cache-common": "4.23.3", - "@algolia/cache-in-memory": "4.23.3", - "@algolia/client-account": "4.23.3", - "@algolia/client-analytics": "4.23.3", - "@algolia/client-common": "4.23.3", - "@algolia/client-personalization": "4.23.3", - "@algolia/client-search": "4.23.3", - "@algolia/logger-common": "4.23.3", - "@algolia/logger-console": "4.23.3", - "@algolia/recommend": "4.23.3", - "@algolia/requester-browser-xhr": "4.23.3", - "@algolia/requester-common": "4.23.3", - "@algolia/requester-node-http": "4.23.3", - "@algolia/transporter": "4.23.3" + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.24.0.tgz", + "integrity": "sha512-bf0QV/9jVejssFBmz2HQLxUadxk574t4iwjCKp5E7NBzwKkrDEhKPISIIjAU/p6K5qDx3qoeh4+26zWN1jmw3g==", + "license": "MIT", + "dependencies": { + "@algolia/cache-browser-local-storage": "4.24.0", + "@algolia/cache-common": "4.24.0", + "@algolia/cache-in-memory": "4.24.0", + "@algolia/client-account": "4.24.0", + "@algolia/client-analytics": "4.24.0", + "@algolia/client-common": "4.24.0", + "@algolia/client-personalization": "4.24.0", + "@algolia/client-search": "4.24.0", + "@algolia/logger-common": "4.24.0", + "@algolia/logger-console": "4.24.0", + "@algolia/recommend": "4.24.0", + "@algolia/requester-browser-xhr": "4.24.0", + "@algolia/requester-common": "4.24.0", + "@algolia/requester-node-http": "4.24.0", + "@algolia/transporter": "4.24.0" } }, "node_modules/algoliasearch-helper": { - "version": "3.18.0", - "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.18.0.tgz", - "integrity": "sha512-ZXvA8r6VG46V343jnIE7Tei8Xr0/9N8YhD27joC0BKxeogQyvNu7O37i510wA7FnrDjoa/tFhK90WUaBlkaqnw==", + "version": "3.22.5", + "resolved": "https://registry.npmjs.org/algoliasearch-helper/-/algoliasearch-helper-3.22.5.tgz", + "integrity": "sha512-lWvhdnc+aKOKx8jyA3bsdEgHzm/sglC4cYdMG4xSQyRiPLJVJtH/IVYZG3Hp6PkTEhQqhyVYkeP9z2IlcHJsWw==", + "license": "MIT", "dependencies": { "@algolia/events": "^4.0.1" }, @@ -4791,6 +5001,45 @@ "algoliasearch": ">= 3.1 < 6" } }, + "node_modules/algoliasearch/node_modules/@algolia/client-common": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-4.24.0.tgz", + "integrity": "sha512-bc2ROsNL6w6rqpl5jj/UywlIYC21TwSSoFHKl01lYirGMW+9Eek6r02Tocg4gZ8HAw3iBvu6XQiM3BEbmEMoiA==", + "license": "MIT", + "dependencies": { + "@algolia/requester-common": "4.24.0", + "@algolia/transporter": "4.24.0" + } + }, + "node_modules/algoliasearch/node_modules/@algolia/client-search": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-4.24.0.tgz", + "integrity": "sha512-uRW6EpNapmLAD0mW47OXqTP8eiIx5F6qN9/x/7HHO6owL3N1IXqydGwW5nhDFBrV+ldouro2W1VX3XlcUXEFCA==", + "license": "MIT", + "dependencies": { + "@algolia/client-common": "4.24.0", + "@algolia/requester-common": "4.24.0", + "@algolia/transporter": "4.24.0" + } + }, + "node_modules/algoliasearch/node_modules/@algolia/requester-browser-xhr": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.24.0.tgz", + "integrity": "sha512-Z2NxZMb6+nVXSjF13YpjYTdvV3032YTBSGm2vnYvYPA6mMxzM3v5rsCiSspndn9rzIW4Qp1lPHBvuoKJV6jnAA==", + "license": "MIT", + "dependencies": { + "@algolia/requester-common": "4.24.0" + } + }, + "node_modules/algoliasearch/node_modules/@algolia/requester-node-http": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-4.24.0.tgz", + "integrity": "sha512-JF18yTjNOVYvU/L3UosRcvbPMGT9B+/GQWNWnenIImglzNVGpyzChkXLnrSf6uxwVNO6ESGu6oN8MqcGQcjQJw==", + "license": "MIT", + "dependencies": { + "@algolia/requester-common": "4.24.0" + } + }, "node_modules/ansi-align": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", @@ -4865,7 +5114,8 @@ "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==" + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "license": "MIT" }, "node_modules/argparse": { "version": "2.0.1", @@ -5041,9 +5291,10 @@ } }, "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==", + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "license": "MIT", "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.5", @@ -5053,7 +5304,7 @@ "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", - "qs": "6.11.0", + "qs": "6.13.0", "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" @@ -5067,6 +5318,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -5075,6 +5327,7 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", "dependencies": { "ms": "2.0.0" } @@ -5082,7 +5335,8 @@ "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==" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" }, "node_modules/bonjour-service": { "version": "1.2.1", @@ -5269,6 +5523,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==", + "license": "MIT", "dependencies": { "browserslist": "^4.0.0", "caniuse-lite": "^1.0.0", @@ -5542,9 +5797,10 @@ } }, "node_modules/clsx": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.0.tgz", - "integrity": "sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", "engines": { "node": ">=6" } @@ -5577,7 +5833,8 @@ "node_modules/colord": { "version": "2.9.3", "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", - "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==" + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", + "license": "MIT" }, "node_modules/colorette": { "version": "2.0.20", @@ -5725,6 +5982,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -5751,6 +6009,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/copy-text-to-clipboard/-/copy-text-to-clipboard-3.2.0.tgz", "integrity": "sha512-RnJFp1XR/LOBDckxTib5Qjr/PMfkatD0MUCQgdpqS8MdKiNUzBjAQBEN6oUy+jW7LI93BBG3DtMB2KOOKpGs2Q==", + "license": "MIT", "engines": { "node": ">=12" }, @@ -5859,18 +6118,29 @@ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" }, "node_modules/cosmiconfig": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", - "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "version": "8.3.6", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", + "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", + "license": "MIT", "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" + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" }, "engines": { - "node": ">=10" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, "node_modules/cross-spawn": { @@ -5912,11 +6182,12 @@ } }, "node_modules/css-declaration-sorter": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz", - "integrity": "sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.2.0.tgz", + "integrity": "sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow==", + "license": "ISC", "engines": { - "node": "^10 || ^12 || >=14" + "node": "^14 || ^16 || >=18" }, "peerDependencies": { "postcss": "^8.0.9" @@ -5957,16 +6228,17 @@ } }, "node_modules/css-minimizer-webpack-plugin": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-4.2.2.tgz", - "integrity": "sha512-s3Of/4jKfw1Hj9CxEO1E5oXhQAxlayuHO2y/ML+C6I9sQ7FdzfEV6QgMLN3vI+qFsjJGIAFLKtQK7t8BOXAIyA==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-5.0.1.tgz", + "integrity": "sha512-3caImjKFQkS+ws1TGcFn0V1HyDJFq1Euy589JlD6/3rV2kj+w7r5G9WDMgSHvpvXHNZ2calVypZWuEDQd9wfLg==", + "license": "MIT", "dependencies": { - "cssnano": "^5.1.8", - "jest-worker": "^29.1.2", - "postcss": "^8.4.17", - "schema-utils": "^4.0.0", - "serialize-javascript": "^6.0.0", - "source-map": "^0.6.1" + "@jridgewell/trace-mapping": "^0.3.18", + "cssnano": "^6.0.1", + "jest-worker": "^29.4.3", + "postcss": "^8.4.24", + "schema-utils": "^4.0.1", + "serialize-javascript": "^6.0.1" }, "engines": { "node": ">= 14.15.0" @@ -5999,14 +6271,6 @@ } } }, - "node_modules/css-minimizer-webpack-plugin/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==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/css-select": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", @@ -6023,23 +6287,16 @@ } }, "node_modules/css-tree": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", - "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "license": "MIT", "dependencies": { - "mdn-data": "2.0.14", - "source-map": "^0.6.1" + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" }, "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/css-tree/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==", - "engines": { - "node": ">=0.10.0" + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" } }, "node_modules/css-what": { @@ -6065,108 +6322,135 @@ } }, "node_modules/cssnano": { - "version": "5.1.15", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-5.1.15.tgz", - "integrity": "sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-6.1.2.tgz", + "integrity": "sha512-rYk5UeX7VAM/u0lNqewCdasdtPK81CgX8wJFLEIXHbV2oldWRgJAsZrdhRXkV1NJzA2g850KiFm9mMU2HxNxMA==", + "license": "MIT", "dependencies": { - "cssnano-preset-default": "^5.2.14", - "lilconfig": "^2.0.3", - "yaml": "^1.10.2" + "cssnano-preset-default": "^6.1.2", + "lilconfig": "^3.1.1" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/cssnano" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/cssnano-preset-advanced": { - "version": "5.3.10", - "resolved": "https://registry.npmjs.org/cssnano-preset-advanced/-/cssnano-preset-advanced-5.3.10.tgz", - "integrity": "sha512-fnYJyCS9jgMU+cmHO1rPSPf9axbQyD7iUhLO5Df6O4G+fKIOMps+ZbU0PdGFejFBBZ3Pftf18fn1eG7MAPUSWQ==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/cssnano-preset-advanced/-/cssnano-preset-advanced-6.1.2.tgz", + "integrity": "sha512-Nhao7eD8ph2DoHolEzQs5CfRpiEP0xa1HBdnFZ82kvqdmbwVBUr2r1QuQ4t1pi+D1ZpqpcO4T+wy/7RxzJ/WPQ==", + "license": "MIT", "dependencies": { - "autoprefixer": "^10.4.12", - "cssnano-preset-default": "^5.2.14", - "postcss-discard-unused": "^5.1.0", - "postcss-merge-idents": "^5.1.1", - "postcss-reduce-idents": "^5.2.0", - "postcss-zindex": "^5.1.0" + "autoprefixer": "^10.4.19", + "browserslist": "^4.23.0", + "cssnano-preset-default": "^6.1.2", + "postcss-discard-unused": "^6.0.5", + "postcss-merge-idents": "^6.0.3", + "postcss-reduce-idents": "^6.0.3", + "postcss-zindex": "^6.0.2" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/cssnano-preset-default": { - "version": "5.2.14", - "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-5.2.14.tgz", - "integrity": "sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A==", - "dependencies": { - "css-declaration-sorter": "^6.3.1", - "cssnano-utils": "^3.1.0", - "postcss-calc": "^8.2.3", - "postcss-colormin": "^5.3.1", - "postcss-convert-values": "^5.1.3", - "postcss-discard-comments": "^5.1.2", - "postcss-discard-duplicates": "^5.1.0", - "postcss-discard-empty": "^5.1.1", - "postcss-discard-overridden": "^5.1.0", - "postcss-merge-longhand": "^5.1.7", - "postcss-merge-rules": "^5.1.4", - "postcss-minify-font-values": "^5.1.0", - "postcss-minify-gradients": "^5.1.1", - "postcss-minify-params": "^5.1.4", - "postcss-minify-selectors": "^5.2.1", - "postcss-normalize-charset": "^5.1.0", - "postcss-normalize-display-values": "^5.1.0", - "postcss-normalize-positions": "^5.1.1", - "postcss-normalize-repeat-style": "^5.1.1", - "postcss-normalize-string": "^5.1.0", - "postcss-normalize-timing-functions": "^5.1.0", - "postcss-normalize-unicode": "^5.1.1", - "postcss-normalize-url": "^5.1.0", - "postcss-normalize-whitespace": "^5.1.1", - "postcss-ordered-values": "^5.1.3", - "postcss-reduce-initial": "^5.1.2", - "postcss-reduce-transforms": "^5.1.0", - "postcss-svgo": "^5.1.0", - "postcss-unique-selectors": "^5.1.1" - }, - "engines": { - "node": "^10 || ^12 || >=14.0" - }, - "peerDependencies": { - "postcss": "^8.2.15" + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-6.1.2.tgz", + "integrity": "sha512-1C0C+eNaeN8OcHQa193aRgYexyJtU8XwbdieEjClw+J9d94E41LwT6ivKH0WT+fYwYWB0Zp3I3IZ7tI/BbUbrg==", + "license": "MIT", + "dependencies": { + "browserslist": "^4.23.0", + "css-declaration-sorter": "^7.2.0", + "cssnano-utils": "^4.0.2", + "postcss-calc": "^9.0.1", + "postcss-colormin": "^6.1.0", + "postcss-convert-values": "^6.1.0", + "postcss-discard-comments": "^6.0.2", + "postcss-discard-duplicates": "^6.0.3", + "postcss-discard-empty": "^6.0.3", + "postcss-discard-overridden": "^6.0.2", + "postcss-merge-longhand": "^6.0.5", + "postcss-merge-rules": "^6.1.1", + "postcss-minify-font-values": "^6.1.0", + "postcss-minify-gradients": "^6.0.3", + "postcss-minify-params": "^6.1.0", + "postcss-minify-selectors": "^6.0.4", + "postcss-normalize-charset": "^6.0.2", + "postcss-normalize-display-values": "^6.0.2", + "postcss-normalize-positions": "^6.0.2", + "postcss-normalize-repeat-style": "^6.0.2", + "postcss-normalize-string": "^6.0.2", + "postcss-normalize-timing-functions": "^6.0.2", + "postcss-normalize-unicode": "^6.1.0", + "postcss-normalize-url": "^6.0.2", + "postcss-normalize-whitespace": "^6.0.2", + "postcss-ordered-values": "^6.0.2", + "postcss-reduce-initial": "^6.1.0", + "postcss-reduce-transforms": "^6.0.2", + "postcss-svgo": "^6.0.3", + "postcss-unique-selectors": "^6.0.4" + }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, + "peerDependencies": { + "postcss": "^8.4.31" } }, "node_modules/cssnano-utils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz", - "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-4.0.2.tgz", + "integrity": "sha512-ZR1jHg+wZ8o4c3zqf1SIUSTIvm/9mU343FMR6Obe/unskbvpGhZOo1J6d/r8D1pzkRQYuwbcH3hToOuoA2G7oQ==", + "license": "MIT", "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/csso": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz", - "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", + "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", + "license": "MIT", "dependencies": { - "css-tree": "^1.1.2" + "css-tree": "~2.2.0" }, "engines": { - "node": ">=8.0.0" + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" } }, + "node_modules/csso/node_modules/css-tree": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz", + "integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==", + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.28", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/csso/node_modules/mdn-data": { + "version": "2.0.28", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", + "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==", + "license": "CC0-1.0" + }, "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", @@ -6336,6 +6620,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -6352,6 +6637,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", "engines": { "node": ">= 0.8", "npm": "1.2.8000 || >= 1.4.16" @@ -6553,7 +6839,8 @@ "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==" + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" }, "node_modules/electron-to-chromium": { "version": "1.4.749", @@ -6588,17 +6875,43 @@ } }, "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==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", "engines": { "node": ">= 0.8" } }, + "node_modules/encoding-sniffer": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.0.tgz", + "integrity": "sha512-ju7Wq1kg04I3HtiYIOrUrdfdDvkyO9s5XM8QAj/bN61Yo/Vb4vgJxy5vi4Yxk01gWHbrofpPtpxM8bKger9jhg==", + "license": "MIT", + "dependencies": { + "iconv-lite": "^0.6.3", + "whatwg-encoding": "^3.1.1" + }, + "funding": { + "url": "https://github.com/fb55/encoding-sniffer?sponsor=1" + } + }, + "node_modules/encoding-sniffer/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==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.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==", + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", + "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -7118,6 +7431,7 @@ "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -7170,36 +7484,37 @@ } }, "node_modules/express": { - "version": "4.19.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", - "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", + "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", + "license": "MIT", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.2", + "body-parser": "1.20.3", "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", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", - "finalhandler": "1.2.0", + "finalhandler": "1.3.1", "fresh": "0.5.2", "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", + "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", + "path-to-regexp": "0.1.10", "proxy-addr": "~2.0.7", - "qs": "6.11.0", + "qs": "6.13.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", + "send": "0.19.0", + "serve-static": "1.16.2", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", @@ -7235,9 +7550,10 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "node_modules/express/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==" + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", + "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==", + "license": "MIT" }, "node_modules/express/node_modules/range-parser": { "version": "1.2.1", @@ -7440,12 +7756,13 @@ } }, "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "license": "MIT", "dependencies": { "debug": "2.6.9", - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "2.4.1", "parseurl": "~1.3.3", @@ -7460,6 +7777,7 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", "dependencies": { "ms": "2.0.0" } @@ -7467,7 +7785,8 @@ "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==" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" }, "node_modules/find-cache-dir": { "version": "4.0.0", @@ -7706,6 +8025,7 @@ "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -8491,6 +8811,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", @@ -8578,6 +8899,7 @@ "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3" }, @@ -8672,9 +8994,10 @@ } }, "node_modules/infima": { - "version": "0.2.0-alpha.43", - "resolved": "https://registry.npmjs.org/infima/-/infima-0.2.0-alpha.43.tgz", - "integrity": "sha512-2uw57LvUqW0rK/SWYnd/2rRfxNA5DDNOh33jxF7fy46VWoNhGxiUQyVZHbBMjQ33mQem0cjdDVwgWVAmlRfgyQ==", + "version": "0.2.0-alpha.44", + "resolved": "https://registry.npmjs.org/infima/-/infima-0.2.0-alpha.44.tgz", + "integrity": "sha512-tuRkUSO/lB3rEhLJk25atwAjgLuzq070+pOW8XcvpHky/YbENnRRdPd85IBkyeTgttmOy5ah+yHYsK1HhUd4lQ==", + "license": "MIT", "engines": { "node": ">=12" } @@ -9012,6 +9335,7 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "license": "MIT", "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", @@ -9028,6 +9352,7 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "license": "MIT", "dependencies": { "@types/node": "*", "jest-util": "^29.7.0", @@ -9042,6 +9367,7 @@ "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -9219,11 +9545,15 @@ } }, "node_modules/lilconfig": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", - "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", + "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" } }, "node_modules/lines-and-columns": { @@ -9279,7 +9609,8 @@ "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==" + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "license": "MIT" }, "node_modules/lodash.merge": { "version": "4.6.2", @@ -9290,7 +9621,8 @@ "node_modules/lodash.uniq": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==" + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", + "license": "MIT" }, "node_modules/longest-streak": { "version": "3.1.0", @@ -9753,14 +10085,16 @@ } }, "node_modules/mdn-data": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz", - "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==" + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", + "license": "CC0-1.0" }, "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==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -9777,9 +10111,13 @@ } }, "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==" + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/merge-stream": { "version": "2.0.0", @@ -11474,11 +11812,11 @@ ] }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { @@ -11489,6 +11827,7 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", "bin": { "mime": "cli.js" }, @@ -11689,17 +12028,6 @@ "node": ">=0.10.0" } }, - "node_modules/normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/npm-run-path": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", @@ -11714,7 +12042,8 @@ "node_modules/nprogress": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz", - "integrity": "sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==" + "integrity": "sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==", + "license": "MIT" }, "node_modules/nth-check": { "version": "2.1.1", @@ -11736,9 +12065,13 @@ } }, "node_modules/object-inspect": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -11777,6 +12110,7 @@ "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", "dependencies": { "ee-first": "1.1.1" }, @@ -12031,6 +12365,18 @@ "url": "https://github.com/inikulin/parse5?sponsor=1" } }, + "node_modules/parse5-parser-stream": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz", + "integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==", + "license": "MIT", + "dependencies": { + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -12083,9 +12429,10 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "node_modules/path-to-regexp": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", - "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.9.0.tgz", + "integrity": "sha512-xIp7/apCFJuUHdDLWe8O1HIkb0kQrOMb/0u6FXQjemHn/ii5LrIzU6bdECnsiTF/GjZkMEKg1xdiZwNqDYlZ6g==", + "license": "MIT", "dependencies": { "isarray": "0.0.1" } @@ -12109,9 +12456,10 @@ } }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", + "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", @@ -12206,9 +12554,9 @@ } }, "node_modules/postcss": { - "version": "8.4.33", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz", - "integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==", + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", "funding": [ { "type": "opencollective", @@ -12223,115 +12571,127 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" } }, "node_modules/postcss-calc": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz", - "integrity": "sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-9.0.1.tgz", + "integrity": "sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==", + "license": "MIT", "dependencies": { - "postcss-selector-parser": "^6.0.9", + "postcss-selector-parser": "^6.0.11", "postcss-value-parser": "^4.2.0" }, + "engines": { + "node": "^14 || ^16 || >=18.0" + }, "peerDependencies": { "postcss": "^8.2.2" } }, "node_modules/postcss-colormin": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-5.3.1.tgz", - "integrity": "sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-6.1.0.tgz", + "integrity": "sha512-x9yX7DOxeMAR+BgGVnNSAxmAj98NX/YxEMNFP+SDCEeNLb2r3i6Hh1ksMsnW8Ub5SLCpbescQqn9YEbE9554Sw==", + "license": "MIT", "dependencies": { - "browserslist": "^4.21.4", + "browserslist": "^4.23.0", "caniuse-api": "^3.0.0", - "colord": "^2.9.1", + "colord": "^2.9.3", "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-convert-values": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.3.tgz", - "integrity": "sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-6.1.0.tgz", + "integrity": "sha512-zx8IwP/ts9WvUM6NkVSkiU902QZL1bwPhaVaLynPtCsOTqp+ZKbNi+s6XJg3rfqpKGA/oc7Oxk5t8pOQJcwl/w==", + "license": "MIT", "dependencies": { - "browserslist": "^4.21.4", + "browserslist": "^4.23.0", "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-discard-comments": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz", - "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-6.0.2.tgz", + "integrity": "sha512-65w/uIqhSBBfQmYnG92FO1mWZjJ4GL5b8atm5Yw2UgrwD7HiNiSSNwJor1eCFGzUgYnN/iIknhNRVqjrrpuglw==", + "license": "MIT", "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-discard-duplicates": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz", - "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-6.0.3.tgz", + "integrity": "sha512-+JA0DCvc5XvFAxwx6f/e68gQu/7Z9ud584VLmcgto28eB8FqSFZwtrLwB5Kcp70eIoWP/HXqz4wpo8rD8gpsTw==", + "license": "MIT", "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-discard-empty": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz", - "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-6.0.3.tgz", + "integrity": "sha512-znyno9cHKQsK6PtxL5D19Fj9uwSzC2mB74cpT66fhgOadEUPyXFkbgwm5tvc3bt3NAy8ltE5MrghxovZRVnOjQ==", + "license": "MIT", "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-discard-overridden": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz", - "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-6.0.2.tgz", + "integrity": "sha512-j87xzI4LUggC5zND7KdjsI25APtyMuynXZSujByMaav2roV6OZX+8AaCUcZSWqckZpjAjRyFDdpqybgjFO0HJQ==", + "license": "MIT", "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-discard-unused": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-5.1.0.tgz", - "integrity": "sha512-KwLWymI9hbwXmJa0dkrzpRbSJEh0vVUd7r8t0yOGPcfKzyJJxFM8kLyC5Ev9avji6nY95pOp1W6HqIrfT+0VGw==", + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-6.0.5.tgz", + "integrity": "sha512-wHalBlRHkaNnNwfC8z+ppX57VhvS+HWgjW508esjdaEYr3Mx7Gnn2xA4R/CKf5+Z9S5qsqC+Uzh4ueENWwCVUA==", + "license": "MIT", "dependencies": { - "postcss-selector-parser": "^6.0.5" + "postcss-selector-parser": "^6.0.16" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-loader": { @@ -12355,136 +12715,118 @@ "webpack": "^5.0.0" } }, - "node_modules/postcss-loader/node_modules/cosmiconfig": { - "version": "8.3.6", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", - "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", - "dependencies": { - "import-fresh": "^3.3.0", - "js-yaml": "^4.1.0", - "parse-json": "^5.2.0", - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" - }, - "peerDependencies": { - "typescript": ">=4.9.5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, "node_modules/postcss-merge-idents": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-5.1.1.tgz", - "integrity": "sha512-pCijL1TREiCoog5nQp7wUe+TUonA2tC2sQ54UGeMmryK3UFGIYKqDyjnqd6RcuI4znFn9hWSLNN8xKE/vWcUQw==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-6.0.3.tgz", + "integrity": "sha512-1oIoAsODUs6IHQZkLQGO15uGEbK3EAl5wi9SS8hs45VgsxQfMnxvt+L+zIr7ifZFIH14cfAeVe2uCTa+SPRa3g==", + "license": "MIT", "dependencies": { - "cssnano-utils": "^3.1.0", + "cssnano-utils": "^4.0.2", "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-merge-longhand": { - "version": "5.1.7", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.7.tgz", - "integrity": "sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==", + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-6.0.5.tgz", + "integrity": "sha512-5LOiordeTfi64QhICp07nzzuTDjNSO8g5Ksdibt44d+uvIIAE1oZdRn8y/W5ZtYgRH/lnLDlvi9F8btZcVzu3w==", + "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0", - "stylehacks": "^5.1.1" + "stylehacks": "^6.1.1" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-merge-rules": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-5.1.4.tgz", - "integrity": "sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-6.1.1.tgz", + "integrity": "sha512-KOdWF0gju31AQPZiD+2Ar9Qjowz1LTChSjFFbS+e2sFgc4uHOp3ZvVX4sNeTlk0w2O31ecFGgrFzhO0RSWbWwQ==", + "license": "MIT", "dependencies": { - "browserslist": "^4.21.4", + "browserslist": "^4.23.0", "caniuse-api": "^3.0.0", - "cssnano-utils": "^3.1.0", - "postcss-selector-parser": "^6.0.5" + "cssnano-utils": "^4.0.2", + "postcss-selector-parser": "^6.0.16" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-minify-font-values": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz", - "integrity": "sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-6.1.0.tgz", + "integrity": "sha512-gklfI/n+9rTh8nYaSJXlCo3nOKqMNkxuGpTn/Qm0gstL3ywTr9/WRKznE+oy6fvfolH6dF+QM4nCo8yPLdvGJg==", + "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-minify-gradients": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz", - "integrity": "sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-6.0.3.tgz", + "integrity": "sha512-4KXAHrYlzF0Rr7uc4VrfwDJ2ajrtNEpNEuLxFgwkhFZ56/7gaE4Nr49nLsQDZyUe+ds+kEhf+YAUolJiYXF8+Q==", + "license": "MIT", "dependencies": { - "colord": "^2.9.1", - "cssnano-utils": "^3.1.0", + "colord": "^2.9.3", + "cssnano-utils": "^4.0.2", "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-minify-params": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.4.tgz", - "integrity": "sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-6.1.0.tgz", + "integrity": "sha512-bmSKnDtyyE8ujHQK0RQJDIKhQ20Jq1LYiez54WiaOoBtcSuflfK3Nm596LvbtlFcpipMjgClQGyGr7GAs+H1uA==", + "license": "MIT", "dependencies": { - "browserslist": "^4.21.4", - "cssnano-utils": "^3.1.0", + "browserslist": "^4.23.0", + "cssnano-utils": "^4.0.2", "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-minify-selectors": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz", - "integrity": "sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==", + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-6.0.4.tgz", + "integrity": "sha512-L8dZSwNLgK7pjTto9PzWRoMbnLq5vsZSTu8+j1P/2GB8qdtGQfn+K1uSvFgYvgh83cbyxT5m43ZZhUMTJDSClQ==", + "license": "MIT", "dependencies": { - "postcss-selector-parser": "^6.0.5" + "postcss-selector-parser": "^6.0.16" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-modules-extract-imports": { @@ -12543,192 +12885,205 @@ } }, "node_modules/postcss-normalize-charset": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz", - "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-6.0.2.tgz", + "integrity": "sha512-a8N9czmdnrjPHa3DeFlwqst5eaL5W8jYu3EBbTTkI5FHkfMhFZh1EGbku6jhHhIzTA6tquI2P42NtZ59M/H/kQ==", + "license": "MIT", "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-normalize-display-values": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz", - "integrity": "sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-6.0.2.tgz", + "integrity": "sha512-8H04Mxsb82ON/aAkPeq8kcBbAtI5Q2a64X/mnRRfPXBq7XeogoQvReqxEfc0B4WPq1KimjezNC8flUtC3Qz6jg==", + "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-normalize-positions": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz", - "integrity": "sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-6.0.2.tgz", + "integrity": "sha512-/JFzI441OAB9O7VnLA+RtSNZvQ0NCFZDOtp6QPFo1iIyawyXg0YI3CYM9HBy1WvwCRHnPep/BvI1+dGPKoXx/Q==", + "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-normalize-repeat-style": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz", - "integrity": "sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-6.0.2.tgz", + "integrity": "sha512-YdCgsfHkJ2jEXwR4RR3Tm/iOxSfdRt7jplS6XRh9Js9PyCR/aka/FCb6TuHT2U8gQubbm/mPmF6L7FY9d79VwQ==", + "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-normalize-string": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz", - "integrity": "sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-6.0.2.tgz", + "integrity": "sha512-vQZIivlxlfqqMp4L9PZsFE4YUkWniziKjQWUtsxUiVsSSPelQydwS8Wwcuw0+83ZjPWNTl02oxlIvXsmmG+CiQ==", + "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-normalize-timing-functions": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz", - "integrity": "sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-6.0.2.tgz", + "integrity": "sha512-a+YrtMox4TBtId/AEwbA03VcJgtyW4dGBizPl7e88cTFULYsprgHWTbfyjSLyHeBcK/Q9JhXkt2ZXiwaVHoMzA==", + "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-normalize-unicode": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.1.tgz", - "integrity": "sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-6.1.0.tgz", + "integrity": "sha512-QVC5TQHsVj33otj8/JD869Ndr5Xcc/+fwRh4HAsFsAeygQQXm+0PySrKbr/8tkDKzW+EVT3QkqZMfFrGiossDg==", + "license": "MIT", "dependencies": { - "browserslist": "^4.21.4", + "browserslist": "^4.23.0", "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-normalize-url": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz", - "integrity": "sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-6.0.2.tgz", + "integrity": "sha512-kVNcWhCeKAzZ8B4pv/DnrU1wNh458zBNp8dh4y5hhxih5RZQ12QWMuQrDgPRw3LRl8mN9vOVfHl7uhvHYMoXsQ==", + "license": "MIT", "dependencies": { - "normalize-url": "^6.0.1", "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-normalize-whitespace": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz", - "integrity": "sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-6.0.2.tgz", + "integrity": "sha512-sXZ2Nj1icbJOKmdjXVT9pnyHQKiSAyuNQHSgRCUgThn2388Y9cGVDR+E9J9iAYbSbLHI+UUwLVl1Wzco/zgv0Q==", + "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-ordered-values": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz", - "integrity": "sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-6.0.2.tgz", + "integrity": "sha512-VRZSOB+JU32RsEAQrO94QPkClGPKJEL/Z9PCBImXMhIeK5KAYo6slP/hBYlLgrCjFxyqvn5VC81tycFEDBLG1Q==", + "license": "MIT", "dependencies": { - "cssnano-utils": "^3.1.0", + "cssnano-utils": "^4.0.2", "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-reduce-idents": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-5.2.0.tgz", - "integrity": "sha512-BTrLjICoSB6gxbc58D5mdBK8OhXRDqud/zodYfdSi52qvDHdMwk+9kB9xsM8yJThH/sZU5A6QVSmMmaN001gIg==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-6.0.3.tgz", + "integrity": "sha512-G3yCqZDpsNPoQgbDUy3T0E6hqOQ5xigUtBQyrmq3tn2GxlyiL0yyl7H+T8ulQR6kOcHJ9t7/9H4/R2tv8tJbMA==", + "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-reduce-initial": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-5.1.2.tgz", - "integrity": "sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-6.1.0.tgz", + "integrity": "sha512-RarLgBK/CrL1qZags04oKbVbrrVK2wcxhvta3GCxrZO4zveibqbRPmm2VI8sSgCXwoUHEliRSbOfpR0b/VIoiw==", + "license": "MIT", "dependencies": { - "browserslist": "^4.21.4", + "browserslist": "^4.23.0", "caniuse-api": "^3.0.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-reduce-transforms": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz", - "integrity": "sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-6.0.2.tgz", + "integrity": "sha512-sB+Ya++3Xj1WaT9+5LOOdirAxP7dJZms3GRcYheSPi1PiTMigsxHAdkrbItHxwYHr4kt1zL7mmcHstgMYT+aiA==", + "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-selector-parser": { - "version": "6.0.15", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz", - "integrity": "sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "license": "MIT", "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -12738,46 +13093,49 @@ } }, "node_modules/postcss-sort-media-queries": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/postcss-sort-media-queries/-/postcss-sort-media-queries-4.4.1.tgz", - "integrity": "sha512-QDESFzDDGKgpiIh4GYXsSy6sek2yAwQx1JASl5AxBtU1Lq2JfKBljIPNdil989NcSKRQX1ToiaKphImtBuhXWw==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/postcss-sort-media-queries/-/postcss-sort-media-queries-5.2.0.tgz", + "integrity": "sha512-AZ5fDMLD8SldlAYlvi8NIqo0+Z8xnXU2ia0jxmuhxAU+Lqt9K+AlmLNJ/zWEnE9x+Zx3qL3+1K20ATgNOr3fAA==", + "license": "MIT", "dependencies": { - "sort-css-media-queries": "2.1.0" + "sort-css-media-queries": "2.2.0" }, "engines": { - "node": ">=10.0.0" + "node": ">=14.0.0" }, "peerDependencies": { - "postcss": "^8.4.16" + "postcss": "^8.4.23" } }, "node_modules/postcss-svgo": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.1.0.tgz", - "integrity": "sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-6.0.3.tgz", + "integrity": "sha512-dlrahRmxP22bX6iKEjOM+c8/1p+81asjKT+V5lrgOH944ryx/OHpclnIbGsKVd3uWOXFLYJwCVf0eEkJGvO96g==", + "license": "MIT", "dependencies": { "postcss-value-parser": "^4.2.0", - "svgo": "^2.7.0" + "svgo": "^3.2.0" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >= 18" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-unique-selectors": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz", - "integrity": "sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==", + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-6.0.4.tgz", + "integrity": "sha512-K38OCaIrO8+PzpArzkLKB42dSARtC2tmG6PvD4b1o1Q2E9Os8jzfWFfSy/rixsHwohtsDdFtAWGjFVFUdwYaMg==", + "license": "MIT", "dependencies": { - "postcss-selector-parser": "^6.0.5" + "postcss-selector-parser": "^6.0.16" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/postcss-value-parser": { @@ -12786,14 +13144,15 @@ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, "node_modules/postcss-zindex": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-5.1.0.tgz", - "integrity": "sha512-fgFMf0OtVSBR1va1JNHYgMxYk73yhn/qb4uQDq1DLGYolz8gHCyr/sesEuGUaYs58E3ZJRcpoGuPVoB7Meiq9A==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-6.0.2.tgz", + "integrity": "sha512-5BxW9l1evPB/4ZIc+2GobEBoKC+h8gPGCMi+jxsYvd2x0mjq7wazk6DrP71pStqxE9Foxh5TVnonbWpFZzXaYg==", + "license": "MIT", "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/prelude-ls": { @@ -12823,9 +13182,10 @@ } }, "node_modules/prism-react-renderer": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-2.3.1.tgz", - "integrity": "sha512-Rdf+HzBLR7KYjzpJ1rSoxT9ioO85nZngQEoFIhL07XhtJHlCU3SOz0GJ6+qvMyQe0Se+BV3qpe6Yd/NmQF5Juw==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/prism-react-renderer/-/prism-react-renderer-2.4.0.tgz", + "integrity": "sha512-327BsVCD/unU4CNLZTWVHyUHKnsqcvj2qbPlQ8MiBE2eq2rgctjigPA1Gp9HLF83kZ20zNN6jgizHJeEsyFYOw==", + "license": "MIT", "dependencies": { "@types/prismjs": "^1.26.0", "clsx": "^2.0.0" @@ -12838,6 +13198,7 @@ "version": "1.29.0", "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==", + "license": "MIT", "engines": { "node": ">=6" } @@ -12923,11 +13284,12 @@ } }, "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.0.4" + "side-channel": "^1.0.6" }, "engines": { "node": ">=0.6" @@ -12994,6 +13356,7 @@ "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==", + "license": "MIT", "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", @@ -13008,6 +13371,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -13099,9 +13463,10 @@ } }, "node_modules/react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", "dependencies": { "loose-envify": "^1.1.0" }, @@ -13228,15 +13593,16 @@ } }, "node_modules/react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", "dependencies": { "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" + "scheduler": "^0.23.2" }, "peerDependencies": { - "react": "^18.2.0" + "react": "^18.3.1" } }, "node_modules/react-error-overlay": { @@ -13271,9 +13637,10 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, "node_modules/react-json-view-lite": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/react-json-view-lite/-/react-json-view-lite-1.3.0.tgz", - "integrity": "sha512-aN1biKC5v4DQkmQBlZjuMFR09MKZGMPtIg+cut8zEeg2HXd6gl2gRy0n4HMacHf0dznQgo0SVXN7eT8zV3hEuQ==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/react-json-view-lite/-/react-json-view-lite-1.5.0.tgz", + "integrity": "sha512-nWqA1E4jKPklL2jvHWs6s+7Na0qNgw9HCP6xehdQJeg6nPBTFZgGwyko9Q0oj+jQWKTTVRS30u0toM5wiuL3iw==", + "license": "MIT", "engines": { "node": ">=14" }, @@ -13283,12 +13650,12 @@ }, "node_modules/react-loadable": { "name": "@docusaurus/react-loadable", - "version": "5.5.2", - "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-5.5.2.tgz", - "integrity": "sha512-A3dYjdBGuy0IGT+wyLIGIKLRE+sAk1iNk0f1HjNDysO7u8lhL4N3VEm+FAubmJbAztn94F7MxBTPmnixbiyFdQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-6.0.0.tgz", + "integrity": "sha512-YMMxTUQV/QFSnbgrP3tjDzLHRg7vsbMn8e9HAa8o/1iXoiomo48b7sk/kkmWEuWNDPJVlKSJRB6Y2fHqdJk+SQ==", + "license": "MIT", "dependencies": { - "@types/react": "*", - "prop-types": "^15.6.2" + "@types/react": "*" }, "peerDependencies": { "react": "*" @@ -13838,9 +14205,10 @@ "integrity": "sha512-PGMBq03+TTG/p/cRB7HCLKJ1MgDIi07+QU1faSjiYRfmY5UsAttV9Hs08jDAHVwcOwmVLcSJkpwyfXszVjWfIQ==" }, "node_modules/rtlcss": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-4.1.1.tgz", - "integrity": "sha512-/oVHgBtnPNcggP2aVXQjSy6N1mMAfHg4GSag0QtZBlD5bdDgAHwr4pydqJGd+SUCu9260+Pjqbjwtvu7EMH1KQ==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-4.3.0.tgz", + "integrity": "sha512-FI+pHEn7Wc4NqKXMXFM+VAYKEj/mRIcW4h24YVwVtyjI+EqGrLc2Hx/Ny0lrZ21cBWU2goLy36eqMcNj3AQJig==", + "license": "MIT", "dependencies": { "escalade": "^3.1.1", "picocolors": "^1.0.0", @@ -13898,7 +14266,8 @@ "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" }, "node_modules/sax": { "version": "1.3.0", @@ -13906,9 +14275,10 @@ "integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==" }, "node_modules/scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", "dependencies": { "loose-envify": "^1.1.0" } @@ -13932,9 +14302,10 @@ } }, "node_modules/search-insights": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.13.0.tgz", - "integrity": "sha512-Orrsjf9trHHxFRuo9/rzm0KIWmgzE8RMlZMzuhZOJ01Rnz3D0YBAe+V6473t6/H6c7irs6Lt48brULAiRWb3Vw==", + "version": "2.17.2", + "resolved": "https://registry.npmjs.org/search-insights/-/search-insights-2.17.2.tgz", + "integrity": "sha512-zFNpOpUO+tY2D85KrxJ+aqwnIfdEGi06UH2+xEb+Bp9Mwznmauqc9djbnBibJO5mpfUPPa8st6Sx65+vbeO45g==", + "license": "MIT", "peer": true }, "node_modules/section-matter": { @@ -14011,9 +14382,10 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "license": "MIT", "dependencies": { "debug": "2.6.9", "depd": "2.0.0", @@ -14037,6 +14409,7 @@ "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", "dependencies": { "ms": "2.0.0" } @@ -14044,17 +14417,29 @@ "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==" + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } }, "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==" + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" }, "node_modules/send/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==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -14158,14 +14543,15 @@ } }, "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==", + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "license": "MIT", "dependencies": { - "encodeurl": "~1.0.2", + "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", - "send": "0.18.0" + "send": "0.19.0" }, "engines": { "node": ">= 0.8.0" @@ -14190,7 +14576,8 @@ "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" }, "node_modules/shallow-clone": { "version": "3.0.1", @@ -14255,6 +14642,7 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "license": "MIT", "dependencies": { "call-bind": "^1.0.7", "es-errors": "^1.3.0", @@ -14292,9 +14680,10 @@ "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" }, "node_modules/sitemap": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-7.1.1.tgz", - "integrity": "sha512-mK3aFtjz4VdJN0igpIJrinf3EO8U8mxOPsTBzSsy06UtjZQJ3YY3o3Xa7zSc5nMqcMrRwlChHZ18Kxg0caiPBg==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/sitemap/-/sitemap-7.1.2.tgz", + "integrity": "sha512-ARCqzHJ0p4gWt+j7NlU5eDlIO9+Rkr/JhPFZKKQ1l5GCus7rJH4UdrlVAh0xC/gDS/Qir2UMxqYNHtsKr2rpCw==", + "license": "MIT", "dependencies": { "@types/node": "^17.0.5", "@types/sax": "^1.2.1", @@ -14312,7 +14701,8 @@ "node_modules/sitemap/node_modules/@types/node": { "version": "17.0.45", "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz", - "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==" + "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==", + "license": "MIT" }, "node_modules/skin-tone": { "version": "2.0.0", @@ -14333,6 +14723,16 @@ "node": ">=8" } }, + "node_modules/snake-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", + "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "license": "MIT", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/sockjs": { "version": "0.3.24", "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", @@ -14344,9 +14744,10 @@ } }, "node_modules/sort-css-media-queries": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/sort-css-media-queries/-/sort-css-media-queries-2.1.0.tgz", - "integrity": "sha512-IeWvo8NkNiY2vVYdPa27MCQiR0MN0M80johAYFVxWWXQ44KU84WNxjslwBHmc/7ZL2ccwkM7/e6S5aiKZXm7jA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/sort-css-media-queries/-/sort-css-media-queries-2.2.0.tgz", + "integrity": "sha512-0xtkGhWCC9MGt/EzgnvbbbKhqWjl1+/rncmhTh5qCpbYguXh6S/qwePfv/JQ8jePXXmqingylxoC49pCkSPIbA==", + "license": "MIT", "engines": { "node": ">= 6.3.0" } @@ -14360,9 +14761,10 @@ } }, "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==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -14437,16 +14839,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/stable": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", - "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", - "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility" - }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -14578,18 +14975,19 @@ } }, "node_modules/stylehacks": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", - "integrity": "sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-6.1.1.tgz", + "integrity": "sha512-gSTTEQ670cJNoaeIp9KX6lZmm8LJ3jPB5yJmX8Zq/wQxOsAFXV3qjWzHas3YYk1qesuVIyYWWUpZ0vSE/dTSGg==", + "license": "MIT", "dependencies": { - "browserslist": "^4.21.4", - "postcss-selector-parser": "^6.0.4" + "browserslist": "^4.23.0", + "postcss-selector-parser": "^6.0.16" }, "engines": { - "node": "^10 || ^12 || >=14.0" + "node": "^14 || ^16 || >=18.0" }, "peerDependencies": { - "postcss": "^8.2.15" + "postcss": "^8.4.31" } }, "node_modules/supports-color": { @@ -14617,99 +15015,43 @@ "node_modules/svg-parser": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz", - "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==" + "integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==", + "license": "MIT" }, "node_modules/svgo": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz", - "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.2.tgz", + "integrity": "sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==", + "license": "MIT", "dependencies": { "@trysound/sax": "0.2.0", "commander": "^7.2.0", - "css-select": "^4.1.3", - "css-tree": "^1.1.3", - "csso": "^4.2.0", - "picocolors": "^1.0.0", - "stable": "^0.1.8" + "css-select": "^5.1.0", + "css-tree": "^2.3.1", + "css-what": "^6.1.0", + "csso": "^5.0.5", + "picocolors": "^1.0.0" }, "bin": { "svgo": "bin/svgo" }, "engines": { - "node": ">=10.13.0" + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/svgo" } }, "node_modules/svgo/node_modules/commander": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "license": "MIT", "engines": { "node": ">= 10" } }, - "node_modules/svgo/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==", - "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/svgo/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==", - "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/svgo/node_modules/domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", - "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/svgo/node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "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/svgo/node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, "node_modules/tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", @@ -14888,6 +15230,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", "engines": { "node": ">=0.6" } @@ -14983,6 +15326,7 @@ "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" @@ -14995,6 +15339,7 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -15003,6 +15348,7 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", "dependencies": { "mime-db": "1.52.0" }, @@ -15019,9 +15365,10 @@ } }, "node_modules/typescript": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", - "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", + "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -15030,6 +15377,15 @@ "node": ">=14.17" } }, + "node_modules/undici": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.19.8.tgz", + "integrity": "sha512-U8uCCl2x9TK3WANvmBavymRzxbfFYG+tAu+fgx3zxQy3qdagQqBLwJVrdyO1TBfUXvfKveMKJZhpvUYoOjM+4g==", + "license": "MIT", + "engines": { + "node": ">=18.17" + } + }, "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", @@ -15211,6 +15567,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -15508,9 +15865,9 @@ } }, "node_modules/watchpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", + "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" @@ -15537,25 +15894,24 @@ } }, "node_modules/webpack": { - "version": "5.90.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.90.0.tgz", - "integrity": "sha512-bdmyXRCXeeNIePv6R6tGPyy20aUobw4Zy8r0LUS2EWO+U+Ke/gYDgsCh7bl5rB6jPpr4r0SZa6dPxBxLooDT3w==", + "version": "5.94.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz", + "integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==", "dependencies": { - "@types/eslint-scope": "^3.7.3", "@types/estree": "^1.0.5", - "@webassemblyjs/ast": "^1.11.5", - "@webassemblyjs/wasm-edit": "^1.11.5", - "@webassemblyjs/wasm-parser": "^1.11.5", + "@webassemblyjs/ast": "^1.12.1", + "@webassemblyjs/wasm-edit": "^1.12.1", + "@webassemblyjs/wasm-parser": "^1.12.1", "acorn": "^8.7.1", - "acorn-import-assertions": "^1.9.0", + "acorn-import-attributes": "^1.9.5", "browserslist": "^4.21.10", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.15.0", + "enhanced-resolve": "^5.17.1", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.9", + "graceful-fs": "^4.2.11", "json-parse-even-better-errors": "^2.3.1", "loader-runner": "^4.2.0", "mime-types": "^2.1.27", @@ -15563,7 +15919,7 @@ "schema-utils": "^3.2.0", "tapable": "^2.1.1", "terser-webpack-plugin": "^5.3.10", - "watchpack": "^2.4.0", + "watchpack": "^2.4.1", "webpack-sources": "^3.2.3" }, "bin": { @@ -15866,6 +16222,39 @@ "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==", + "license": "MIT", + "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==", + "license": "MIT", + "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==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/docs/package.json b/docs/package.json index e82a99814f..b9078b15ce 100644 --- a/docs/package.json +++ b/docs/package.json @@ -15,25 +15,25 @@ "typecheck": "tsc" }, "dependencies": { - "@docusaurus/core": "^3.2.1", - "@docusaurus/preset-classic": "^3.2.1", - "@easyops-cn/docusaurus-search-local": "^0.40.1", - "@mdx-js/react": "^3.0.0", - "clsx": "^2.0.0", - "prism-react-renderer": "^2.3.0", + "@docusaurus/core": "^3.5.2", + "@docusaurus/preset-classic": "^3.5.2", + "@easyops-cn/docusaurus-search-local": "^0.44.5", + "@mdx-js/react": "^3.0.1", + "clsx": "^2.1.1", + "prism-react-renderer": "^2.4.0", "raw-loader": "^4.0.2", - "react": "^18.0.0", - "react-dom": "^18.0.0" + "react": "^18.3.1", + "react-dom": "^18.3.1" }, "devDependencies": { - "@docusaurus/eslint-plugin": "^3.2.1", - "@docusaurus/module-type-aliases": "^3.2.1", - "@docusaurus/tsconfig": "^3.2.1", - "@docusaurus/types": "^3.2.1", + "@docusaurus/eslint-plugin": "^3.5.2", + "@docusaurus/module-type-aliases": "^3.5.2", + "@docusaurus/tsconfig": "^3.5.2", + "@docusaurus/types": "^3.5.2", "@typescript-eslint/eslint-plugin": "^6.20.0", "@typescript-eslint/parser": "^6.20.0", "eslint": "^8.56.0", - "typescript": "~5.2.2" + "typescript": "~5.6.2" }, "browserslist": { "production": [ diff --git a/docs/sidebars.ts b/docs/sidebars.ts index 35442be736..126df5d848 100644 --- a/docs/sidebars.ts +++ b/docs/sidebars.ts @@ -76,6 +76,7 @@ const sidebars: SidebarsConfig = { 'guides/database-backup', 'guides/profiles', 'guides/object-storage', + 'guides/sso', 'guides/check', 'guides/resource-manager', 'guides/managing-api-tokens', diff --git a/frontend/app/.eslintignore b/frontend/app/.eslintignore deleted file mode 100644 index 280b20d9e6..0000000000 --- a/frontend/app/.eslintignore +++ /dev/null @@ -1,3 +0,0 @@ -dist -node_modules -playwright-report diff --git a/frontend/app/.eslintrc b/frontend/app/.eslintrc deleted file mode 100644 index 81da3d72ac..0000000000 --- a/frontend/app/.eslintrc +++ /dev/null @@ -1,78 +0,0 @@ -{ - "env": { - "browser": true, - "node": true, - "es2021": true - }, - "settings": { - "react": { - "version": "detect" - } - }, - "extends": [ - "eslint:recommended", - "plugin:react/recommended", - "plugin:cypress/recommended", - // "plugin:prettier/recommended", - "prettier" - ], - "parser": "@typescript-eslint/parser", - "plugins": [ - "@typescript-eslint", - "unused-imports" - ], - "ignorePatterns": [ - "tsconfig.json" - ], - "rules": { - "react/prop-types": "off", - "quotes": [ - "error", - "double", - { - "avoidEscape": true - } - ], - "array-bracket-spacing": [ - "error", - "never" - ], - "no-return-assign": [ - 0 - ], - "no-underscore-dangle": [ - 0 - ], - "no-tabs": [ - 0 - ], - "no-confusing-arrow": [ - 0 - ], - "no-case-declarations": [ - 0 - ], - "react/display-name": [ - 0 - ], - "import/no-relative-packages": [ - 0 - ], - "react/react-in-jsx-scope": "off", - "@typescript-eslint/no-unused-vars": [ - "error", - { - "ignoreRestSiblings": true - } - ], - "no-unused-vars": [ - "error", - { - "ignoreRestSiblings": true - } - ], - "unused-imports/no-unused-imports-ts": 2, - "semi": "error", - "no-trailing-spaces": "error" - } -} \ No newline at end of file diff --git a/frontend/app/.prettierignore b/frontend/app/.prettierignore deleted file mode 100644 index d97329607c..0000000000 --- a/frontend/app/.prettierignore +++ /dev/null @@ -1,3 +0,0 @@ -tsconfig.json -dist -playwright-report diff --git a/frontend/app/.prettierrc.json b/frontend/app/.prettierrc.json deleted file mode 100644 index 54bed9944e..0000000000 --- a/frontend/app/.prettierrc.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "semi": true, - "tabWidth": 2, - "printWidth": 100, - "singleQuote": false, - "trailingComma": "es5", - "bracketSameLine": true -} diff --git a/frontend/app/biome.json b/frontend/app/biome.json new file mode 100644 index 0000000000..27a91dd865 --- /dev/null +++ b/frontend/app/biome.json @@ -0,0 +1,122 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.9.3/schema.json", + "vcs": { "enabled": false, "clientKind": "git", "useIgnoreFile": false }, + "files": { + "ignoreUnknown": false, + "ignore": [ + "./coverage", + "./dist", + "./playwright-report", + "./src/generated", + "./test-results", + "./tests/e2e/.auth" + ] + }, + "formatter": { + "enabled": true, + "useEditorconfig": true, + "formatWithErrors": false, + "indentStyle": "space", + "indentWidth": 2, + "lineEnding": "lf", + "lineWidth": 100, + "attributePosition": "auto", + "bracketSpacing": true + }, + "organizeImports": { "enabled": true }, + "linter": { + "enabled": true, + "rules": { + "all": true, + "a11y": { + "noSvgWithoutTitle": "off", + "useAriaPropsForRole": "off", + "useButtonType": "off", + "useKeyWithClickEvents": "off", + "useSemanticElements": "off" + }, + "complexity": { + "noBannedTypes": "off", + "noExcessiveCognitiveComplexity": "off", + "noForEach": "off", + "noUselessFragments": "off", + "noUselessSwitchCase": "off", + "noUselessThisAlias": "off", + "useArrowFunction": "off", + "useDateNow": "off", + "useOptionalChain": "off", + "useSimplifiedLogicExpression": "off" + }, + "correctness": { + "noSwitchDeclarations": "off", + "noUndeclaredDependencies": "off", + "noUndeclaredVariables": "off", + "noUnusedFunctionParameters": "off", + "noUnusedImports": "off", + "noUnusedVariables": "off", + "useExhaustiveDependencies": "off", + "useHookAtTopLevel": "off", + "useImportExtensions": "off" + }, + "performance": { + "noAccumulatingSpread": "off", + "useTopLevelRegex": "off" + }, + "style": { + "noArguments": "off", + "noDefaultExport": "off", + "noImplicitBoolean": "off", + "noInferrableTypes": "off", + "noNamespace": "off", + "noNamespaceImport": "off", + "noNegationElse": "off", + "noNonNullAssertion": "off", + "noUselessElse": "off", + "useBlockStatements": "off", + "useConsistentArrayType": "off", + "useConsistentBuiltinInstantiation": "off", + "useConst": "off", + "useDefaultSwitchClause": "off", + "useEnumInitializers": "off", + "useExplicitLengthCheck": "off", + "useFilenamingConvention": "off", + "useImportType": "off", + "useNamingConvention": "off", + "useNumberNamespace": "off", + "useSelfClosingElements": "off", + "useShorthandArrayType": "off", + "useSingleCaseStatement": "off", + "useTemplate": "off" + }, + "suspicious": { + "noArrayIndexKey": "off", + "noAssignInExpressions": "off", + "noConsole": "off", + "noConsoleLog": "off", + "noDoubleEquals": "off", + "noEmptyBlock": "off", + "noEmptyBlockStatements": "off", + "noEvolvingTypes": "off", + "noExplicitAny": "off", + "noExportsInTest": "off", + "noGlobalIsNan": "off", + "noImplicitAnyLet": "off", + "noReactSpecificProps": "off", + "useAwait": "off" + } + } + }, + "javascript": { + "formatter": { + "jsxQuoteStyle": "double", + "quoteProperties": "asNeeded", + "trailingCommas": "es5", + "semicolons": "always", + "arrowParentheses": "always", + "bracketSameLine": false, + "quoteStyle": "double", + "attributePosition": "auto", + "bracketSpacing": true + } + } +} diff --git a/frontend/app/cypress/support/component.tsx b/frontend/app/cypress/support/component.tsx index 976faec040..e603bdff7a 100644 --- a/frontend/app/cypress/support/component.tsx +++ b/frontend/app/cypress/support/component.tsx @@ -60,7 +60,8 @@ Cypress.Commands.add("mount", (component, options = {}) => { options={{ searchStringToObject: queryString.parse, objectToSearchString: queryString.stringify, - }}> + }} + > {component} diff --git a/frontend/app/index.html b/frontend/app/index.html index 9c948e45bf..3492d9dcbc 100644 --- a/frontend/app/index.html +++ b/frontend/app/index.html @@ -14,9 +14,7 @@ - + Infrahub diff --git a/frontend/app/package-lock.json b/frontend/app/package-lock.json index e3ca7b8ecd..b992fc2364 100644 --- a/frontend/app/package-lock.json +++ b/frontend/app/package-lock.json @@ -18,8 +18,8 @@ "@headlessui/react": "^1.7.18", "@heroicons/react": "^2.1.3", "@hookform/error-message": "^2.0.1", - "@iconify-icon/react": "^2.0.1", - "@iconify-json/mdi": "^1.1.64", + "@iconify-icon/react": "^2.1.0", + "@iconify-json/mdi": "^1.2.0", "@popperjs/core": "^2.11.8", "@radix-ui/react-dropdown-menu": "^2.0.6", "@radix-ui/react-label": "^2.0.2", @@ -36,12 +36,13 @@ "clsx": "^2.1.0", "cm6-graphql": "^0.0.14", "cm6-theme-basic-light": "^0.2.0", + "cmdk": "^1.0.0", "cross-fetch": "^4.0.0", "date-fns": "^3.6.0", "graphiql": "^3.1.2", "graphql": "^16.8.1", "handlebars": "^4.7.8", - "jotai": "^2.7.2", + "jotai": "^2.10.0", "json-to-graphql-query": "^2.2.5", "prismjs": "^1.29.0", "query-string": "^9.0.0", @@ -69,13 +70,13 @@ "unidiff": "^1.0.4", "use-query-params": "^2.2.1", "vite": "^5.2.8", - "vite-tsconfig-paths": "^4.3.2", - "web-vitals": "^2.1.4" + "vite-tsconfig-paths": "^4.3.2" }, "devDependencies": { + "@biomejs/biome": "1.9.3", "@graphql-codegen/cli": "^5.0.2", "@graphql-codegen/typescript": "^4.0.9", - "@playwright/test": "^1.47.0", + "@playwright/test": "^1.47.2", "@testing-library/react": "^14.2.2", "@types/loadable__component": "^5.13.9", "@types/node": "^20.12.3", @@ -86,42 +87,19 @@ "@types/react-dom": "^18.2.23", "@types/react-test-renderer": "^18.0.7", "@types/sha1": "^1.1.5", - "@typescript-eslint/eslint-plugin": "^7.16.0", "@vitest/coverage-v8": "^1.4.0", - "cypress": "^13.7.2", - "eslint": "^8.57.0", - "eslint-config-prettier": "^9.1.0", - "eslint-plugin-cypress": "^2.15.1", - "eslint-plugin-import": "^2.29.1", - "eslint-plugin-n": "^16.6.2", - "eslint-plugin-promise": "^6.1.1", - "eslint-plugin-react": "^7.34.1", - "eslint-plugin-unused-imports": "^3.2.0", + "cypress": "^13.15.0", "husky": "^8.0.3", "jsdom": "^24.0.0", "lint-staged": "^15.2.10", "openapi-typescript": "^7.0.2", "postcss": "^8.4.23", - "prettier": "2.8.8", - "prettier-eslint": "^16.3.0", - "pretty-quick": "^3.1.3", "react-test-renderer": "^18.2.0", "tailwindcss": "^3.4.3", - "ts-node": "^10.9.2", "typescript": "^5.5.3", - "typescript-eslint": "^7.16.0", "vitest": "^1.4.0" } }, - "node_modules/@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/@alloc/quick-lru": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", @@ -2276,6 +2254,170 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "node_modules/@biomejs/biome": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-1.9.3.tgz", + "integrity": "sha512-POjAPz0APAmX33WOQFGQrwLvlu7WLV4CFJMlB12b6ZSg+2q6fYu9kZwLCOA+x83zXfcPd1RpuWOKJW0GbBwLIQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT OR Apache-2.0", + "bin": { + "biome": "bin/biome" + }, + "engines": { + "node": ">=14.21.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/biome" + }, + "optionalDependencies": { + "@biomejs/cli-darwin-arm64": "1.9.3", + "@biomejs/cli-darwin-x64": "1.9.3", + "@biomejs/cli-linux-arm64": "1.9.3", + "@biomejs/cli-linux-arm64-musl": "1.9.3", + "@biomejs/cli-linux-x64": "1.9.3", + "@biomejs/cli-linux-x64-musl": "1.9.3", + "@biomejs/cli-win32-arm64": "1.9.3", + "@biomejs/cli-win32-x64": "1.9.3" + } + }, + "node_modules/@biomejs/cli-darwin-arm64": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.9.3.tgz", + "integrity": "sha512-QZzD2XrjJDUyIZK+aR2i5DDxCJfdwiYbUKu9GzkCUJpL78uSelAHAPy7m0GuPMVtF/Uo+OKv97W3P9nuWZangQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-darwin-x64": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.9.3.tgz", + "integrity": "sha512-vSCoIBJE0BN3SWDFuAY/tRavpUtNoqiceJ5PrU3xDfsLcm/U6N93JSM0M9OAiC/X7mPPfejtr6Yc9vSgWlEgVw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-arm64": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.9.3.tgz", + "integrity": "sha512-vJkAimD2+sVviNTbaWOGqEBy31cW0ZB52KtpVIbkuma7PlfII3tsLhFa+cwbRAcRBkobBBhqZ06hXoZAN8NODQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-arm64-musl": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.9.3.tgz", + "integrity": "sha512-VBzyhaqqqwP3bAkkBrhVq50i3Uj9+RWuj+pYmXrMDgjS5+SKYGE56BwNw4l8hR3SmYbLSbEo15GcV043CDSk+Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-x64": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-1.9.3.tgz", + "integrity": "sha512-x220V4c+romd26Mu1ptU+EudMXVS4xmzKxPVb9mgnfYlN4Yx9vD5NZraSx/onJnd3Gh/y8iPUdU5CDZJKg9COA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-x64-musl": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.9.3.tgz", + "integrity": "sha512-TJmnOG2+NOGM72mlczEsNki9UT+XAsMFAOo8J0me/N47EJ/vkLXxf481evfHLlxMejTY6IN8SdRSiPVLv6AHlA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-arm64": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.9.3.tgz", + "integrity": "sha512-lg/yZis2HdQGsycUvHWSzo9kOvnGgvtrYRgoCEwPBwwAL8/6crOp3+f47tPwI/LI1dZrhSji7PNsGKGHbwyAhw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-x64": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-1.9.3.tgz", + "integrity": "sha512-cQMy2zanBkVLpmmxXdK6YePzmZx0s5Z7KEnwmrW54rcXK3myCNbQa09SwGZ8i/8sLw0H9F3X7K4rxVNGU8/D4Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.3" + } + }, "node_modules/@codemirror/autocomplete": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.2.0.tgz", @@ -2479,7 +2621,8 @@ "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "devOptional": true, + "optional": true, + "peer": true, "dependencies": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -2491,17 +2634,19 @@ "version": "0.3.9", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "devOptional": true, + "optional": true, + "peer": true, "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" } }, "node_modules/@cypress/request": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.1.tgz", - "integrity": "sha512-TWivJlJi8ZDx2wGOw1dbLuHJKUYX7bWySw377nlnGOW3hP9/MUKIsEdXT/YngWxVdgNCHRBmFlBipE+5/2ZZlQ==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.5.tgz", + "integrity": "sha512-v+XHd9XmWbufxF1/bTaVm2yhbxY+TB4YtWRqF2zaXBlDNMkls34KiATz0AVDLavL3iB6bQk9/7n3oY1EoLSWGA==", "dev": true, + "license": "Apache-2.0", "dependencies": { "aws-sign2": "~0.7.0", "aws4": "^1.8.0", @@ -2509,14 +2654,14 @@ "combined-stream": "~1.0.6", "extend": "~3.0.2", "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "http-signature": "~1.3.6", + "form-data": "~4.0.0", + "http-signature": "~1.4.0", "is-typedarray": "~1.0.0", "isstream": "~0.1.2", "json-stringify-safe": "~5.0.1", "mime-types": "~2.1.19", "performance-now": "^2.1.0", - "qs": "6.10.4", + "qs": "6.13.0", "safe-buffer": "^5.1.2", "tough-cookie": "^4.1.3", "tunnel-agent": "^0.6.0", @@ -2526,20 +2671,6 @@ "node": ">= 6" } }, - "node_modules/@cypress/request/node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 0.12" - } - }, "node_modules/@cypress/xvfb": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz", @@ -2942,89 +3073,6 @@ "node": ">=12" } }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", - "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", - "dev": true, - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", - "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/eslintrc/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/js": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", - "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, "node_modules/@floating-ui/core": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.0.tgz", @@ -4147,45 +4195,13 @@ "react-hook-form": "^7.0.0" } }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", - "dev": true, - "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", - "debug": "^4.3.1", - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", - "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", - "dev": true - }, "node_modules/@iconify-icon/react": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@iconify-icon/react/-/react-2.0.1.tgz", - "integrity": "sha512-1m6L2yNsSJ25k5baQRqNqh2J0w+91PwOn1WdBIR6ZTwxePbsZC8k3NNVc6m9BJObsIQdUlMA1NGj8el4tfbsVg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@iconify-icon/react/-/react-2.1.0.tgz", + "integrity": "sha512-OuEsW5Y474rg3WlseLFQ0uuJjnyk1DhLN1Ire5JGjF4sF8/rNxGJDLSItEogRcKuUbL+zzuoBsaTUVVInuixRA==", + "license": "MIT", "dependencies": { - "iconify-icon": "^2.0.0" + "iconify-icon": "^2.1.0" }, "funding": { "url": "https://github.com/sponsors/cyberalien" @@ -4195,9 +4211,10 @@ } }, "node_modules/@iconify-json/mdi": { - "version": "1.1.64", - "resolved": "https://registry.npmjs.org/@iconify-json/mdi/-/mdi-1.1.64.tgz", - "integrity": "sha512-zGeo5TjhNFAY6FmSDBLAzDO811t77r6v/mDi7CAL9w5eXqKez6bIjk8R9AL/RHIeq44ALP4Ozr4lMqFTkHr7ug==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@iconify-json/mdi/-/mdi-1.2.0.tgz", + "integrity": "sha512-E9/3l5Syg3wfuarorFodhn4s8YorxhH3U3U20LaNBNiqw1kFNIDWhF6HymuzAD35k7RH0OBasJ+ZUyFtVVV6eg==", + "license": "Apache-2.0", "dependencies": { "@iconify/types": "*" } @@ -4347,9 +4364,10 @@ } }, "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==" + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.25", @@ -4630,13 +4648,13 @@ } }, "node_modules/@playwright/test": { - "version": "1.47.0", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.47.0.tgz", - "integrity": "sha512-SgAdlSwYVpToI4e/IH19IHHWvoijAYH5hu2MWSXptRypLSnzj51PcGD+rsOXFayde4P9ZLi+loXVwArg6IUkCA==", + "version": "1.47.2", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.47.2.tgz", + "integrity": "sha512-jTXRsoSPONAs8Za9QEQdyjFn+0ZQFjCiIztAIF6bi1HqhBzG9Ma7g1WotyiGqFSBRZjIEqMdT8RUlbk1QVhzCQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright": "1.47.0" + "playwright": "1.47.2" }, "bin": { "playwright": "cli.js" @@ -5972,6 +5990,7 @@ "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.4.tgz", "integrity": "sha512-FlS4ZWlp97iiNWig0Muq8p+3rVDjRiYE+YKGbAqXOu9nwJFFOdL00kFpz42M+4huzYi86vAK1sOOfyOG45muIQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", @@ -5987,10 +6006,11 @@ } }, "node_modules/@testing-library/react": { - "version": "14.2.2", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-14.2.2.tgz", - "integrity": "sha512-SOUuM2ysCvjUWBXTNfQ/ztmnKDmqaiPV3SvoIuyxMUca45rbSWWAT/qB8CUs/JQ/ux/8JFs9DNdFQ3f6jH3crA==", + "version": "14.3.1", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-14.3.1.tgz", + "integrity": "sha512-H99XjUhWQw0lTgyMN05W3xQG1Nh4lq574D8keFf1dDoNTJgp66VbJozRaczoF+wsiaPJNt/TcnfpLGufGxSrZQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/runtime": "^7.12.5", "@testing-library/dom": "^9.0.0", @@ -6016,31 +6036,36 @@ "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "devOptional": true + "optional": true, + "peer": true }, "node_modules/@tsconfig/node12": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "devOptional": true + "optional": true, + "peer": true }, "node_modules/@tsconfig/node14": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "devOptional": true + "optional": true, + "peer": true }, "node_modules/@tsconfig/node16": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "devOptional": true + "optional": true, + "peer": true }, "node_modules/@types/aria-query": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/babel__core": { "version": "7.20.5", @@ -6188,12 +6213,6 @@ "integrity": "sha512-b7bq23s4fgBB76n34m2b3RBf6M369B0Z9uRR8aHTMd8kZISRkmDEpPD8hhpYvDFzr3bJCPES96cm3Q6qRNDbQw==", "dev": true }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true - }, "node_modules/@types/loadable__component": { "version": "5.13.9", "resolved": "https://registry.npmjs.org/@types/loadable__component/-/loadable__component-5.13.9.tgz", @@ -6353,242 +6372,21 @@ "@types/node": "*" } }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.16.0.tgz", - "integrity": "sha512-py1miT6iQpJcs1BiJjm54AMzeuMPBSPuKPlnT8HlfudbcS5rYeX5jajpLf3mrdRh9dA/Ec2FVUY0ifeVNDIhZw==", - "dev": true, - "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.16.0", - "@typescript-eslint/type-utils": "7.16.0", - "@typescript-eslint/utils": "7.16.0", - "@typescript-eslint/visitor-keys": "7.16.0", - "graphemer": "^1.4.0", - "ignore": "^5.3.1", - "natural-compare": "^1.4.0", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, + "node_modules/@uiw/color-convert": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@uiw/color-convert/-/color-convert-2.1.1.tgz", + "integrity": "sha512-L421mBAT2NRsmYv7BQvofOEwV0iKee1upPVxMjo2NnkJWyIu4I+H1RxK9m3uT8yvcOlStZhv7BQBsFyJCGmIMg==", "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "url": "https://jaywcjlove.github.io/#/sponsor" }, "peerDependencies": { - "@typescript-eslint/parser": "^7.0.0", - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "@babel/runtime": ">=7.19.0" } }, - "node_modules/@typescript-eslint/parser": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.16.0.tgz", - "integrity": "sha512-ar9E+k7CU8rWi2e5ErzQiC93KKEFAXA2Kky0scAlPcxYblLt8+XZuHUZwlyfXILyQa95P6lQg+eZgh/dDs3+Vw==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "7.16.0", - "@typescript-eslint/types": "7.16.0", - "@typescript-eslint/typescript-estree": "7.16.0", - "@typescript-eslint/visitor-keys": "7.16.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.16.0.tgz", - "integrity": "sha512-8gVv3kW6n01Q6TrI1cmTZ9YMFi3ucDT7i7aI5lEikk2ebk1AEjrwX8MDTdaX5D7fPXMBLvnsaa0IFTAu+jcfOw==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "7.16.0", - "@typescript-eslint/visitor-keys": "7.16.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.16.0.tgz", - "integrity": "sha512-j0fuUswUjDHfqV/UdW6mLtOQQseORqfdmoBNDFOqs9rvNVR2e+cmu6zJu/Ku4SDuqiJko6YnhwcL8x45r8Oqxg==", - "dev": true, - "dependencies": { - "@typescript-eslint/typescript-estree": "7.16.0", - "@typescript-eslint/utils": "7.16.0", - "debug": "^4.3.4", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.16.0.tgz", - "integrity": "sha512-fecuH15Y+TzlUutvUl9Cc2XJxqdLr7+93SQIbcZfd4XRGGKoxyljK27b+kxKamjRkU7FYC6RrbSCg0ALcZn/xw==", - "dev": true, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.16.0.tgz", - "integrity": "sha512-a5NTvk51ZndFuOLCh5OaJBELYc2O3Zqxfl3Js78VFE1zE46J2AaVuW+rEbVkQznjkmlzWsUI15BG5tQMixzZLw==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "7.16.0", - "@typescript-eslint/visitor-keys": "7.16.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/typescript-estree/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==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.16.0.tgz", - "integrity": "sha512-PqP4kP3hb4r7Jav+NiRCntlVzhxBNWq6ZQ+zQwII1y/G/1gdIPeYDCKr2+dH6049yJQsWZiHU6RlwvIFBXXGNA==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.16.0", - "@typescript-eslint/types": "7.16.0", - "@typescript-eslint/typescript-estree": "7.16.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.56.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.16.0.tgz", - "integrity": "sha512-rMo01uPy9C7XxG7AFsxa8zLnWXTF8N3PYclekWSrurvhwiw1eW88mrKiAYe6s53AUY57nTRz8dJsuuXdkAhzCg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "7.16.0", - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@uiw/color-convert": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@uiw/color-convert/-/color-convert-2.1.1.tgz", - "integrity": "sha512-L421mBAT2NRsmYv7BQvofOEwV0iKee1upPVxMjo2NnkJWyIu4I+H1RxK9m3uT8yvcOlStZhv7BQBsFyJCGmIMg==", - "funding": { - "url": "https://jaywcjlove.github.io/#/sponsor" - }, - "peerDependencies": { - "@babel/runtime": ">=7.19.0" - } - }, - "node_modules/@uiw/react-color": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@uiw/react-color/-/react-color-2.1.1.tgz", - "integrity": "sha512-RE95rGzlOej848nK0onqxk2N+asrHpp3LEH2h7VJkcdJLOK54jccnGKdCc2seNue3zpCIcwPcR38hOeHhfJLJg==", + "node_modules/@uiw/react-color": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@uiw/react-color/-/react-color-2.1.1.tgz", + "integrity": "sha512-RE95rGzlOej848nK0onqxk2N+asrHpp3LEH2h7VJkcdJLOK54jccnGKdCc2seNue3zpCIcwPcR38hOeHhfJLJg==", "dependencies": { "@uiw/color-convert": "2.1.1", "@uiw/react-color-alpha": "2.1.1", @@ -7022,6 +6820,7 @@ "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.4.0.tgz", "integrity": "sha512-Jths0sWCJZ8BxjKe+p+eKsoqev1/T8lYcrjavEaz8auEJ4jAVY0GwW3JKmdVU4mmNPLPHixh4GNXP7GFtAiDHA==", "dev": true, + "license": "MIT", "dependencies": { "@vitest/spy": "1.4.0", "@vitest/utils": "1.4.0", @@ -7036,6 +6835,7 @@ "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.4.0.tgz", "integrity": "sha512-EDYVSmesqlQ4RD2VvWo3hQgTJ7ZrFQ2VSJdfiJiArkCerDAGeyF1i6dHkmySqk573jLp6d/cfqCN+7wUB5tLgg==", "dev": true, + "license": "MIT", "dependencies": { "@vitest/utils": "1.4.0", "p-limit": "^5.0.0", @@ -7050,6 +6850,7 @@ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-5.0.0.tgz", "integrity": "sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==", "dev": true, + "license": "MIT", "dependencies": { "yocto-queue": "^1.0.0" }, @@ -7061,10 +6862,11 @@ } }, "node_modules/@vitest/runner/node_modules/yocto-queue": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", - "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.1.1.tgz", + "integrity": "sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==", "dev": true, + "license": "MIT", "engines": { "node": ">=12.20" }, @@ -7077,6 +6879,7 @@ "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.4.0.tgz", "integrity": "sha512-saAFnt5pPIA5qDGxOHxJ/XxhMFKkUSBJmVt5VgDsAqPTX6JP326r5C/c9UuCMPoXNzuudTPsYDZCoJ5ilpqG2A==", "dev": true, + "license": "MIT", "dependencies": { "magic-string": "^0.30.5", "pathe": "^1.1.1", @@ -7091,6 +6894,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -7103,6 +6907,7 @@ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, + "license": "MIT", "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", @@ -7113,16 +6918,18 @@ } }, "node_modules/@vitest/snapshot/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" }, "node_modules/@vitest/spy": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.4.0.tgz", "integrity": "sha512-Ywau/Qs1DzM/8Uc+yA77CwSegizMlcgTJuYGAi0jujOteJOUf1ujunHThYo243KG9nAyWT3L9ifPYZ5+As/+6Q==", "dev": true, + "license": "MIT", "dependencies": { "tinyspy": "^2.2.0" }, @@ -7135,6 +6942,7 @@ "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.4.0.tgz", "integrity": "sha512-mx3Yd1/6e2Vt/PUC98DcqTirtfxUyAZ32uK82r8rZzbtBeBo+nqgnjx/LvqQdWsrvNtm14VmurNgcf4nqY5gJg==", "dev": true, + "license": "MIT", "dependencies": { "diff-sequences": "^29.6.3", "estree-walker": "^3.0.3", @@ -7150,6 +6958,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -7162,6 +6971,7 @@ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", "dev": true, + "license": "MIT", "dependencies": { "@types/estree": "^1.0.0" } @@ -7171,6 +6981,7 @@ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, + "license": "MIT", "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", @@ -7181,10 +6992,11 @@ } }, "node_modules/@vitest/utils/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" }, "node_modules/@whatwg-node/events": { "version": "0.0.3", @@ -7274,15 +7086,6 @@ "node": ">=0.4.0" } }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, "node_modules/acorn-walk": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", @@ -7317,22 +7120,6 @@ "node": ">=8" } }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, "node_modules/ansi-colors": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", @@ -7442,6 +7229,7 @@ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { "deep-equal": "^2.0.5" } @@ -7462,26 +7250,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/array-includes": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", - "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.4", - "is-string": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", @@ -7491,129 +7259,6 @@ "node": ">=8" } }, - "node_modules/array.prototype.findlast": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", - "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.findlastindex": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", - "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flat": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", - "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flatmap": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", - "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.toreversed": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/array.prototype.toreversed/-/array.prototype.toreversed-1.1.2.tgz", - "integrity": "sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - } - }, - "node_modules/array.prototype.tosorted": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.3.tgz", - "integrity": "sha512-/DdH4TiTmOKzyQbp/eadcCVexiCb36xJg7HshYOYJnNZFDj33GEv0P7GxsynpShhq4OLYJzbGcBDkLsDt7MnNg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.1.0", - "es-shim-unscopables": "^1.0.2" - } - }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", - "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", - "dev": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.2.1", - "get-intrinsic": "^1.2.3", - "is-array-buffer": "^3.0.4", - "is-shared-array-buffer": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", @@ -7625,6 +7270,7 @@ "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", "dev": true, + "license": "MIT", "dependencies": { "safer-buffer": "~2.1.0" } @@ -7648,6 +7294,7 @@ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8" } @@ -7657,6 +7304,7 @@ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", "dev": true, + "license": "MIT", "engines": { "node": "*" } @@ -7759,15 +7407,17 @@ "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "*" } }, "node_modules/aws4": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", - "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==", - "dev": true + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz", + "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==", + "dev": true, + "license": "MIT" }, "node_modules/babel-plugin-polyfill-corejs2": { "version": "0.4.10", @@ -7893,6 +7543,7 @@ "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "tweetnacl": "^0.14.3" } @@ -8027,60 +7678,6 @@ "node": "*" } }, - "node_modules/builtin-modules": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", - "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", - "dev": true, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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/builtins/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/builtins/node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/builtins/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/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -8098,6 +7695,7 @@ "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -8168,9 +7766,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001605", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001605.tgz", - "integrity": "sha512-nXwGlFWo34uliI9z3n6Qc0wZaf7zaZWA1CPZ169La5mV3I/gem7bst0vr5XQH5TJXZIMfDeZyOrZnSlVzKxxHQ==", + "version": "1.0.30001664", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001664.tgz", + "integrity": "sha512-AmE7k4dXiNKQipgn7a2xg558IRqPN3jMQY/rOsbxDhrd0tyChwbITBfiwtnqz8bi2M5mIWbxAYBvk7W7QBUS2g==", "funding": [ { "type": "opencollective", @@ -8184,7 +7782,8 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ] + ], + "license": "CC-BY-4.0" }, "node_modules/capital-case": { "version": "1.0.4", @@ -8201,7 +7800,8 @@ "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", - "dev": true + "dev": true, + "license": "Apache-2.0" }, "node_modules/ccount": { "version": "2.0.1", @@ -8213,10 +7813,11 @@ } }, "node_modules/chai": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.4.1.tgz", - "integrity": "sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", + "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", "dev": true, + "license": "MIT", "dependencies": { "assertion-error": "^1.1.0", "check-error": "^1.0.3", @@ -8224,7 +7825,7 @@ "get-func-name": "^2.0.2", "loupe": "^2.3.6", "pathval": "^1.1.1", - "type-detect": "^4.0.8" + "type-detect": "^4.1.0" }, "engines": { "node": ">=4" @@ -8339,6 +7940,7 @@ "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", "dev": true, + "license": "MIT", "dependencies": { "get-func-name": "^2.0.2" }, @@ -8574,6 +8176,20 @@ "@lezer/highlight": "^1.0.0" } }, + "node_modules/cmdk": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cmdk/-/cmdk-1.0.0.tgz", + "integrity": "sha512-gDzVf0a09TvoJ5jnuPvygTB77+XdOSwEmJ88L6XPFPlv7T3RxbP9jgenfylrAMD0+Le1aO0nVjQUzl2g+vjz5Q==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-dialog": "1.0.5", + "@radix-ui/react-primitive": "1.0.3" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, "node_modules/codemirror": { "version": "5.65.16", "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.16.tgz", @@ -8668,6 +8284,13 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "node_modules/confbox": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.7.tgz", + "integrity": "sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==", + "dev": true, + "license": "MIT" + }, "node_modules/constant-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/constant-case/-/constant-case-3.0.4.tgz", @@ -8708,7 +8331,8 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/cosmiconfig": { "version": "8.3.6", @@ -8739,7 +8363,8 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "devOptional": true + "optional": true, + "peer": true }, "node_modules/crelt": { "version": "1.0.6", @@ -8884,13 +8509,14 @@ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, "node_modules/cypress": { - "version": "13.7.3", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.7.3.tgz", - "integrity": "sha512-uoecY6FTCAuIEqLUYkTrxamDBjMHTYak/1O7jtgwboHiTnS1NaMOoR08KcTrbRZFCBvYOiS4tEkQRmsV+xcrag==", + "version": "13.15.0", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.15.0.tgz", + "integrity": "sha512-53aO7PwOfi604qzOkCSzNlWquCynLlKE/rmmpSPcziRH6LNfaDUAklQT6WJIsD8ywxlIy+uVZsnTMCCQVd2kTw==", "dev": true, "hasInstallScript": true, + "license": "MIT", "dependencies": { - "@cypress/request": "^3.0.0", + "@cypress/request": "^3.0.4", "@cypress/xvfb": "^1.2.4", "@types/sinonjs__fake-timers": "8.1.1", "@types/sizzle": "^2.3.2", @@ -8929,7 +8555,7 @@ "request-progress": "^3.0.0", "semver": "^7.5.3", "supports-color": "^8.1.1", - "tmp": "~0.2.1", + "tmp": "~0.2.3", "untildify": "^4.0.0", "yauzl": "^2.10.0" }, @@ -9153,6 +8779,7 @@ "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", "dev": true, + "license": "MIT", "dependencies": { "assert-plus": "^1.0.0" }, @@ -9173,57 +8800,6 @@ "node": ">=18" } }, - "node_modules/data-view-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", - "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/data-view-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", - "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/data-view-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", - "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/dataloader": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/dataloader/-/dataloader-2.2.2.tgz", @@ -9309,10 +8885,11 @@ } }, "node_modules/deep-eql": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", - "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", + "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", "dev": true, + "license": "MIT", "dependencies": { "type-detect": "^4.0.0" }, @@ -9325,6 +8902,7 @@ "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==", "dev": true, + "license": "MIT", "dependencies": { "array-buffer-byte-length": "^1.0.0", "call-bind": "^1.0.5", @@ -9352,12 +8930,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, "node_modules/deepmerge": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", @@ -9487,7 +9059,8 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "devOptional": true, + "optional": true, + "peer": true, "engines": { "node": ">=0.3.1" } @@ -9502,6 +9075,7 @@ "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", "dev": true, + "license": "MIT", "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } @@ -9523,23 +9097,12 @@ "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/dom-accessibility-api": { "version": "0.5.16", "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/dom-helpers": { "version": "5.2.1", @@ -9642,6 +9205,7 @@ "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", "dev": true, + "license": "MIT", "dependencies": { "jsbn": "~0.1.0", "safer-buffer": "^2.1.0" @@ -9711,66 +9275,6 @@ "is-arrayish": "^0.2.1" } }, - "node_modules/es-abstract": { - "version": "1.23.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", - "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", - "dev": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "arraybuffer.prototype.slice": "^1.0.3", - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "data-view-buffer": "^1.0.1", - "data-view-byte-length": "^1.0.1", - "data-view-byte-offset": "^1.0.0", - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-set-tostringtag": "^2.0.3", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.4", - "get-symbol-description": "^1.0.2", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", - "has-symbols": "^1.0.3", - "hasown": "^2.0.2", - "internal-slot": "^1.0.7", - "is-array-buffer": "^3.0.4", - "is-callable": "^1.2.7", - "is-data-view": "^1.0.1", - "is-negative-zero": "^2.0.3", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.3", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.13", - "is-weakref": "^1.0.2", - "object-inspect": "^1.13.1", - "object-keys": "^1.1.1", - "object.assign": "^4.1.5", - "regexp.prototype.flags": "^1.5.2", - "safe-array-concat": "^1.1.2", - "safe-regex-test": "^1.0.3", - "string.prototype.trim": "^1.2.9", - "string.prototype.trimend": "^1.0.8", - "string.prototype.trimstart": "^1.0.8", - "typed-array-buffer": "^1.0.2", - "typed-array-byte-length": "^1.0.1", - "typed-array-byte-offset": "^1.0.2", - "typed-array-length": "^1.0.6", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.15" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/es-define-property": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", @@ -9797,6 +9301,7 @@ "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.2", "get-intrinsic": "^1.1.3", @@ -9812,83 +9317,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es-iterator-helpers": { - "version": "1.0.18", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.18.tgz", - "integrity": "sha512-scxAJaewsahbqTYrGKJihhViaM6DDZDDoucfvzNbK0pOren1g/daDQ3IAhzn+1G14rBG7w+i5N+qul60++zlKA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.0", - "es-errors": "^1.3.0", - "es-set-tostringtag": "^2.0.3", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "globalthis": "^1.0.3", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.7", - "iterator.prototype": "^1.1.2", - "safe-array-concat": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", - "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", - "dev": true, - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", - "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.4", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-shim-unscopables": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", - "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", - "dev": true, - "dependencies": { - "hasown": "^2.0.0" - } - }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/esbuild": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", @@ -9935,325 +9363,265 @@ "node": ">=6" } }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, + "node_modules/estree-util-is-identifier-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/eslint": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", - "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.0", - "@humanwhocodes/config-array": "^0.11.14", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" }, - "node_modules/eslint-compat-utils": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.5.0.tgz", - "integrity": "sha512-dc6Y8tzEcSYZMHa+CMPLi/hyo1FzNeonbhJL7Ol0ccuKQkwopJcJBA9YL/xmMTLU1eKigXo9vj9nALElWYSowg==", - "dev": true, - "dependencies": { - "semver": "^7.5.4" - }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "engines": { - "node": ">=12" - }, - "peerDependencies": { - "eslint": ">=6.0.0" + "node": ">=0.10.0" } }, - "node_modules/eslint-compat-utils/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" + "node_modules/eventemitter2": { + "version": "6.4.7", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.7.tgz", + "integrity": "sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==", + "dev": true + }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "dev": true, + "license": "MIT" + }, + "node_modules/execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/eslint-compat-utils/node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "node_modules/executable": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz", + "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", "dev": true, "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" + "pify": "^2.2.0" }, "engines": { - "node": ">=10" + "node": ">=4" } }, - "node_modules/eslint-compat-utils/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/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, - "node_modules/eslint-config-prettier": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", - "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", + "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, - "bin": { - "eslint-config-prettier": "bin/cli.js" + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" }, - "peerDependencies": { - "eslint": ">=7.0.0" + "engines": { + "node": ">=4" } }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", - "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "node_modules/external-editor/node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", "dev": true, "dependencies": { - "debug": "^3.2.7", - "is-core-module": "^2.13.0", - "resolve": "^1.22.4" + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" } }, - "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "node_modules/extract-files": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/extract-files/-/extract-files-11.0.0.tgz", + "integrity": "sha512-FuoE1qtbJ4bBVvv94CC7s0oTnKUGvQs+Rjf1L2SJFfS+HTVVjhPFtehPdQ0JiGPqVNfSSZvL5yzHHQq2Z4WNhQ==", "dev": true, - "dependencies": { - "ms": "^2.1.1" + "engines": { + "node": "^12.20 || >= 14.13" + }, + "funding": { + "url": "https://github.com/sponsors/jaydenseric" } }, - "node_modules/eslint-module-utils": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.1.tgz", - "integrity": "sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==", + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", "dev": true, "dependencies": { - "debug": "^3.2.7" + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" }, "engines": { - "node": ">=4" + "node": ">= 10.17.0" }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } + "optionalDependencies": { + "@types/yauzl": "^2.9.1" } }, - "node_modules/eslint-module-utils/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", "dev": true, - "dependencies": { - "ms": "^2.1.1" + "engines": [ + "node >=0.6.0" + ], + "license": "MIT" + }, + "node_modules/fast-decode-uri-component": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz", + "integrity": "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==", + "dev": true + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-equals": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.0.1.tgz", + "integrity": "sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ==", + "engines": { + "node": ">=6.0.0" } }, - "node_modules/eslint-plugin-cypress": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-cypress/-/eslint-plugin-cypress-2.15.1.tgz", - "integrity": "sha512-eLHLWP5Q+I4j2AWepYq0PgFEei9/s5LvjuSqWrxurkg1YZ8ltxdvMNmdSf0drnsNo57CTgYY/NIHHLRSWejR7w==", - "dev": true, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dependencies": { - "globals": "^13.20.0" + "@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" }, - "peerDependencies": { - "eslint": ">= 3.2.1" + "engines": { + "node": ">=8.6.0" } }, - "node_modules/eslint-plugin-cypress/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "node_modules/fast-querystring": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fast-querystring/-/fast-querystring-1.1.2.tgz", + "integrity": "sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==", "dev": true, "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "fast-decode-uri-component": "^1.0.1" } }, - "node_modules/eslint-plugin-cypress/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "node_modules/fast-url-parser": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz", + "integrity": "sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ==", "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "dependencies": { + "punycode": "^1.3.2" } }, - "node_modules/eslint-plugin-es-x": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-7.6.0.tgz", - "integrity": "sha512-I0AmeNgevgaTR7y2lrVCJmGYF0rjoznpDvqV/kIkZSZbZ8Rw3eu4cGlvBBULScfkSOCzqKbff5LR4CNrV7mZHA==", - "dev": true, + "node_modules/fastq": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.0.tgz", + "integrity": "sha512-zGygtijUMT7jnk3h26kUms3BkSDp4IfIKjmnqI2tvx6nuBfiF1UqOxbnLfzdv+apBy+53oaImsKtMw/xYbW+1w==", "dependencies": { - "@eslint-community/eslint-utils": "^4.1.2", - "@eslint-community/regexpp": "^4.6.0", - "eslint-compat-utils": "^0.5.0" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ota-meshi" - }, - "peerDependencies": { - "eslint": ">=8" + "reusify": "^1.0.4" } }, - "node_modules/eslint-plugin-import": { - "version": "2.29.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", - "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", "dev": true, "dependencies": { - "array-includes": "^3.1.7", - "array.prototype.findlastindex": "^1.2.3", - "array.prototype.flat": "^1.3.2", - "array.prototype.flatmap": "^1.3.2", - "debug": "^3.2.7", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.9", - "eslint-module-utils": "^2.8.0", - "hasown": "^2.0.0", - "is-core-module": "^2.13.1", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.fromentries": "^2.0.7", - "object.groupby": "^1.0.1", - "object.values": "^1.1.7", - "semver": "^6.3.1", - "tsconfig-paths": "^3.15.0" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + "bser": "2.1.1" } }, - "node_modules/eslint-plugin-import/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "node_modules/fbjs": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-3.0.5.tgz", + "integrity": "sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg==", "dev": true, "dependencies": { - "ms": "^2.1.1" + "cross-fetch": "^3.1.5", + "fbjs-css-vars": "^1.0.0", + "loose-envify": "^1.0.0", + "object-assign": "^4.1.0", + "promise": "^7.1.1", + "setimmediate": "^1.0.5", + "ua-parser-js": "^1.0.35" } }, - "node_modules/eslint-plugin-import/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "node_modules/fbjs-css-vars": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz", + "integrity": "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==", + "dev": true + }, + "node_modules/fbjs/node_modules/cross-fetch": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz", + "integrity": "sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==", "dev": true, "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" + "node-fetch": "^2.6.12" } }, - "node_modules/eslint-plugin-n": { - "version": "16.6.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-16.6.2.tgz", - "integrity": "sha512-6TyDmZ1HXoFQXnhCTUjVFULReoBPOAjpuiKELMkeP40yffI/1ZRO+d9ug/VC6fqISo2WkuIBk3cvuRPALaWlOQ==", + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", "dev": true, "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "builtins": "^5.0.1", - "eslint-plugin-es-x": "^7.5.0", - "get-tsconfig": "^4.7.0", - "globals": "^13.24.0", - "ignore": "^5.2.4", - "is-builtin-module": "^3.2.1", - "is-core-module": "^2.12.1", - "minimatch": "^3.1.2", - "resolve": "^1.22.2", - "semver": "^7.5.3" - }, - "engines": { - "node": ">=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=7.0.0" + "pend": "~1.2.0" } }, - "node_modules/eslint-plugin-n/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "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": { - "type-fest": "^0.20.2" + "escape-string-regexp": "^1.0.5" }, "engines": { "node": ">=8" @@ -10262,517 +9630,460 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint-plugin-n/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" - }, + "node_modules/figures/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": ">=10" + "node": ">=0.8.0" } }, - "node_modules/eslint-plugin-n/node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dev": true, + "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": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" + "to-regex-range": "^5.0.1" }, "engines": { - "node": ">=10" + "node": ">=8" } }, - "node_modules/eslint-plugin-n/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, + "node_modules/filter-obj": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-5.1.0.tgz", + "integrity": "sha512-qWeTREPoT7I0bifpPUXtxkZJ1XJzxWtfoWWkdVGqa+eCr3SHW/Ocp89o8vLvbUuQnadybJpjOKu4V+RwO6sGng==", "engines": { - "node": ">=10" + "node": ">=14.16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint-plugin-n/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/eslint-plugin-promise": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.1.1.tgz", - "integrity": "sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig==", + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "dependencies": { + "is-callable": "^1.1.3" } }, - "node_modules/eslint-plugin-react": { - "version": "7.34.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.1.tgz", - "integrity": "sha512-N97CxlouPT1AHt8Jn0mhhN2RrADlUAsk1/atcT2KyA/l9Q/E6ll7OIGwNumFmWfZ9skV3XXccYS19h80rHtgkw==", + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", "dev": true, - "dependencies": { - "array-includes": "^3.1.7", - "array.prototype.findlast": "^1.2.4", - "array.prototype.flatmap": "^1.3.2", - "array.prototype.toreversed": "^1.1.2", - "array.prototype.tosorted": "^1.1.3", - "doctrine": "^2.1.0", - "es-iterator-helpers": "^1.0.17", - "estraverse": "^5.3.0", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.1.2", - "object.entries": "^1.1.7", - "object.fromentries": "^2.0.7", - "object.hasown": "^1.1.3", - "object.values": "^1.1.7", - "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.5", - "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.10" - }, + "license": "Apache-2.0", "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + "node": "*" } }, - "node_modules/eslint-plugin-react/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "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==", "dev": true, "dependencies": { - "esutils": "^2.0.2" + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" }, "engines": { - "node": ">=0.10.0" + "node": ">= 6" } }, - "node_modules/eslint-plugin-react/node_modules/resolve": { - "version": "2.0.0-next.5", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", - "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", - "dev": true, - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "engines": { + "node": "*" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "patreon", + "url": "https://github.com/sponsors/rawify" } }, - "node_modules/eslint-plugin-unused-imports": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-3.2.0.tgz", - "integrity": "sha512-6uXyn6xdINEpxE1MtDjxQsyXB37lfyO2yKGVVgtD7WEWQGORSOZjgrD6hBhvGv4/SO+TOlS+UnC6JppRqbuwGQ==", - "dev": true, + "node_modules/framer-motion": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-6.5.1.tgz", + "integrity": "sha512-o1BGqqposwi7cgDrtg0dNONhkmPsUFDaLcKXigzuTFC5x58mE8iyTazxSudFzmT6MEyJKfjjU8ItoMe3W+3fiw==", "dependencies": { - "eslint-rule-composer": "^0.3.0" + "@motionone/dom": "10.12.0", + "framesync": "6.0.1", + "hey-listen": "^1.0.8", + "popmotion": "11.0.3", + "style-value-types": "5.0.0", + "tslib": "^2.1.0" }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "optionalDependencies": { + "@emotion/is-prop-valid": "^0.8.2" }, "peerDependencies": { - "@typescript-eslint/eslint-plugin": "6 - 7", - "eslint": "8" - }, - "peerDependenciesMeta": { - "@typescript-eslint/eslint-plugin": { - "optional": true - } + "react": ">=16.8 || ^17.0.0 || ^18.0.0", + "react-dom": ">=16.8 || ^17.0.0 || ^18.0.0" } }, - "node_modules/eslint-rule-composer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz", - "integrity": "sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg==", - "dev": true, - "engines": { - "node": ">=4.0.0" + "node_modules/framesync": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/framesync/-/framesync-6.0.1.tgz", + "integrity": "sha512-fUY88kXvGiIItgNC7wcTOl0SNRCVXMKSWW2Yzfmn7EKNc+MpCzcz9DhdHcdjbrtN3c6R4H5dTY2jiCpPdysEjA==", + "dependencies": { + "tslib": "^2.1.0" } }, - "node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", "dev": true, "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, + "node": ">=10" + } + }, + "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==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint/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==", + "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==", + "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, - "dependencies": { - "is-glob": "^4.0.3" - }, "engines": { - "node": ">=10.13.0" + "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/eslint/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "node_modules/get-east-asian-width": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz", + "integrity": "sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==", "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", "dev": true, + "license": "MIT", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "*" } }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "dev": true, "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">= 0.4" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", - "dev": true, - "dependencies": { - "estraverse": "^5.1.0" - }, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", "engines": { - "node": ">=0.10" + "node": ">=6" } }, - "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==", + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", "dev": true, "dependencies": { - "estraverse": "^5.2.0" + "pump": "^3.0.0" }, "engines": { - "node": ">=4.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "node_modules/getos": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/getos/-/getos-3.2.1.tgz", + "integrity": "sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==", "dev": true, - "engines": { - "node": ">=4.0" + "dependencies": { + "async": "^3.2.0" } }, - "node_modules/estree-util-is-identifier-name": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", - "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0" } }, - "node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eventemitter2": { - "version": "6.4.7", - "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.7.tgz", - "integrity": "sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==", - "dev": true - }, - "node_modules/eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", - "dev": true, - "license": "MIT" + "node_modules/gitdiff-parser": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/gitdiff-parser/-/gitdiff-parser-0.3.1.tgz", + "integrity": "sha512-YQJnY8aew65id8okGxKCksH3efDCJ9HzV7M9rsvd65habf39Pkh4cgYJ27AaoDMqo1X98pgNJhNMrm/kpV7UVQ==" }, - "node_modules/execa": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", - "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "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": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" + "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": ">=10" + "node": "*" }, "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/executable": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz", - "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", - "dev": true, + "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": { - "pify": "^2.2.0" + "is-glob": "^4.0.1" }, "engines": { - "node": ">=4" + "node": ">= 6" } }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "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==", + "node_modules/global-dirs": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", + "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==", "dev": true, "dependencies": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" + "ini": "2.0.0" }, "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/external-editor/node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "dependencies": { - "os-tmpdir": "~1.0.2" - }, + "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==", "engines": { - "node": ">=0.6.0" + "node": ">=4" } }, - "node_modules/extract-files": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/extract-files/-/extract-files-11.0.0.tgz", - "integrity": "sha512-FuoE1qtbJ4bBVvv94CC7s0oTnKUGvQs+Rjf1L2SJFfS+HTVVjhPFtehPdQ0JiGPqVNfSSZvL5yzHHQq2Z4WNhQ==", + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, "engines": { - "node": "^12.20 || >= 14.13" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/jaydenseric" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/extract-zip": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", - "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "node_modules/globrex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", + "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==" + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", "dev": true, "dependencies": { - "debug": "^4.1.1", - "get-stream": "^5.1.0", - "yauzl": "^2.10.0" - }, - "bin": { - "extract-zip": "cli.js" - }, - "engines": { - "node": ">= 10.17.0" + "get-intrinsic": "^1.1.3" }, - "optionalDependencies": { - "@types/yauzl": "^2.9.1" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", - "dev": true, - "engines": [ - "node >=0.6.0" - ] - }, - "node_modules/fast-decode-uri-component": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz", - "integrity": "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==", + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, - "node_modules/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/graphiql": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/graphiql/-/graphiql-3.1.2.tgz", + "integrity": "sha512-k3p2k+7ZgARdLnqMDV192VL47cTmPNn02n5ullULnBE1nv1dtJfUve+AJxaU+kU8JcbwCxSxu3qlIxuu1N3mDQ==", + "dependencies": { + "@graphiql/react": "^0.20.4", + "@graphiql/toolkit": "^0.9.1", + "graphql-language-service": "^5.2.0", + "markdown-it": "^12.2.0" + }, + "peerDependencies": { + "graphql": "^15.5.0 || ^16.0.0", + "react": "^16.8.0 || ^17 || ^18", + "react-dom": "^16.8.0 || ^17 || ^18" + } }, - "node_modules/fast-equals": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.0.1.tgz", - "integrity": "sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ==", + "node_modules/graphql": { + "version": "16.8.1", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.8.1.tgz", + "integrity": "sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==", "engines": { - "node": ">=6.0.0" + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" } }, - "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "node_modules/graphql-config": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/graphql-config/-/graphql-config-5.0.3.tgz", + "integrity": "sha512-BNGZaoxIBkv9yy6Y7omvsaBUHOzfFcII3UN++tpH8MGOKFPFkCPZuwx09ggANMt8FgyWP1Od8SWPmrUEZca4NQ==", + "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" + "@graphql-tools/graphql-file-loader": "^8.0.0", + "@graphql-tools/json-file-loader": "^8.0.0", + "@graphql-tools/load": "^8.0.0", + "@graphql-tools/merge": "^9.0.0", + "@graphql-tools/url-loader": "^8.0.0", + "@graphql-tools/utils": "^10.0.0", + "cosmiconfig": "^8.1.0", + "jiti": "^1.18.2", + "minimatch": "^4.2.3", + "string-env-interpolation": "^1.0.1", + "tslib": "^2.4.0" }, "engines": { - "node": ">=8.6.0" + "node": ">= 16.0.0" + }, + "peerDependencies": { + "cosmiconfig-toml-loader": "^1.0.0", + "graphql": "^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" + }, + "peerDependenciesMeta": { + "cosmiconfig-toml-loader": { + "optional": true + } } }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "node_modules/fast-querystring": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/fast-querystring/-/fast-querystring-1.1.2.tgz", - "integrity": "sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==", + "node_modules/graphql-config/node_modules/minimatch": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.3.tgz", + "integrity": "sha512-lIUdtK5hdofgCTu3aT0sOaHsYR37viUuIc0rwnnDXImbwFRcumyLMeZaM0t0I/fgxS6s6JMfu0rLD1Wz9pv1ng==", "dev": true, "dependencies": { - "fast-decode-uri-component": "^1.0.1" + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": ">=10" } }, - "node_modules/fast-url-parser": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz", - "integrity": "sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ==", - "dev": true, - "dependencies": { - "punycode": "^1.3.2" - } - }, - "node_modules/fastq": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.0.tgz", - "integrity": "sha512-zGygtijUMT7jnk3h26kUms3BkSDp4IfIKjmnqI2tvx6nuBfiF1UqOxbnLfzdv+apBy+53oaImsKtMw/xYbW+1w==", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, + "node_modules/graphql-language-service": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/graphql-language-service/-/graphql-language-service-5.2.0.tgz", + "integrity": "sha512-o/ZgTS0pBxWm3hSF4+6GwiV1//DxzoLWEbS38+jqpzzy1d/QXBidwQuVYTOksclbtOJZ3KR/tZ8fi/tI6VpVMg==", "dependencies": { - "bser": "2.1.1" + "nullthrows": "^1.0.0", + "vscode-languageserver-types": "^3.17.1" + }, + "bin": { + "graphql": "dist/temp-bin.js" + }, + "peerDependencies": { + "graphql": "^15.5.0 || ^16.0.0" } }, - "node_modules/fbjs": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-3.0.5.tgz", - "integrity": "sha512-ztsSx77JBtkuMrEypfhgc3cI0+0h+svqeie7xHbh1k/IKdcydnvadp/mUaGgjAOXQmQSxsqgaRhS3q9fy+1kxg==", + "node_modules/graphql-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/graphql-request/-/graphql-request-6.1.0.tgz", + "integrity": "sha512-p+XPfS4q7aIpKVcgmnZKhMNqhltk20hfXtkaIkTfjjmiKMJ5xrt5c743cL03y/K7y1rg3WrIC49xGiEQ4mxdNw==", "dev": true, "dependencies": { - "cross-fetch": "^3.1.5", - "fbjs-css-vars": "^1.0.0", - "loose-envify": "^1.0.0", - "object-assign": "^4.1.0", - "promise": "^7.1.1", - "setimmediate": "^1.0.5", - "ua-parser-js": "^1.0.35" + "@graphql-typed-document-node/core": "^3.2.0", + "cross-fetch": "^3.1.5" + }, + "peerDependencies": { + "graphql": "14 - 16" } }, - "node_modules/fbjs-css-vars": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz", - "integrity": "sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==", - "dev": true - }, - "node_modules/fbjs/node_modules/cross-fetch": { + "node_modules/graphql-request/node_modules/cross-fetch": { "version": "3.1.8", "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz", "integrity": "sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==", @@ -10781,444 +10092,533 @@ "node-fetch": "^2.6.12" } }, - "node_modules/fd-slicer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", - "dev": true, + "node_modules/graphql-tag": { + "version": "2.12.6", + "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.6.tgz", + "integrity": "sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==", "dependencies": { - "pend": "~1.2.0" + "tslib": "^2.1.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "graphql": "^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.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, + "node_modules/graphql-ws": { + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/graphql-ws/-/graphql-ws-5.16.0.tgz", + "integrity": "sha512-Ju2RCU2dQMgSKtArPbEtsK5gNLnsQyTNIo/T7cZNp96niC1x0KdJNZV0TIoilceBPQwfb5itrGl8pkFeOUMl4A==", + "devOptional": true, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "graphql": ">=0.11 <=16" + } + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", "dependencies": { - "escape-string-regexp": "^1.0.5" + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" }, "engines": { - "node": ">=8" + "node": ">=0.4.7" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "optionalDependencies": { + "uglify-js": "^3.1.4" } }, - "node_modules/figures/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==", + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", "dev": true, - "engines": { - "node": ">=0.8.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "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, - "dependencies": { - "flat-cache": "^3.0.4" - }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=8" } }, - "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==", + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, "dependencies": { - "to-regex-range": "^5.0.1" + "es-define-property": "^1.0.0" }, - "engines": { - "node": ">=8" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/filter-obj": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-5.1.0.tgz", - "integrity": "sha512-qWeTREPoT7I0bifpPUXtxkZJ1XJzxWtfoWWkdVGqa+eCr3SHW/Ocp89o8vLvbUuQnadybJpjOKu4V+RwO6sGng==", + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "dev": true, "engines": { - "node": ">=14.16" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" + "has-symbols": "^1.0.3" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/flatted": { - "version": "3.2.9", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", - "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", - "dev": true - }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dev": true, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dependencies": { - "is-callable": "^1.1.3" - } - }, - "node_modules/forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", - "dev": true, + "function-bind": "^1.1.2" + }, "engines": { - "node": "*" + "node": ">= 0.4" } }, - "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==", - "dev": true, + "node_modules/hast-util-to-jsx-runtime": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.0.tgz", + "integrity": "sha512-H/y0+IWPdsLLS738P8tDnrQ8Z+dj12zQQ6WC11TIM21C8WFVoIxcqWXf2H3hiTVZjF1AWqoimGwrTWecWrnmRQ==", "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-object": "^1.0.0", + "unist-util-position": "^5.0.0", + "vfile-message": "^4.0.0" }, - "engines": { - "node": ">= 6" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/fraction.js": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", - "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", - "engines": { - "node": "*" + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "dependencies": { + "@types/hast": "^3.0.0" }, "funding": { - "type": "patreon", - "url": "https://github.com/sponsors/rawify" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/framer-motion": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-6.5.1.tgz", - "integrity": "sha512-o1BGqqposwi7cgDrtg0dNONhkmPsUFDaLcKXigzuTFC5x58mE8iyTazxSudFzmT6MEyJKfjjU8ItoMe3W+3fiw==", - "dependencies": { - "@motionone/dom": "10.12.0", - "framesync": "6.0.1", - "hey-listen": "^1.0.8", - "popmotion": "11.0.3", - "style-value-types": "5.0.0", - "tslib": "^2.1.0" - }, - "optionalDependencies": { - "@emotion/is-prop-valid": "^0.8.2" - }, - "peerDependencies": { - "react": ">=16.8 || ^17.0.0 || ^18.0.0", - "react-dom": ">=16.8 || ^17.0.0 || ^18.0.0" + "node_modules/header-case": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/header-case/-/header-case-2.0.4.tgz", + "integrity": "sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==", + "dev": true, + "dependencies": { + "capital-case": "^1.0.4", + "tslib": "^2.0.3" } }, - "node_modules/framesync": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/framesync/-/framesync-6.0.1.tgz", - "integrity": "sha512-fUY88kXvGiIItgNC7wcTOl0SNRCVXMKSWW2Yzfmn7EKNc+MpCzcz9DhdHcdjbrtN3c6R4H5dTY2jiCpPdysEjA==", + "node_modules/hey-listen": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/hey-listen/-/hey-listen-1.0.8.tgz", + "integrity": "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==" + }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", "dependencies": { - "tslib": "^2.1.0" + "react-is": "^16.7.0" } }, - "node_modules/fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "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==", "dev": true, "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "whatwg-encoding": "^3.1.1" }, "engines": { - "node": ">=10" + "node": ">=18" } }, - "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/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/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "node_modules/html-url-attributes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.0.tgz", + "integrity": "sha512-/sXbVCWayk6GDVg3ctOX6nxaVj7So40FcFAnWlWGNAB1LpYKcV5Cd10APjPjW80O7zYW2MsjBV4zZ7IZO5fVow==", "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/function.prototype.name": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", - "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" + "agent-base": "^7.1.0", + "debug": "^4.3.4" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 14" } }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "node_modules/http-signature": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.4.0.tgz", + "integrity": "sha512-G5akfn7eKbpDN+8nPS/cb57YeA1jLTVxjpCj7tmm3QKPdyDy7T+qSC40e9ptydSWvkwjSXw1VbkpyEm39ukeAg==", "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^2.0.2", + "sshpk": "^1.18.0" + }, + "engines": { + "node": ">=0.10" } }, - "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==", + "node_modules/https-proxy-agent": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz", + "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==", + "dev": true, + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, "engines": { - "node": ">=6.9.0" + "node": ">= 14" } }, - "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==", + "node_modules/human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", "dev": true, "engines": { - "node": "6.* || 8.* || >= 10.*" + "node": ">=8.12.0" } }, - "node_modules/get-east-asian-width": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz", - "integrity": "sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==", + "node_modules/husky": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz", + "integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==", "dev": true, - "license": "MIT", + "bin": { + "husky": "lib/bin.js" + }, "engines": { - "node": ">=18" + "node": ">=14" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/typicode" } }, - "node_modules/get-func-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", - "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", - "dev": true, - "engines": { - "node": "*" + "node_modules/iconify-icon": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/iconify-icon/-/iconify-icon-2.1.0.tgz", + "integrity": "sha512-lto4XU3bwTQnb+D/CsJ4dWAo0aDe+uPMxEtxyOodw9l7R9QnJUUab3GCehlw2M8mDHdeUu/ufx8PvRQiJphhXg==", + "license": "MIT", + "dependencies": { + "@iconify/types": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/cyberalien" } }, - "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "safer-buffer": ">= 2.1.2 < 3" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.10.0" } }, - "node_modules/get-nonce": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", - "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "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.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "dev": true, "engines": { - "node": ">=6" + "node": ">= 4" } }, - "node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "node_modules/immutable": { + "version": "3.7.6", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.7.6.tgz", + "integrity": "sha512-AizQPcaofEtO11RZhPPHBOJRdo/20MKQF9mBLnVkBoyHi1/zXK8fzVdnEpSV9gxqtnh6Qomfp3F0xT5qP/vThw==", "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "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==", "dependencies": { - "pump": "^3.0.0" + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" }, "engines": { - "node": ">=8" + "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/get-symbol-description": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", - "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.5", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4" - }, + "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==", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=4" } }, - "node_modules/get-tsconfig": { - "version": "4.7.3", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.3.tgz", - "integrity": "sha512-ZvkrzoUA0PQZM6fy6+/Hce561s+faD1rsNwhnO5FelNjyy7EMGJ3Rz1AQ8GYDWjhRs/7dBLOEJvhK8MiEJOAFg==", - "dev": true, - "dependencies": { - "resolve-pkg-maps": "^1.0.0" - }, + "node_modules/import-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-4.0.0.tgz", + "integrity": "sha512-P9J71vT5nLlDeV8FHs5nNxaLbrpfAV5cF5srvbZfpwpcJoM/xZR3hiv+q+SAnuSmuGbXMWud063iIMx/V/EWZQ==", + "dev": true, + "engines": { + "node": ">=12.2" + }, "funding": { - "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/getos": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/getos/-/getos-3.2.1.tgz", - "integrity": "sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==", + "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, - "dependencies": { - "async": "^3.2.0" + "engines": { + "node": ">=8" } }, - "node_modules/getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "node_modules/index-to-position": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-0.1.2.tgz", + "integrity": "sha512-MWDKS3AS1bGCHLBA2VLImJz42f7bJh8wQsTGCzI3j519/CASStoDONUBVz2I/VID0MpiX3SGSnbOD2xUalbE5g==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", "dev": true, "dependencies": { - "assert-plus": "^1.0.0" + "once": "^1.3.0", + "wrappy": "1" } }, - "node_modules/gitdiff-parser": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/gitdiff-parser/-/gitdiff-parser-0.3.1.tgz", - "integrity": "sha512-YQJnY8aew65id8okGxKCksH3efDCJ9HzV7M9rsvd65habf39Pkh4cgYJ27AaoDMqo1X98pgNJhNMrm/kpV7UVQ==" + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "node_modules/ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/inline-style-parser": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.2.tgz", + "integrity": "sha512-EcKzdTHVe8wFVOGEYXiW9WmJXPjqi1T+234YpJr98RiFYKHV3cdy1+3mkTE+KHTHxFFLH51SfaGOoUdW+v7ViQ==" + }, + "node_modules/inquirer": { + "version": "8.2.6", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz", + "integrity": "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==", "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" + "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": "^6.0.1" }, "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=12.0.0" } }, - "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==", + "node_modules/internal-slot": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", + "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", + "dev": true, "dependencies": { - "is-glob": "^4.0.1" + "es-errors": "^1.3.0", + "hasown": "^2.0.0", + "side-channel": "^1.0.4" }, "engines": { - "node": ">= 6" + "node": ">= 0.4" } }, - "node_modules/global-dirs": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", - "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==", + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "node_modules/is-absolute": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", + "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", "dev": true, "dependencies": { - "ini": "2.0.0" + "is-relative": "^1.0.0", + "is-windows": "^1.0.1" }, "engines": { - "node": ">=10" - }, + "node": ">=0.10.0" + } + }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "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==", - "engines": { - "node": ">=4" + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", "dev": true, + "license": "MIT", "dependencies": { - "define-properties": "^1.1.3" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -11227,287 +10627,219 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "node_modules/is-array-buffer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", + "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", "dev": true, "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1" }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/globrex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", - "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==" + "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==" }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", "dev": true, "dependencies": { - "get-intrinsic": "^1.1.3" + "has-bigints": "^1.0.1" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true - }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true - }, - "node_modules/graphiql": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/graphiql/-/graphiql-3.1.2.tgz", - "integrity": "sha512-k3p2k+7ZgARdLnqMDV192VL47cTmPNn02n5ullULnBE1nv1dtJfUve+AJxaU+kU8JcbwCxSxu3qlIxuu1N3mDQ==", + "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": { - "@graphiql/react": "^0.20.4", - "@graphiql/toolkit": "^0.9.1", - "graphql-language-service": "^5.2.0", - "markdown-it": "^12.2.0" + "binary-extensions": "^2.0.0" }, - "peerDependencies": { - "graphql": "^15.5.0 || ^16.0.0", - "react": "^16.8.0 || ^17 || ^18", - "react-dom": "^16.8.0 || ^17 || ^18" - } - }, - "node_modules/graphql": { - "version": "16.8.1", - "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.8.1.tgz", - "integrity": "sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==", "engines": { - "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" + "node": ">=8" } }, - "node_modules/graphql-config": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/graphql-config/-/graphql-config-5.0.3.tgz", - "integrity": "sha512-BNGZaoxIBkv9yy6Y7omvsaBUHOzfFcII3UN++tpH8MGOKFPFkCPZuwx09ggANMt8FgyWP1Od8SWPmrUEZca4NQ==", + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", "dev": true, "dependencies": { - "@graphql-tools/graphql-file-loader": "^8.0.0", - "@graphql-tools/json-file-loader": "^8.0.0", - "@graphql-tools/load": "^8.0.0", - "@graphql-tools/merge": "^9.0.0", - "@graphql-tools/url-loader": "^8.0.0", - "@graphql-tools/utils": "^10.0.0", - "cosmiconfig": "^8.1.0", - "jiti": "^1.18.2", - "minimatch": "^4.2.3", - "string-env-interpolation": "^1.0.1", - "tslib": "^2.4.0" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">= 16.0.0" - }, - "peerDependencies": { - "cosmiconfig-toml-loader": "^1.0.0", - "graphql": "^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" + "node": ">= 0.4" }, - "peerDependenciesMeta": { - "cosmiconfig-toml-loader": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/graphql-config/node_modules/minimatch": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.3.tgz", - "integrity": "sha512-lIUdtK5hdofgCTu3aT0sOaHsYR37viUuIc0rwnnDXImbwFRcumyLMeZaM0t0I/fgxS6s6JMfu0rLD1Wz9pv1ng==", + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, "engines": { - "node": ">=10" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/graphql-language-service": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/graphql-language-service/-/graphql-language-service-5.2.0.tgz", - "integrity": "sha512-o/ZgTS0pBxWm3hSF4+6GwiV1//DxzoLWEbS38+jqpzzy1d/QXBidwQuVYTOksclbtOJZ3KR/tZ8fi/tI6VpVMg==", + "node_modules/is-ci": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", + "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", + "dev": true, "dependencies": { - "nullthrows": "^1.0.0", - "vscode-languageserver-types": "^3.17.1" + "ci-info": "^3.2.0" }, "bin": { - "graphql": "dist/temp-bin.js" - }, - "peerDependencies": { - "graphql": "^15.5.0 || ^16.0.0" + "is-ci": "bin.js" } }, - "node_modules/graphql-request": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/graphql-request/-/graphql-request-6.1.0.tgz", - "integrity": "sha512-p+XPfS4q7aIpKVcgmnZKhMNqhltk20hfXtkaIkTfjjmiKMJ5xrt5c743cL03y/K7y1rg3WrIC49xGiEQ4mxdNw==", - "dev": true, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", "dependencies": { - "@graphql-typed-document-node/core": "^3.2.0", - "cross-fetch": "^3.1.5" + "hasown": "^2.0.0" }, - "peerDependencies": { - "graphql": "14 - 16" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/graphql-request/node_modules/cross-fetch": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz", - "integrity": "sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==", + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", "dev": true, "dependencies": { - "node-fetch": "^2.6.12" - } - }, - "node_modules/graphql-tag": { - "version": "2.12.6", - "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.6.tgz", - "integrity": "sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==", - "dependencies": { - "tslib": "^2.1.0" + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, - "peerDependencies": { - "graphql": "^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/graphql-ws": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/graphql-ws/-/graphql-ws-5.16.0.tgz", - "integrity": "sha512-Ju2RCU2dQMgSKtArPbEtsK5gNLnsQyTNIo/T7cZNp96niC1x0KdJNZV0TIoilceBPQwfb5itrGl8pkFeOUMl4A==", - "devOptional": true, + "node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "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": ">=10" - }, - "peerDependencies": { - "graphql": ">=0.11 <=16" + "node": ">=0.10.0" } }, - "node_modules/handlebars": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", - "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", - "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.2", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, + "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==", "engines": { - "node": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" + "node": ">=8" } }, - "node_modules/has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", - "dev": true, + "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": { - "ansi-regex": "^2.0.0" + "is-extglob": "^2.1.1" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/has-ansi/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "dev": true, - "engines": { - "node": ">=0.10.0" + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "node_modules/is-installed-globally": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", + "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", "dev": true, + "dependencies": { + "global-dirs": "^3.0.0", + "is-path-inside": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "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/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "node_modules/is-lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-lower-case/-/is-lower-case-2.0.2.tgz", + "integrity": "sha512-bVcMJy4X5Og6VZfdOZstSexlEy20Sr0k/p/b2IlQJlfdKAQuMpiv5w2Ccxb8sKdRUNAG1PnHVHjFSdRDVS6NlQ==", "dev": true, "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "tslib": "^2.0.3" } }, - "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "node_modules/is-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", + "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", "dev": true, - "engines": { - "node": ">= 0.4" - }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true, + "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.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.12.0" } }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", "dev": true, "dependencies": { - "has-symbols": "^1.0.3" + "has-tostringtag": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -11516,499 +10848,466 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dependencies": { - "function-bind": "^1.1.2" - }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, "engines": { - "node": ">= 0.4" + "node": ">=8" } }, - "node_modules/hast-util-to-jsx-runtime": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.0.tgz", - "integrity": "sha512-H/y0+IWPdsLLS738P8tDnrQ8Z+dj12zQQ6WC11TIM21C8WFVoIxcqWXf2H3hiTVZjF1AWqoimGwrTWecWrnmRQ==", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "hast-util-whitespace": "^3.0.0", - "mdast-util-mdx-expression": "^2.0.0", - "mdast-util-mdx-jsx": "^3.0.0", - "mdast-util-mdxjs-esm": "^2.0.0", - "property-information": "^6.0.0", - "space-separated-tokens": "^2.0.0", - "style-to-object": "^1.0.0", - "unist-util-position": "^5.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-whitespace": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", - "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", - "dependencies": { - "@types/hast": "^3.0.0" + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "engines": { + "node": ">=12" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/header-case": { + "node_modules/is-plain-object": { "version": "2.0.4", - "resolved": "https://registry.npmjs.org/header-case/-/header-case-2.0.4.tgz", - "integrity": "sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==", - "dev": true, + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dependencies": { - "capital-case": "^1.0.4", - "tslib": "^2.0.3" + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "node_modules/hey-listen": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/hey-listen/-/hey-listen-1.0.8.tgz", - "integrity": "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==" + "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==", + "dev": true }, - "node_modules/hoist-non-react-statics": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", - "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "dependencies": { - "react-is": "^16.7.0" + "node_modules/is-primitive": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-3.0.1.tgz", + "integrity": "sha512-GljRxhWvlCNRfZyORiH77FwdFwGcMO620o37EOYC0ORWdq+WYNVqW0w2Juzew4M+L81l6/QS3t5gkkihyRqv9w==", + "engines": { + "node": ">=0.10.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==", + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", "dev": true, "dependencies": { - "whatwg-encoding": "^3.1.1" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">=18" - } - }, - "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/html-url-attributes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.0.tgz", - "integrity": "sha512-/sXbVCWayk6GDVg3ctOX6nxaVj7So40FcFAnWlWGNAB1LpYKcV5Cd10APjPjW80O7zYW2MsjBV4zZ7IZO5fVow==", + "node": ">= 0.4" + }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "node_modules/is-relative": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", + "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", "dev": true, "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" + "is-unc-path": "^1.0.0" }, "engines": { - "node": ">= 14" + "node": ">=0.10.0" } }, - "node_modules/http-signature": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz", - "integrity": "sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==", + "node_modules/is-set": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", + "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", "dev": true, - "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^2.0.2", - "sshpk": "^1.14.1" - }, - "engines": { - "node": ">=0.10" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/https-proxy-agent": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz", - "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==", + "node_modules/is-shared-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", + "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", "dev": true, "dependencies": { - "agent-base": "^7.0.2", - "debug": "4" + "call-bind": "^1.0.7" }, "engines": { - "node": ">= 14" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/human-signals": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", - "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "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.12.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/husky": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz", - "integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==", + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", "dev": true, - "bin": { - "husky": "lib/bin.js" + "dependencies": { + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">=14" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/typicode" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/iconify-icon": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/iconify-icon/-/iconify-icon-2.0.0.tgz", - "integrity": "sha512-38ArOkxmyD9oDbJBkxaFpE6eZ0K3F9Sk+3x4mWGfjMJaxi3EKrix9Du4iWhgBFT3imKC4FJJE34ur2Rc7Xm+Uw==", + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, "dependencies": { - "@iconify/types": "^2.0.0" + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/cyberalien" + "url": "https://github.com/sponsors/ljharb" } }, - "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==", + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-unc-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", + "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", "dev": true, "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" + "unc-path-regex": "^0.1.2" }, "engines": { "node": ">=0.10.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==", + "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, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "node_modules/is-upper-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-upper-case/-/is-upper-case-2.0.2.tgz", + "integrity": "sha512-44pxmxAvnnAOwBg4tHPnkfvgjPwbc5QIsSstNU+YcJ1ovxVzCWpSGosPJOZh/a1tdl81fbgnLc9LLv+x2ywbPQ==", "dev": true, - "engines": { - "node": ">= 4" + "dependencies": { + "tslib": "^2.0.3" } }, - "node_modules/immutable": { - "version": "3.7.6", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.7.6.tgz", - "integrity": "sha512-AizQPcaofEtO11RZhPPHBOJRdo/20MKQF9mBLnVkBoyHi1/zXK8fzVdnEpSV9gxqtnh6Qomfp3F0xT5qP/vThw==", + "node_modules/is-weakmap": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", + "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", "dev": true, - "engines": { - "node": ">=0.8.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "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==", + "node_modules/is-weakset": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", + "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", + "dev": true, "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" }, "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==", - "engines": { - "node": ">=4" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/import-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/import-from/-/import-from-4.0.0.tgz", - "integrity": "sha512-P9J71vT5nLlDeV8FHs5nNxaLbrpfAV5cF5srvbZfpwpcJoM/xZR3hiv+q+SAnuSmuGbXMWud063iIMx/V/EWZQ==", + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", "dev": true, "engines": { - "node": ">=12.2" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", "engines": { - "node": ">=0.8.19" + "node": ">=0.10.0" } }, - "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==", + "node_modules/isomorphic-ws": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz", + "integrity": "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==", "dev": true, - "engines": { - "node": ">=8" + "peerDependencies": { + "ws": "*" } }, - "node_modules/index-to-position": { + "node_modules/isstream": { "version": "0.1.2", - "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-0.1.2.tgz", - "integrity": "sha512-MWDKS3AS1bGCHLBA2VLImJz42f7bJh8wQsTGCzI3j519/CASStoDONUBVz2I/VID0MpiX3SGSnbOD2xUalbE5g==", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", "dev": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "license": "MIT" }, - "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==", + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" + "engines": { + "node": ">=8" } }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/ini": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", - "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "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/inline-style-parser": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.2.tgz", - "integrity": "sha512-EcKzdTHVe8wFVOGEYXiW9WmJXPjqi1T+234YpJr98RiFYKHV3cdy1+3mkTE+KHTHxFFLH51SfaGOoUdW+v7ViQ==" - }, - "node_modules/inquirer": { - "version": "8.2.6", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz", - "integrity": "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==", + "node_modules/istanbul-lib-source-maps": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.4.tgz", + "integrity": "sha512-wHOoEsNJTVltaJp8eVkm8w+GVkVNHT2YDYo53YdzQEL2gWm1hBX5cGFR9hQJtuGLebidVX7et3+dmDZrmclduw==", "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": "^6.0.1" + "@jridgewell/trace-mapping": "^0.3.23", + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0" }, "engines": { - "node": ">=12.0.0" + "node": ">=10" } }, - "node_modules/internal-slot": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", - "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", "dev": true, "dependencies": { - "es-errors": "^1.3.0", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" }, "engines": { - "node": ">= 0.4" - } - }, - "node_modules/internmap": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", - "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", - "engines": { - "node": ">=12" + "node": ">=8" } }, - "node_modules/invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "dependencies": { - "loose-envify": "^1.0.0" - } + "node_modules/iterall": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.3.0.tgz", + "integrity": "sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg==" }, - "node_modules/is-absolute": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", - "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", - "dev": true, + "node_modules/jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", "dependencies": { - "is-relative": "^1.0.0", - "is-windows": "^1.0.1" + "@isaacs/cliui": "^8.0.2" }, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-alphabetical": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", - "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "node": ">=14" + }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/is-alphanumerical": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", - "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", - "dependencies": { - "is-alphabetical": "^2.0.0", - "is-decimal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "node_modules/jiti": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", + "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==", + "bin": { + "jiti": "bin/jiti.js" } }, - "node_modules/is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "node_modules/jose": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/jose/-/jose-5.2.3.tgz", + "integrity": "sha512-KUXdbctm1uHVL8BYhnyHkgp3zDX5KW8ZhAKVFEfUbU2P8Alpzjb+48hHvjOdQIyPshoblhzsuqOwEEAbtHVirA==", "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/panva" } }, - "node_modules/is-array-buffer": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", - "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1" - }, + "node_modules/jotai": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/jotai/-/jotai-2.10.0.tgz", + "integrity": "sha512-8W4u0aRlOIwGlLQ0sqfl/c6+eExl5D8lZgAUolirZLktyaj4WnxO/8a0HEPmtriQAB6X5LMhXzZVmw02X0P0qQ==", + "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">=12.20.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "@types/react": ">=17.0.0", + "react": ">=17.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + } } }, - "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==" - }, - "node_modules/is-async-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", - "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", + "node_modules/js-levenshtein": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", + "integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==", "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, "engines": { - "node": ">= 0.4" + "node": ">=0.10.0" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dependencies": { + "argparse": "^2.0.1" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsdom": { + "version": "24.0.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-24.0.0.tgz", + "integrity": "sha512-UDS2NayCvmXSXVP6mpTj+73JnNQadZlr9N68189xib2tx5Mls7swlTNao26IoHv46BZJFvXygyRtyXd1feAk1A==", "dev": true, "dependencies": { - "has-bigints": "^1.0.1" + "cssstyle": "^4.0.1", + "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.16.0", + "xml-name-validator": "^5.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "canvas": "^2.11.2" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": 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" + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "bin": { + "jsesc": "bin/jsesc" }, "engines": { - "node": ">=8" + "node": ">=4" } }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "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==" + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "dev": true, + "license": "(AFL-2.1 OR BSD-3-Clause)" + }, + "node_modules/json-stable-stringify": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.1.1.tgz", + "integrity": "sha512-SU/971Kt5qVQfJpyDveVhQ/vya+5hvrjClFOcr8c0Fq5aODJjMwutrOfCU+eCnVD5gpx1Q3fEqkyom77zH1iIg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "call-bind": "^1.0.5", + "isarray": "^2.0.5", + "jsonify": "^0.0.1", + "object-keys": "^1.1.1" }, "engines": { "node": ">= 0.4" @@ -12017,2290 +11316,1392 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-builtin-module": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", - "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true, + "license": "ISC" + }, + "node_modules/json-to-graphql-query": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/json-to-graphql-query/-/json-to-graphql-query-2.2.5.tgz", + "integrity": "sha512-5Nom9inkIMrtY992LMBBG1Zaekrc10JaRhyZgprwHBVMDtRgllTvzl0oBbg13wJsVZoSoFNNMaeIVQs0P04vsA==", + "license": "MIT" + }, + "node_modules/json-to-pretty-yaml": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/json-to-pretty-yaml/-/json-to-pretty-yaml-1.2.2.tgz", + "integrity": "sha512-rvm6hunfCcqegwYaG5T4yKJWxc9FXFgBVrcTZ4XfSVRwa5HA/Xs+vB/Eo9treYYHCeNM0nrSUr82V/M31Urc7A==", "dev": true, "dependencies": { - "builtin-modules": "^3.3.0" + "remedial": "^1.0.7", + "remove-trailing-spaces": "^1.0.6" }, "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.2.0" } }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true, - "engines": { - "node": ">= 0.4" + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "bin": { + "json5": "lib/cli.js" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=6" } }, - "node_modules/is-ci": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", - "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, "dependencies": { - "ci-info": "^3.2.0" + "universalify": "^2.0.0" }, - "bin": { - "is-ci": "bin.js" + "optionalDependencies": { + "graceful-fs": "^4.1.6" } }, - "node_modules/is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", - "dependencies": { - "hasown": "^2.0.0" - }, + "node_modules/jsonify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz", + "integrity": "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==", + "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-data-view": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", - "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", + "node_modules/jsprim": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz", + "integrity": "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==", "dev": true, + "engines": [ + "node >=0.6.0" + ], + "license": "MIT", "dependencies": { - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" } }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "node_modules/lazy-ass": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz", + "integrity": "sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==", "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-decimal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", - "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "node": "> 0.8" } }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", "engines": { - "node": ">=0.10.0" + "node": ">=10" } }, - "node_modules/is-finalizationregistry": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz", - "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "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==" }, - "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==", - "engines": { - "node": ">=8" + "node_modules/linkify-it": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", + "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", + "dependencies": { + "uc.micro": "^1.0.1" } }, - "node_modules/is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "node_modules/lint-staged": { + "version": "15.2.10", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.10.tgz", + "integrity": "sha512-5dY5t743e1byO19P9I4b3x8HJwalIznL5E1FWYnU6OWw33KxNBSLAc6Cy7F2PsFEO8FKnLwjwm5hx7aMF0jzZg==", "dev": true, + "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "chalk": "~5.3.0", + "commander": "~12.1.0", + "debug": "~4.3.6", + "execa": "~8.0.1", + "lilconfig": "~3.1.2", + "listr2": "~8.2.4", + "micromatch": "~4.0.8", + "pidtree": "~0.6.0", + "string-argv": "~0.3.2", + "yaml": "~2.5.0" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" }, "engines": { - "node": ">= 0.4" + "node": ">=18.12.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://opencollective.com/lint-staged" } }, - "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==", + "node_modules/lint-staged/node_modules/ansi-escapes": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz", + "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==", + "dev": true, + "license": "MIT", "dependencies": { - "is-extglob": "^2.1.1" + "environment": "^1.0.0" }, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-hexadecimal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", - "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "node": ">=18" + }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-installed-globally": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", - "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", + "node_modules/lint-staged/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", "dev": true, - "dependencies": { - "global-dirs": "^3.0.0", - "is-path-inside": "^3.0.2" - }, + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "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==", + "node_modules/lint-staged/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/is-lower-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-lower-case/-/is-lower-case-2.0.2.tgz", - "integrity": "sha512-bVcMJy4X5Og6VZfdOZstSexlEy20Sr0k/p/b2IlQJlfdKAQuMpiv5w2Ccxb8sKdRUNAG1PnHVHjFSdRDVS6NlQ==", + "node_modules/lint-staged/node_modules/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true, - "dependencies": { - "tslib": "^2.0.3" + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/is-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", - "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", + "node_modules/lint-staged/node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-negative-zero": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", - "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "node_modules/lint-staged/node_modules/cli-truncate": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", + "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", "dev": true, + "license": "MIT", + "dependencies": { + "slice-ansi": "^5.0.0", + "string-width": "^7.0.0" + }, "engines": { - "node": ">= 0.4" + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "node_modules/lint-staged/node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=0.12.0" + "node": ">=18" } }, - "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "node_modules/lint-staged/node_modules/emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lint-staged/node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", "dev": true, + "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=16.17" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "node_modules/lint-staged/node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", "dev": true, + "license": "MIT", "engines": { - "node": ">=8" - } - }, - "node_modules/is-plain-obj": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", - "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", - "engines": { - "node": ">=12" + "node": ">=16" }, "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==", - "dependencies": { - "isobject": "^3.0.1" - }, + "node_modules/lint-staged/node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">=0.10.0" + "node": ">=16.17.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==", - "dev": true - }, - "node_modules/is-primitive": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-3.0.1.tgz", - "integrity": "sha512-GljRxhWvlCNRfZyORiH77FwdFwGcMO620o37EOYC0ORWdq+WYNVqW0w2Juzew4M+L81l6/QS3t5gkkihyRqv9w==", + "node_modules/lint-staged/node_modules/is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "node_modules/lint-staged/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, + "license": "MIT", "engines": { - "node": ">= 0.4" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-relative": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", - "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", + "node_modules/lint-staged/node_modules/lilconfig": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", + "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", "dev": true, - "dependencies": { - "is-unc-path": "^1.0.0" - }, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" } }, - "node_modules/is-set": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", - "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", + "node_modules/lint-staged/node_modules/listr2": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.4.tgz", + "integrity": "sha512-opevsywziHd3zHCVQGAj8zu+Z3yHNkkoYhWIGnq54RrCVwLz0MozotJEDnKsIBLvkfLGN6BLOyAeRrYI0pKA4g==", "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "license": "MIT", + "dependencies": { + "cli-truncate": "^4.0.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^6.1.0", + "rfdc": "^1.4.1", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18.0.0" } }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", - "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", + "node_modules/lint-staged/node_modules/log-update": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", + "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7" + "ansi-escapes": "^7.0.0", + "cli-cursor": "^5.0.0", + "slice-ansi": "^7.1.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "node_modules/lint-staged/node_modules/log-update/node_modules/is-fullwidth-code-point": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz", + "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==", "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.0.0" + }, "engines": { - "node": ">=8" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "node_modules/lint-staged/node_modules/log-update/node_modules/slice-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz", + "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==", "dev": true, + "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "node_modules/lint-staged/node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", "dev": true, + "license": "MIT", "dependencies": { - "has-symbols": "^1.0.2" + "path-key": "^4.0.0" }, "engines": { - "node": ">= 0.4" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", - "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", + "node_modules/lint-staged/node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", "dev": true, + "license": "MIT", "dependencies": { - "which-typed-array": "^1.1.14" + "mimic-fn": "^4.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "dev": true + "node_modules/lint-staged/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/is-unc-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", - "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", + "node_modules/lint-staged/node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", "dev": true, + "license": "MIT", "dependencies": { - "unc-path-regex": "^0.1.2" + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=18" + }, + "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==", + "node_modules/lint-staged/node_modules/restore-cursor/node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", "dev": true, + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-upper-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-upper-case/-/is-upper-case-2.0.2.tgz", - "integrity": "sha512-44pxmxAvnnAOwBg4tHPnkfvgjPwbc5QIsSstNU+YcJ1ovxVzCWpSGosPJOZh/a1tdl81fbgnLc9LLv+x2ywbPQ==", + "node_modules/lint-staged/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, - "dependencies": { - "tslib": "^2.0.3" + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/is-weakmap": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", - "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", + "node_modules/lint-staged/node_modules/slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "node_modules/lint-staged/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2" + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-weakset": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", - "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", + "node_modules/lint-staged/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "node_modules/lint-staged/node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" - }, - "node_modules/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/isomorphic-ws": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz", - "integrity": "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==", - "dev": true, - "peerDependencies": { - "ws": "*" - } - }, - "node_modules/isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", - "dev": true - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "node_modules/lint-staged/node_modules/wrap-ansi": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", + "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, "engines": { - "node": ">=8" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "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==", + "node_modules/listr2": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-4.0.5.tgz", + "integrity": "sha512-juGHV1doQdpNT3GSTs9IUN43QJb7KHdF9uqg7Vufs/tG9VTzpFphqF4pm/ICdAABGQxsyNn9CiYA3StkI6jpwA==", "dev": true, "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" + "cli-truncate": "^2.1.0", + "colorette": "^2.0.16", + "log-update": "^4.0.0", + "p-map": "^4.0.0", + "rfdc": "^1.3.0", + "rxjs": "^7.5.5", + "through": "^2.3.8", + "wrap-ansi": "^7.0.0" }, "engines": { - "node": ">=10" + "node": ">=12" + }, + "peerDependencies": { + "enquirer": ">= 2.3.0 < 3" + }, + "peerDependenciesMeta": { + "enquirer": { + "optional": true + } } }, - "node_modules/istanbul-lib-source-maps": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.4.tgz", - "integrity": "sha512-wHOoEsNJTVltaJp8eVkm8w+GVkVNHT2YDYo53YdzQEL2gWm1hBX5cGFR9hQJtuGLebidVX7et3+dmDZrmclduw==", + "node_modules/listr2/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": { - "@jridgewell/trace-mapping": "^0.3.23", - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0" + "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/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "node_modules/local-pkg": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.0.tgz", + "integrity": "sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==", "dev": true, + "license": "MIT", "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" + "mlly": "^1.4.2", + "pkg-types": "^1.0.3" }, "engines": { - "node": ">=8" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" } }, - "node_modules/iterall": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.3.0.tgz", - "integrity": "sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg==" + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, - "node_modules/iterator.prototype": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", - "integrity": "sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==", - "dev": true, - "dependencies": { - "define-properties": "^1.2.1", - "get-intrinsic": "^1.2.1", - "has-symbols": "^1.0.3", - "reflect.getprototypeof": "^1.0.4", - "set-function-name": "^2.0.1" - } + "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==" }, - "node_modules/jackspeak": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", - "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "dev": true + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "dev": true + }, + "node_modules/lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", + "dev": true + }, + "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": { - "@isaacs/cliui": "^8.0.2" + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" }, "engines": { - "node": ">=14" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/jiti": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", - "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==", - "bin": { - "jiti": "bin/jiti.js" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jose": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/jose/-/jose-5.2.3.tgz", - "integrity": "sha512-KUXdbctm1uHVL8BYhnyHkgp3zDX5KW8ZhAKVFEfUbU2P8Alpzjb+48hHvjOdQIyPshoblhzsuqOwEEAbtHVirA==", + "node_modules/log-update": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", + "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", "dev": true, - "funding": { - "url": "https://github.com/sponsors/panva" - } - }, - "node_modules/jotai": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/jotai/-/jotai-2.7.2.tgz", - "integrity": "sha512-6Ft5kpNu8p93Ssf1Faoza3hYQZRIYp7rioK8MwTTFnbQKwUyZElwquPwl1h6U0uo9hC0jr+ghO3gcSjc6P35/Q==", - "engines": { - "node": ">=12.20.0" + "dependencies": { + "ansi-escapes": "^4.3.0", + "cli-cursor": "^3.1.0", + "slice-ansi": "^4.0.0", + "wrap-ansi": "^6.2.0" }, - "peerDependencies": { - "@types/react": ">=17.0.0", - "react": ">=17.0.0" + "engines": { + "node": ">=10" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "react": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/js-levenshtein": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", - "integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==", + "node_modules/log-update/node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } }, - "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==", + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "dependencies": { - "argparse": "^2.0.1" + "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { - "js-yaml": "bin/js-yaml.js" + "loose-envify": "cli.js" } }, - "node_modules/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", - "dev": true - }, - "node_modules/jsdom": { - "version": "24.0.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-24.0.0.tgz", - "integrity": "sha512-UDS2NayCvmXSXVP6mpTj+73JnNQadZlr9N68189xib2tx5Mls7swlTNao26IoHv46BZJFvXygyRtyXd1feAk1A==", + "node_modules/loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", "dev": true, + "license": "MIT", "dependencies": { - "cssstyle": "^4.0.1", - "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.16.0", - "xml-name-validator": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "canvas": "^2.11.2" - }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } + "get-func-name": "^2.0.1" } }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dependencies": { + "tslib": "^2.0.3" } }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true - }, - "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==" - }, - "node_modules/json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", - "dev": true - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/json-stable-stringify": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.1.1.tgz", - "integrity": "sha512-SU/971Kt5qVQfJpyDveVhQ/vya+5hvrjClFOcr8c0Fq5aODJjMwutrOfCU+eCnVD5gpx1Q3fEqkyom77zH1iIg==", + "node_modules/lower-case-first": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case-first/-/lower-case-first-2.0.2.tgz", + "integrity": "sha512-EVm/rR94FJTZi3zefZ82fLWab+GX14LJN4HrWBcuo6Evmsl9hEfnqxgcHCKb9q+mNf6EVdsjx/qucYFIIB84pg==", "dev": true, "dependencies": { - "call-bind": "^1.0.5", - "isarray": "^2.0.5", - "jsonify": "^0.0.1", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "tslib": "^2.0.3" } }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true - }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "dev": true - }, - "node_modules/json-to-graphql-query": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/json-to-graphql-query/-/json-to-graphql-query-2.2.5.tgz", - "integrity": "sha512-5Nom9inkIMrtY992LMBBG1Zaekrc10JaRhyZgprwHBVMDtRgllTvzl0oBbg13wJsVZoSoFNNMaeIVQs0P04vsA==", - "license": "MIT" - }, - "node_modules/json-to-pretty-yaml": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/json-to-pretty-yaml/-/json-to-pretty-yaml-1.2.2.tgz", - "integrity": "sha512-rvm6hunfCcqegwYaG5T4yKJWxc9FXFgBVrcTZ4XfSVRwa5HA/Xs+vB/Eo9treYYHCeNM0nrSUr82V/M31Urc7A==", - "dev": true, + "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==", "dependencies": { - "remedial": "^1.0.7", - "remove-trailing-spaces": "^1.0.6" - }, - "engines": { - "node": ">= 0.2.0" + "yallist": "^3.0.2" } }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true, + "license": "MIT", "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" + "lz-string": "bin/bin.js" } }, - "node_modules/jsonc-parser": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", - "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==", - "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==", + "node_modules/magic-string": { + "version": "0.30.11", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz", + "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==", "dev": true, + "license": "MIT", "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" + "@jridgewell/sourcemap-codec": "^1.5.0" } }, - "node_modules/jsonify": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz", - "integrity": "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==", + "node_modules/magicast": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.3.tgz", + "integrity": "sha512-ZbrP1Qxnpoes8sz47AM0z08U+jW6TyRgZzcWy3Ma3vDhJttwMwAFDMMQFobwdBxByBD46JYmxRzeF7w2+wJEuw==", "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "dependencies": { + "@babel/parser": "^7.23.6", + "@babel/types": "^7.23.6", + "source-map-js": "^1.0.2" } }, - "node_modules/jsprim": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz", - "integrity": "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==", + "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, - "engines": [ - "node >=0.6.0" - ], "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jsx-ast-utils": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", - "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "node_modules/make-dir/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": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "object.assign": "^4.1.4", - "object.values": "^1.1.6" + "yallist": "^4.0.0" }, "engines": { - "node": ">=4.0" + "node": ">=10" } }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "node_modules/make-dir/node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dev": true, "dependencies": { - "json-buffer": "3.0.1" + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, - "node_modules/lazy-ass": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz", - "integrity": "sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==", + "node_modules/make-dir/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/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "optional": true, + "peer": true + }, + "node_modules/map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==", "dev": true, "engines": { - "node": "> 0.8" + "node": ">=0.10.0" } }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, + "node_modules/markdown-it": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", + "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" + "argparse": "^2.0.1", + "entities": "~2.1.0", + "linkify-it": "^3.0.1", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" }, - "engines": { - "node": ">= 0.8.0" + "bin": { + "markdown-it": "bin/markdown-it.js" } }, - "node_modules/lilconfig": { + "node_modules/markdown-it/node_modules/entities": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", - "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", - "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==" + "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", + "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } }, - "node_modules/linkify-it": { + "node_modules/markdown-table": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", - "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", - "dependencies": { - "uc.micro": "^1.0.1" + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.3.tgz", + "integrity": "sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/lint-staged": { - "version": "15.2.10", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.10.tgz", - "integrity": "sha512-5dY5t743e1byO19P9I4b3x8HJwalIznL5E1FWYnU6OWw33KxNBSLAc6Cy7F2PsFEO8FKnLwjwm5hx7aMF0jzZg==", - "dev": true, - "license": "MIT", + "node_modules/mdast-util-find-and-replace": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.1.tgz", + "integrity": "sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA==", "dependencies": { - "chalk": "~5.3.0", - "commander": "~12.1.0", - "debug": "~4.3.6", - "execa": "~8.0.1", - "lilconfig": "~3.1.2", - "listr2": "~8.2.4", - "micromatch": "~4.0.8", - "pidtree": "~0.6.0", - "string-argv": "~0.3.2", - "yaml": "~2.5.0" - }, - "bin": { - "lint-staged": "bin/lint-staged.js" - }, - "engines": { - "node": ">=18.12.0" + "@types/mdast": "^4.0.0", + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" }, "funding": { - "url": "https://opencollective.com/lint-staged" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/lint-staged/node_modules/ansi-escapes": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz", - "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==", - "dev": true, - "license": "MIT", - "dependencies": { - "environment": "^1.0.0" - }, + "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", "engines": { - "node": ">=18" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lint-staged/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" + "node_modules/mdast-util-from-markdown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.0.tgz", + "integrity": "sha512-n7MTOr/z+8NAX/wmhhDji8O3bRvPTV/U0oTCaZJkjhPSKTPhS3xufVhKGF8s1pJ7Ox4QgoIU7KHseh09S+9rTA==", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" }, "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/lint-staged/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" + "node_modules/mdast-util-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.0.0.tgz", + "integrity": "sha512-dgQEX5Amaq+DuUqf26jJqSK9qgixgd6rYDHAv4aTBuA92cTknZlKpPfa86Z/s8Dj8xsAQpFfBmPUHWJBWqS4Bw==", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-gfm-autolink-literal": "^2.0.0", + "mdast-util-gfm-footnote": "^2.0.0", + "mdast-util-gfm-strikethrough": "^2.0.0", + "mdast-util-gfm-table": "^2.0.0", + "mdast-util-gfm-task-list-item": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/lint-staged/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", - "dev": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.0.tgz", + "integrity": "sha512-FyzMsduZZHSc3i0Px3PQcBT4WJY/X/RCtEJKuybiC6sjPqLv7h1yqAkmILZtuxMSsUyaLUWNp71+vQH2zqp5cg==", + "dependencies": { + "@types/mdast": "^4.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-find-and-replace": "^3.0.0", + "micromark-util-character": "^2.0.0" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/lint-staged/node_modules/cli-cursor": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", - "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", - "dev": true, - "license": "MIT", + "node_modules/mdast-util-gfm-footnote": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.0.0.tgz", + "integrity": "sha512-5jOT2boTSVkMnQ7LTrd6n/18kqwjmuYqo7JUPe+tRCY6O7dAuTFMtTPauYYrMPpox9hlN0uOx/FL8XvEfG9/mQ==", "dependencies": { - "restore-cursor": "^5.0.0" - }, - "engines": { - "node": ">=18" + "@types/mdast": "^4.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/lint-staged/node_modules/cli-truncate": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", - "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", - "dev": true, - "license": "MIT", + "node_modules/mdast-util-gfm-strikethrough": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", + "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", "dependencies": { - "slice-ansi": "^5.0.0", - "string-width": "^7.0.0" - }, - "engines": { - "node": ">=18" + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lint-staged/node_modules/commander": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/lint-staged/node_modules/emoji-regex": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", - "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", - "dev": true, - "license": "MIT" - }, - "node_modules/lint-staged/node_modules/execa": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", - "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", - "dev": true, - "license": "MIT", + "node_modules/mdast-util-gfm-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", + "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^8.0.1", - "human-signals": "^5.0.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^4.1.0", - "strip-final-newline": "^3.0.0" - }, - "engines": { - "node": ">=16.17" + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" }, "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/lint-staged/node_modules/get-stream": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", - "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=16" + "node_modules/mdast-util-gfm-task-list-item": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", + "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/lint-staged/node_modules/human-signals": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", - "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=16.17.0" - } - }, - "node_modules/lint-staged/node_modules/is-fullwidth-code-point": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", - "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" + "node_modules/mdast-util-mdx-expression": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.0.tgz", + "integrity": "sha512-fGCu8eWdKUKNu5mohVGkhBXCXGnOTLuFqOvGMvdikr+J1w7lDJgxThOKpwRWzzbyXAU2hhSwsmssOY4yTokluw==", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/lint-staged/node_modules/is-stream": { + "node_modules/mdast-util-mdx-jsx": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lint-staged/node_modules/lilconfig": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", - "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14" + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.0.0.tgz", + "integrity": "sha512-XZuPPzQNBPAlaqsTTgRrcJnyFbSOBovSadFgbFu8SnuNgm+6Bdx1K+IWoitsmj6Lq6MNtI+ytOqwN70n//NaBA==", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-remove-position": "^5.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" }, "funding": { - "url": "https://github.com/sponsors/antonk52" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/lint-staged/node_modules/listr2": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.4.tgz", - "integrity": "sha512-opevsywziHd3zHCVQGAj8zu+Z3yHNkkoYhWIGnq54RrCVwLz0MozotJEDnKsIBLvkfLGN6BLOyAeRrYI0pKA4g==", - "dev": true, - "license": "MIT", + "node_modules/mdast-util-mdxjs-esm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", + "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", "dependencies": { - "cli-truncate": "^4.0.0", - "colorette": "^2.0.20", - "eventemitter3": "^5.0.1", - "log-update": "^6.1.0", - "rfdc": "^1.4.1", - "wrap-ansi": "^9.0.0" + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" }, - "engines": { - "node": ">=18.0.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/lint-staged/node_modules/log-update": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", - "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", - "dev": true, - "license": "MIT", + "node_modules/mdast-util-phrasing": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.0.0.tgz", + "integrity": "sha512-xadSsJayQIucJ9n053dfQwVu1kuXg7jCTdYsMK8rqzKZh52nLfSH/k0sAxE0u+pj/zKZX+o5wB+ML5mRayOxFA==", "dependencies": { - "ansi-escapes": "^7.0.0", - "cli-cursor": "^5.0.0", - "slice-ansi": "^7.1.0", - "strip-ansi": "^7.1.0", - "wrap-ansi": "^9.0.0" - }, - "engines": { - "node": ">=18" + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/lint-staged/node_modules/log-update/node_modules/is-fullwidth-code-point": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz", - "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==", - "dev": true, - "license": "MIT", + "node_modules/mdast-util-to-hast": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.1.0.tgz", + "integrity": "sha512-/e2l/6+OdGp/FB+ctrJ9Avz71AN/GRH3oi/3KAx/kMnoUsD6q0woXlDT8lLEeViVKE7oZxE7RXzvO3T8kF2/sA==", "dependencies": { - "get-east-asian-width": "^1.0.0" - }, - "engines": { - "node": ">=18" + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/lint-staged/node_modules/log-update/node_modules/slice-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz", - "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==", - "dev": true, - "license": "MIT", + "node_modules/mdast-util-to-markdown": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.0.tgz", + "integrity": "sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ==", "dependencies": { - "ansi-styles": "^6.2.1", - "is-fullwidth-code-point": "^5.0.0" - }, - "engines": { - "node": ">=18" + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" }, "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/lint-staged/node_modules/mimic-fn": { + "node_modules/mdast-util-to-string": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lint-staged/node_modules/npm-run-path": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", - "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", - "dev": true, - "license": "MIT", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", "dependencies": { - "path-key": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "@types/mdast": "^4.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/lint-staged/node_modules/onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-fn": "^4.0.0" - }, + "node_modules/mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==" + }, + "node_modules/mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==" + }, + "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==", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 8" } }, - "node_modules/lint-staged/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "dev": true, - "license": "MIT", + "node_modules/meros": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/meros/-/meros-1.3.0.tgz", + "integrity": "sha512-2BNGOimxEz5hmjUG2FwoxCt5HN7BXdaWyFqEwxPTrJzVdABtrL4TiHTcsWSFAxPQ/tOnEaQEJh3qWq71QRMY+w==", "engines": { - "node": ">=12" + "node": ">=13" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "@types/node": ">=13" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, - "node_modules/lint-staged/node_modules/restore-cursor": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", - "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", - "dev": true, - "license": "MIT", + "node_modules/micromark": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.0.tgz", + "integrity": "sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "dependencies": { - "onetime": "^7.0.0", - "signal-exit": "^4.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/lint-staged/node_modules/restore-cursor/node_modules/onetime": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", - "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", - "dev": true, - "license": "MIT", + "node_modules/micromark-core-commonmark": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.0.tgz", + "integrity": "sha512-jThOz/pVmAYUtkroV3D5c1osFXAMv9e0ypGDOIZuCeAe91/sD6BoE2Sjzt30yuXtwOYUmySOhMas/PVyh02itA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "dependencies": { - "mimic-function": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/lint-staged/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" + "node_modules/micromark-extension-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", + "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", + "dependencies": { + "micromark-extension-gfm-autolink-literal": "^2.0.0", + "micromark-extension-gfm-footnote": "^2.0.0", + "micromark-extension-gfm-strikethrough": "^2.0.0", + "micromark-extension-gfm-table": "^2.0.0", + "micromark-extension-gfm-tagfilter": "^2.0.0", + "micromark-extension-gfm-task-list-item": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/lint-staged/node_modules/slice-ansi": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", - "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", - "dev": true, - "license": "MIT", + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.0.0.tgz", + "integrity": "sha512-rTHfnpt/Q7dEAK1Y5ii0W8bhfJlVJFnJMHIPisfPK3gpVNuOP0VnRl96+YJ3RYWV/P4gFeQoGKNlT3RhuvpqAg==", "dependencies": { - "ansi-styles": "^6.0.0", - "is-fullwidth-code-point": "^4.0.0" - }, - "engines": { - "node": ">=12" + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" }, "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/lint-staged/node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "dev": true, - "license": "MIT", + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.0.0.tgz", + "integrity": "sha512-6Rzu0CYRKDv3BfLAUnZsSlzx3ak6HAoI85KTiijuKIz5UxZxbUI+pD6oHgw+6UtQuiRwnGRhzMmPRv4smcz0fg==", "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/lint-staged/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "license": "MIT", + "node_modules/micromark-extension-gfm-strikethrough": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.0.0.tgz", + "integrity": "sha512-c3BR1ClMp5fxxmwP6AoOY2fXO9U8uFMKs4ADD66ahLTNcwzSCyRVU4k7LPV5Nxo/VJiR4TdzxRQY2v3qIUceCw==", "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" }, "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/lint-staged/node_modules/strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" + "node_modules/micromark-extension-gfm-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.0.0.tgz", + "integrity": "sha512-PoHlhypg1ItIucOaHmKE8fbin3vTLpDOUg8KAr8gRCF1MOZI9Nquq2i/44wFvviM4WuxJzc3demT8Y3dkfvYrw==", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/lint-staged/node_modules/wrap-ansi": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", - "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", - "dev": true, - "license": "MIT", + "node_modules/micromark-extension-gfm-tagfilter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", + "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", "dependencies": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" + "micromark-util-types": "^2.0.0" }, "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/listr2": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-4.0.5.tgz", - "integrity": "sha512-juGHV1doQdpNT3GSTs9IUN43QJb7KHdF9uqg7Vufs/tG9VTzpFphqF4pm/ICdAABGQxsyNn9CiYA3StkI6jpwA==", - "dev": true, + "node_modules/micromark-extension-gfm-task-list-item": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.0.1.tgz", + "integrity": "sha512-cY5PzGcnULaN5O7T+cOzfMoHjBW7j+T9D2sucA5d/KbsBTPcYdebm9zUd9zzdgJGCwahV+/W78Z3nbulBYVbTw==", "dependencies": { - "cli-truncate": "^2.1.0", - "colorette": "^2.0.16", - "log-update": "^4.0.0", - "p-map": "^4.0.0", - "rfdc": "^1.3.0", - "rxjs": "^7.5.5", - "through": "^2.3.8", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - }, - "peerDependencies": { - "enquirer": ">= 2.3.0 < 3" + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" }, - "peerDependenciesMeta": { - "enquirer": { - "optional": true - } - } - }, - "node_modules/listr2/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/local-pkg": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.0.tgz", - "integrity": "sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==", - "dev": true, - "dependencies": { - "mlly": "^1.4.2", - "pkg-types": "^1.0.3" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "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==" - }, - "node_modules/lodash.isequal": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", - "dev": true - }, - "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.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", - "dev": true - }, - "node_modules/lodash.sortby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", - "dev": true - }, - "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-update": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", - "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", - "dev": true, - "dependencies": { - "ansi-escapes": "^4.3.0", - "cli-cursor": "^3.1.0", - "slice-ansi": "^4.0.0", - "wrap-ansi": "^6.2.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-update/node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/loglevel": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.1.tgz", - "integrity": "sha512-hP3I3kCrDIMuRwAwHltphhDM1r8i55H33GgqjXbrisuJhF4kRhW1dNuxsRklp4bXl8DSdLaNLuiL4A/LWRfxvg==", - "dev": true, - "engines": { - "node": ">= 0.6.0" - }, - "funding": { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/loglevel" - } - }, - "node_modules/loglevel-colored-level-prefix": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/loglevel-colored-level-prefix/-/loglevel-colored-level-prefix-1.0.0.tgz", - "integrity": "sha512-u45Wcxxc+SdAlh4yeF/uKlC1SPUPCy0gullSNKXod5I4bmifzk+Q4lSLExNEVn19tGaJipbZ4V4jbFn79/6mVA==", - "dev": true, - "dependencies": { - "chalk": "^1.1.3", - "loglevel": "^1.4.1" - } - }, - "node_modules/loglevel-colored-level-prefix/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/loglevel-colored-level-prefix/node_modules/ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/loglevel-colored-level-prefix/node_modules/chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", - "dev": true, - "dependencies": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/loglevel-colored-level-prefix/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/loglevel-colored-level-prefix/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", - "dev": true, - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/loglevel-colored-level-prefix/node_modules/supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/longest-streak": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", - "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/loupe": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", - "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", - "dev": true, - "dependencies": { - "get-func-name": "^2.0.1" - } - }, - "node_modules/lower-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", - "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/lower-case-first": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lower-case-first/-/lower-case-first-2.0.2.tgz", - "integrity": "sha512-EVm/rR94FJTZi3zefZ82fLWab+GX14LJN4HrWBcuo6Evmsl9hEfnqxgcHCKb9q+mNf6EVdsjx/qucYFIIB84pg==", - "dev": true, - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/lz-string": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", - "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", - "dev": true, - "bin": { - "lz-string": "bin/bin.js" - } - }, - "node_modules/magic-string": { - "version": "0.30.8", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", - "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==", - "dev": true, - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/magicast": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.3.tgz", - "integrity": "sha512-ZbrP1Qxnpoes8sz47AM0z08U+jW6TyRgZzcWy3Ma3vDhJttwMwAFDMMQFobwdBxByBD46JYmxRzeF7w2+wJEuw==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.23.6", - "@babel/types": "^7.23.6", - "source-map-js": "^1.0.2" - } - }, - "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/make-dir/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/make-dir/node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/make-dir/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/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "devOptional": true - }, - "node_modules/map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/markdown-it": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", - "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", - "dependencies": { - "argparse": "^2.0.1", - "entities": "~2.1.0", - "linkify-it": "^3.0.1", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" - }, - "bin": { - "markdown-it": "bin/markdown-it.js" - } - }, - "node_modules/markdown-it/node_modules/entities": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", - "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/markdown-table": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.3.tgz", - "integrity": "sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/mdast-util-find-and-replace": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.1.tgz", - "integrity": "sha512-SG21kZHGC3XRTSUhtofZkBzZTJNM5ecCi0SK2IMKmSXR8vO3peL+kb1O0z7Zl83jKtutG4k5Wv/W7V3/YHvzPA==", - "dependencies": { - "@types/mdast": "^4.0.0", - "escape-string-regexp": "^5.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mdast-util-from-markdown": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.0.tgz", - "integrity": "sha512-n7MTOr/z+8NAX/wmhhDji8O3bRvPTV/U0oTCaZJkjhPSKTPhS3xufVhKGF8s1pJ7Ox4QgoIU7KHseh09S+9rTA==", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "mdast-util-to-string": "^4.0.0", - "micromark": "^4.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-decode-string": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.0.0.tgz", - "integrity": "sha512-dgQEX5Amaq+DuUqf26jJqSK9qgixgd6rYDHAv4aTBuA92cTknZlKpPfa86Z/s8Dj8xsAQpFfBmPUHWJBWqS4Bw==", - "dependencies": { - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-gfm-autolink-literal": "^2.0.0", - "mdast-util-gfm-footnote": "^2.0.0", - "mdast-util-gfm-strikethrough": "^2.0.0", - "mdast-util-gfm-table": "^2.0.0", - "mdast-util-gfm-task-list-item": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-autolink-literal": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.0.tgz", - "integrity": "sha512-FyzMsduZZHSc3i0Px3PQcBT4WJY/X/RCtEJKuybiC6sjPqLv7h1yqAkmILZtuxMSsUyaLUWNp71+vQH2zqp5cg==", - "dependencies": { - "@types/mdast": "^4.0.0", - "ccount": "^2.0.0", - "devlop": "^1.0.0", - "mdast-util-find-and-replace": "^3.0.0", - "micromark-util-character": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-footnote": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.0.0.tgz", - "integrity": "sha512-5jOT2boTSVkMnQ7LTrd6n/18kqwjmuYqo7JUPe+tRCY6O7dAuTFMtTPauYYrMPpox9hlN0uOx/FL8XvEfG9/mQ==", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.1.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-strikethrough": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", - "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-table": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", - "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "markdown-table": "^3.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-task-list-item": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", - "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx-expression": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.0.tgz", - "integrity": "sha512-fGCu8eWdKUKNu5mohVGkhBXCXGnOTLuFqOvGMvdikr+J1w7lDJgxThOKpwRWzzbyXAU2hhSwsmssOY4yTokluw==", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx-jsx": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.0.0.tgz", - "integrity": "sha512-XZuPPzQNBPAlaqsTTgRrcJnyFbSOBovSadFgbFu8SnuNgm+6Bdx1K+IWoitsmj6Lq6MNtI+ytOqwN70n//NaBA==", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "ccount": "^2.0.0", - "devlop": "^1.1.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "parse-entities": "^4.0.0", - "stringify-entities": "^4.0.0", - "unist-util-remove-position": "^5.0.0", - "unist-util-stringify-position": "^4.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdxjs-esm": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", - "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-phrasing": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.0.0.tgz", - "integrity": "sha512-xadSsJayQIucJ9n053dfQwVu1kuXg7jCTdYsMK8rqzKZh52nLfSH/k0sAxE0u+pj/zKZX+o5wB+ML5mRayOxFA==", - "dependencies": { - "@types/mdast": "^4.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-hast": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.1.0.tgz", - "integrity": "sha512-/e2l/6+OdGp/FB+ctrJ9Avz71AN/GRH3oi/3KAx/kMnoUsD6q0woXlDT8lLEeViVKE7oZxE7RXzvO3T8kF2/sA==", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@ungap/structured-clone": "^1.0.0", - "devlop": "^1.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "trim-lines": "^3.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-markdown": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.0.tgz", - "integrity": "sha512-SR2VnIEdVNCJbP6y7kVTJgPLifdr8WEU440fQec7qHoHOUz/oJ2jmNRqdDQ3rbiStOXb2mCDGTuwsK5OPUgYlQ==", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "longest-streak": "^3.0.0", - "mdast-util-phrasing": "^4.0.0", - "mdast-util-to-string": "^4.0.0", - "micromark-util-decode-string": "^2.0.0", - "unist-util-visit": "^5.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", - "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", - "dependencies": { - "@types/mdast": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdn-data": { - "version": "2.0.30", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", - "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==" - }, - "node_modules/mdurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==" - }, - "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==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/meros": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/meros/-/meros-1.3.0.tgz", - "integrity": "sha512-2BNGOimxEz5hmjUG2FwoxCt5HN7BXdaWyFqEwxPTrJzVdABtrL4TiHTcsWSFAxPQ/tOnEaQEJh3qWq71QRMY+w==", - "engines": { - "node": ">=13" - }, - "peerDependencies": { - "@types/node": ">=13" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - } - } - }, - "node_modules/micromark": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.0.tgz", - "integrity": "sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "@types/debug": "^4.0.0", - "debug": "^4.0.0", - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-core-commonmark": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.0.tgz", - "integrity": "sha512-jThOz/pVmAYUtkroV3D5c1osFXAMv9e0ypGDOIZuCeAe91/sD6BoE2Sjzt30yuXtwOYUmySOhMas/PVyh02itA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-factory-destination": "^2.0.0", - "micromark-factory-label": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-factory-title": "^2.0.0", - "micromark-factory-whitespace": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-html-tag-name": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-gfm": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", - "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", - "dependencies": { - "micromark-extension-gfm-autolink-literal": "^2.0.0", - "micromark-extension-gfm-footnote": "^2.0.0", - "micromark-extension-gfm-strikethrough": "^2.0.0", - "micromark-extension-gfm-table": "^2.0.0", - "micromark-extension-gfm-tagfilter": "^2.0.0", - "micromark-extension-gfm-task-list-item": "^2.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-autolink-literal": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.0.0.tgz", - "integrity": "sha512-rTHfnpt/Q7dEAK1Y5ii0W8bhfJlVJFnJMHIPisfPK3gpVNuOP0VnRl96+YJ3RYWV/P4gFeQoGKNlT3RhuvpqAg==", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-footnote": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.0.0.tgz", - "integrity": "sha512-6Rzu0CYRKDv3BfLAUnZsSlzx3ak6HAoI85KTiijuKIz5UxZxbUI+pD6oHgw+6UtQuiRwnGRhzMmPRv4smcz0fg==", - "dependencies": { - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-strikethrough": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.0.0.tgz", - "integrity": "sha512-c3BR1ClMp5fxxmwP6AoOY2fXO9U8uFMKs4ADD66ahLTNcwzSCyRVU4k7LPV5Nxo/VJiR4TdzxRQY2v3qIUceCw==", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-table": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.0.0.tgz", - "integrity": "sha512-PoHlhypg1ItIucOaHmKE8fbin3vTLpDOUg8KAr8gRCF1MOZI9Nquq2i/44wFvviM4WuxJzc3demT8Y3dkfvYrw==", - "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-tagfilter": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", - "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", - "dependencies": { - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-task-list-item": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.0.1.tgz", - "integrity": "sha512-cY5PzGcnULaN5O7T+cOzfMoHjBW7j+T9D2sucA5d/KbsBTPcYdebm9zUd9zzdgJGCwahV+/W78Z3nbulBYVbTw==", - "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, "node_modules/micromark-factory-destination": { @@ -14750,24 +13151,16 @@ } }, "node_modules/mlly": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.6.1.tgz", - "integrity": "sha512-vLgaHvaeunuOXHSmEbZ9izxPx3USsk8KCQ8iC+aTlp5sKRSoZvwhHh5L9VbKSaVC6sJDqbyohIS76E2VmHIPAA==", - "dev": true, - "dependencies": { - "acorn": "^8.11.3", - "pathe": "^1.1.2", - "pkg-types": "^1.0.3", - "ufo": "^1.3.2" - } - }, - "node_modules/mri": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", - "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", - "dev": true, - "engines": { - "node": ">=4" + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.1.tgz", + "integrity": "sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.3", + "pathe": "^1.1.2", + "pkg-types": "^1.1.1", + "ufo": "^1.5.3" } }, "node_modules/ms": { @@ -14808,12 +13201,6 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, "node_modules/neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", @@ -14964,13 +13351,14 @@ } }, "node_modules/object-is": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", - "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" }, "engines": { "node": ">= 0.4" @@ -15006,86 +13394,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/object.entries": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz", - "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.fromentries": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", - "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.groupby": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", - "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.hasown": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.4.tgz", - "integrity": "sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==", - "dev": true, - "dependencies": { - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.values": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", - "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -15201,23 +13509,6 @@ "node": ">=8" } }, - "node_modules/optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", - "dev": true, - "dependencies": { - "@aashutoshrathi/word-wrap": "^1.2.3", - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, "node_modules/ora": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", @@ -15271,21 +13562,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/p-map": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", @@ -15505,13 +13781,15 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/pathval": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", "dev": true, + "license": "MIT", "engines": { "node": "*" } @@ -15526,7 +13804,8 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/picocolors": { "version": "1.1.0", @@ -15574,24 +13853,25 @@ } }, "node_modules/pkg-types": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.0.3.tgz", - "integrity": "sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.2.0.tgz", + "integrity": "sha512-+ifYuSSqOQ8CqP4MbZA5hDpb97n3E8SVWdJe+Wms9kj745lmd3b7EZJiqvmLwAlmRfjrI7Hi5z3kdBJ93lFNPA==", "dev": true, + "license": "MIT", "dependencies": { - "jsonc-parser": "^3.2.0", - "mlly": "^1.2.0", - "pathe": "^1.1.0" + "confbox": "^0.1.7", + "mlly": "^1.7.1", + "pathe": "^1.1.2" } }, "node_modules/playwright": { - "version": "1.47.0", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.47.0.tgz", - "integrity": "sha512-jOWiRq2pdNAX/mwLiwFYnPHpEZ4rM+fRSQpRHwEwZlP2PUANvL3+aJOF/bvISMhFD30rqMxUB4RJx9aQbfh4Ww==", + "version": "1.47.2", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.47.2.tgz", + "integrity": "sha512-nx1cLMmQWqmA3UsnjaaokyoUpdVaaDhJhMoxX2qj3McpjnsqFHs516QAKYhqHAgOP+oCFTEOCOAaD1RgD/RQfA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.47.0" + "playwright-core": "1.47.2" }, "bin": { "playwright": "cli.js" @@ -15604,9 +13884,9 @@ } }, "node_modules/playwright-core": { - "version": "1.47.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.47.0.tgz", - "integrity": "sha512-1DyHT8OqkcfCkYUD9zzUTfg7EfTd+6a8MkD/NWOvjo0u/SCNd5YmY/lJwFvUZOxJbWNds+ei7ic2+R/cRz/PDg==", + "version": "1.47.2", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.47.2.tgz", + "integrity": "sha512-3JvMfF+9LJfe16l7AbSmU555PaTl2tPyQsVInqm3id16pdDfvZ8TTZ/pyzmkbDrZTQefyzU7AIHlZqQnxpqHVQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -15796,253 +14076,8 @@ }, "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==" - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "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-eslint": { - "version": "16.3.0", - "resolved": "https://registry.npmjs.org/prettier-eslint/-/prettier-eslint-16.3.0.tgz", - "integrity": "sha512-Lh102TIFCr11PJKUMQ2kwNmxGhTsv/KzUg9QYF2Gkw259g/kPgndZDWavk7/ycbRvj2oz4BPZ1gCU8bhfZH/Xg==", - "dev": true, - "dependencies": { - "@typescript-eslint/parser": "^6.7.5", - "common-tags": "^1.4.0", - "dlv": "^1.1.0", - "eslint": "^8.7.0", - "indent-string": "^4.0.0", - "lodash.merge": "^4.6.0", - "loglevel-colored-level-prefix": "^1.0.0", - "prettier": "^3.0.1", - "pretty-format": "^29.7.0", - "require-relative": "^0.8.7", - "typescript": "^5.2.2", - "vue-eslint-parser": "^9.1.0" - }, - "engines": { - "node": ">=16.10.0" - }, - "peerDependencies": { - "prettier-plugin-svelte": "^3.0.0", - "svelte-eslint-parser": "*" - }, - "peerDependenciesMeta": { - "prettier-plugin-svelte": { - "optional": true - }, - "svelte-eslint-parser": { - "optional": true - } - } - }, - "node_modules/prettier-eslint/node_modules/@typescript-eslint/parser": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", - "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/prettier-eslint/node_modules/@typescript-eslint/scope-manager": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", - "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/prettier-eslint/node_modules/@typescript-eslint/types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", - "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", - "dev": true, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/prettier-eslint/node_modules/@typescript-eslint/typescript-estree": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", - "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "9.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/prettier-eslint/node_modules/@typescript-eslint/visitor-keys": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", - "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.21.0", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/prettier-eslint/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/prettier-eslint/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==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/prettier-eslint/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/prettier-eslint/node_modules/prettier": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.2.tgz", - "integrity": "sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==", - "dev": true, - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/prettier-eslint/node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/prettier-eslint/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true - }, - "node_modules/prettier-eslint/node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, "node_modules/pretty-bytes": { "version": "5.6.0", @@ -16061,6 +14096,7 @@ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", @@ -16075,6 +14111,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -16086,95 +14123,8 @@ "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true - }, - "node_modules/pretty-quick": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/pretty-quick/-/pretty-quick-3.3.1.tgz", - "integrity": "sha512-3b36UXfYQ+IXXqex6mCca89jC8u0mYLqFAN5eTQKoXO6oCQYcIVYZEB/5AlBHI7JPYygReM2Vv6Vom/Gln7fBg==", - "dev": true, - "dependencies": { - "execa": "^4.1.0", - "find-up": "^4.1.0", - "ignore": "^5.3.0", - "mri": "^1.2.0", - "picocolors": "^1.0.0", - "picomatch": "^3.0.1", - "tslib": "^2.6.2" - }, - "bin": { - "pretty-quick": "dist/cli.js" - }, - "engines": { - "node": ">=10.13" - }, - "peerDependencies": { - "prettier": "^2.0.0" - } - }, - "node_modules/pretty-quick/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/pretty-quick/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/pretty-quick/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/pretty-quick/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/pretty-quick/node_modules/picomatch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-3.0.1.tgz", - "integrity": "sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag==", "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } + "license": "MIT" }, "node_modules/prismjs": { "version": "1.29.0", @@ -16262,12 +14212,13 @@ } }, "node_modules/qs": { - "version": "6.10.4", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.4.tgz", - "integrity": "sha512-OQiU+C+Ds5qiH91qh/mg0w+8nwQuLjM4F4M/PbmhDOoYehPh+Fb0bDjtR1sOvy7YKxvj28Y/M0PhP5uVX0kB+g==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.0.4" + "side-channel": "^1.0.6" }, "engines": { "node": ">=0.6" @@ -16780,27 +14731,6 @@ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" }, - "node_modules/reflect.getprototypeof": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz", - "integrity": "sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.1", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "globalthis": "^1.0.3", - "which-builtin-type": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -17046,12 +14976,6 @@ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, - "node_modules/require-relative": { - "version": "0.8.7", - "resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz", - "integrity": "sha512-AKGr4qvHiryxRb19m3PsLRGuKVAbJLUD7E6eOaHkfKhwc+vSgVOCY5xNvm9EkolBKTOf0GrQAZKLimOCz81Khg==", - "dev": true - }, "node_modules/requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", @@ -17083,15 +15007,6 @@ "node": ">=8" } }, - "node_modules/resolve-pkg-maps": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "dev": true, - "funding": { - "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" - } - }, "node_modules/response-iterator": { "version": "0.2.6", "resolved": "https://registry.npmjs.org/response-iterator/-/response-iterator-0.2.6.tgz", @@ -17129,21 +15044,6 @@ "dev": true, "license": "MIT" }, - "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/rollup": { "version": "4.22.4", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.22.4.tgz", @@ -17225,24 +15125,6 @@ "tslib": "^2.1.0" } }, - "node_modules/safe-array-concat": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", - "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4", - "has-symbols": "^1.0.3", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -17263,23 +15145,6 @@ } ] }, - "node_modules/safe-regex-test": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", - "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-regex": "^1.1.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -17464,7 +15329,8 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/signal-exit": { "version": "3.0.7", @@ -17561,6 +15427,7 @@ "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", "dev": true, + "license": "MIT", "dependencies": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", @@ -17585,7 +15452,8 @@ "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/std-env": { "version": "3.7.0", @@ -17598,6 +15466,7 @@ "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", "dev": true, + "license": "MIT", "dependencies": { "internal-slot": "^1.0.4" }, @@ -17665,81 +15534,6 @@ "node": ">=8" } }, - "node_modules/string.prototype.matchall": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz", - "integrity": "sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.7", - "regexp.prototype.flags": "^1.5.2", - "set-function-name": "^2.0.2", - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trim": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", - "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.0", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", - "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", - "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/stringify-entities": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.3.tgz", @@ -17776,15 +15570,6 @@ "node": ">=8" } }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", @@ -17794,18 +15579,6 @@ "node": ">=6" } }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/strip-literal": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.1.0.tgz", @@ -18149,12 +15922,6 @@ "node": ">=8" } }, - "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", @@ -18195,16 +15962,18 @@ "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==" }, "node_modules/tinybench": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.6.0.tgz", - "integrity": "sha512-N8hW3PG/3aOoZAN5V/NSAEDz0ZixDSSt5b/a05iqtpgfLWMSVuCo7w0k2vVvEjdrIoeGqZzweX2WlyioNIHchA==", - "dev": true + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" }, "node_modules/tinypool": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.3.tgz", - "integrity": "sha512-Ud7uepAklqRH1bvwy22ynrliC7Dljz7Tm8M/0RBUW+YRa4YHhZ6e4PpgE+fu1zr/WqB1kbeuVrdfeuyIBpy4tw==", + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.4.tgz", + "integrity": "sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=14.0.0" } @@ -18214,6 +15983,7 @@ "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.1.tgz", "integrity": "sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==", "dev": true, + "license": "MIT", "engines": { "node": ">=14.0.0" } @@ -18228,15 +15998,13 @@ } }, "node_modules/tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", "dev": true, - "dependencies": { - "rimraf": "^3.0.0" - }, + "license": "MIT", "engines": { - "node": ">=8.17.0" + "node": ">=14.14" } }, "node_modules/to-fast-properties": { @@ -18335,18 +16103,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/ts-api-utils": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", - "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", - "dev": true, - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "typescript": ">=4.2.0" - } - }, "node_modules/ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", @@ -18373,7 +16129,8 @@ "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "devOptional": true, + "optional": true, + "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -18416,7 +16173,8 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "devOptional": true + "optional": true, + "peer": true }, "node_modules/ts-toolbelt": { "version": "9.6.0", @@ -18435,36 +16193,12 @@ "node": "^18 || >=20" }, "peerDependencies": { - "typescript": "^5.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/tsconfig-paths": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", - "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", - "dev": true, - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } - }, - "node_modules/tsconfig-paths/node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "dependencies": { - "minimist": "^1.2.0" + "typescript": "^5.0.0" }, - "bin": { - "json5": "lib/cli.js" + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, "node_modules/tslib": { @@ -18477,6 +16211,7 @@ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", "dev": true, + "license": "Apache-2.0", "dependencies": { "safe-buffer": "^5.0.1" }, @@ -18488,25 +16223,15 @@ "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", - "dev": true - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } + "license": "Unlicense" }, "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } @@ -18523,79 +16248,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/typed-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", - "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/typed-array-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", - "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", - "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-length": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", - "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/types-ramda": { "version": "0.29.10", "resolved": "https://registry.npmjs.org/types-ramda/-/types-ramda-0.29.10.tgz", @@ -18618,32 +16270,6 @@ "node": ">=14.17" } }, - "node_modules/typescript-eslint": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-7.16.0.tgz", - "integrity": "sha512-kaVRivQjOzuoCXU6+hLnjo3/baxyzWVO5GrnExkFzETRYJKVHYkrJglOu2OCm8Hi9RPDWX1PTNNTpU5KRV0+RA==", - "dev": true, - "dependencies": { - "@typescript-eslint/eslint-plugin": "7.16.0", - "@typescript-eslint/parser": "7.16.0", - "@typescript-eslint/utils": "7.16.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, "node_modules/ua-parser-js": { "version": "1.0.37", "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.37.tgz", @@ -18673,10 +16299,11 @@ "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==" }, "node_modules/ufo": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.3.tgz", - "integrity": "sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==", - "dev": true + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz", + "integrity": "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==", + "dev": true, + "license": "MIT" }, "node_modules/uglify-js": { "version": "3.17.4", @@ -18690,21 +16317,6 @@ "node": ">=0.8.0" } }, - "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/unc-path-regex": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", @@ -19062,6 +16674,7 @@ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "dev": true, + "license": "MIT", "bin": { "uuid": "dist/bin/uuid" } @@ -19070,7 +16683,8 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "devOptional": true + "optional": true, + "peer": true }, "node_modules/v8-to-istanbul": { "version": "9.2.0", @@ -19103,6 +16717,7 @@ "engines": [ "node >=0.6.0" ], + "license": "MIT", "dependencies": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", @@ -19221,6 +16836,7 @@ "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.4.0.tgz", "integrity": "sha512-VZDAseqjrHgNd4Kh8icYHWzTKSCZMhia7GyHfhtzLW33fZlG9SwsB6CEhgyVOWkJfJ2pFLrp/Gj1FSfAiqH9Lw==", "dev": true, + "license": "MIT", "dependencies": { "cac": "^6.7.14", "debug": "^4.3.4", @@ -19261,6 +16877,7 @@ "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.4.0.tgz", "integrity": "sha512-gujzn0g7fmwf83/WzrDTnncZt2UiXP41mHuFYFrdwaLRVQ6JYQEiME2IfEjU3vcFL3VKa75XhI3lFgn+hfVsQw==", "dev": true, + "license": "MIT", "dependencies": { "@vitest/expect": "1.4.0", "@vitest/runner": "1.4.0", @@ -19326,6 +16943,7 @@ "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", "dev": true, + "license": "MIT", "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^8.0.1", @@ -19349,6 +16967,7 @@ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", "dev": true, + "license": "MIT", "engines": { "node": ">=16" }, @@ -19361,6 +16980,7 @@ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=16.17.0" } @@ -19370,6 +16990,7 @@ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", "dev": true, + "license": "MIT", "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, @@ -19382,6 +17003,7 @@ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -19394,6 +17016,7 @@ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^4.0.0" }, @@ -19409,6 +17032,7 @@ "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", "dev": true, + "license": "MIT", "dependencies": { "mimic-fn": "^4.0.0" }, @@ -19424,6 +17048,7 @@ "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -19436,6 +17061,7 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, + "license": "ISC", "engines": { "node": ">=14" }, @@ -19448,6 +17074,7 @@ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -19460,42 +17087,6 @@ "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==" }, - "node_modules/vue-eslint-parser": { - "version": "9.4.3", - "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.4.3.tgz", - "integrity": "sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==", - "dev": true, - "dependencies": { - "debug": "^4.3.4", - "eslint-scope": "^7.1.1", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.3.1", - "esquery": "^1.4.0", - "lodash": "^4.17.21", - "semver": "^7.3.6" - }, - "engines": { - "node": "^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=6.0.0" - } - }, - "node_modules/vue-eslint-parser/node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/w3c-keyname": { "version": "2.2.8", "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", @@ -19539,11 +17130,6 @@ "node": ">= 8" } }, - "node_modules/web-vitals": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-2.1.4.tgz", - "integrity": "sha512-sVWcwhU5mX6crfI5Vd2dC4qchyTqxV8URinzt25XqVh+bHEPGH4C3NPrNionCP7Obx59wrYEbNlw4Z8sjALzZg==" - }, "node_modules/webcrypto-core": { "version": "1.7.9", "resolved": "https://registry.npmjs.org/webcrypto-core/-/webcrypto-core-1.7.9.tgz", @@ -19642,32 +17228,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/which-builtin-type": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.3.tgz", - "integrity": "sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==", - "dev": true, - "dependencies": { - "function.prototype.name": "^1.1.5", - "has-tostringtag": "^1.0.0", - "is-async-function": "^2.0.0", - "is-date-object": "^1.0.5", - "is-finalizationregistry": "^1.0.2", - "is-generator-function": "^1.0.10", - "is-regex": "^1.1.4", - "is-weakref": "^1.0.2", - "isarray": "^2.0.5", - "which-boxed-primitive": "^1.0.2", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.9" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/which-collection": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", @@ -19709,10 +17269,11 @@ } }, "node_modules/why-is-node-running": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.2.2.tgz", - "integrity": "sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", "dev": true, + "license": "MIT", "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" @@ -19875,7 +17436,8 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "devOptional": true, + "optional": true, + "peer": true, "engines": { "node": ">=6" } @@ -19916,12 +17478,6 @@ } }, "dependencies": { - "@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true - }, "@alloc/quick-lru": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", @@ -21375,6 +18931,78 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "@biomejs/biome": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-1.9.3.tgz", + "integrity": "sha512-POjAPz0APAmX33WOQFGQrwLvlu7WLV4CFJMlB12b6ZSg+2q6fYu9kZwLCOA+x83zXfcPd1RpuWOKJW0GbBwLIQ==", + "dev": true, + "requires": { + "@biomejs/cli-darwin-arm64": "1.9.3", + "@biomejs/cli-darwin-x64": "1.9.3", + "@biomejs/cli-linux-arm64": "1.9.3", + "@biomejs/cli-linux-arm64-musl": "1.9.3", + "@biomejs/cli-linux-x64": "1.9.3", + "@biomejs/cli-linux-x64-musl": "1.9.3", + "@biomejs/cli-win32-arm64": "1.9.3", + "@biomejs/cli-win32-x64": "1.9.3" + } + }, + "@biomejs/cli-darwin-arm64": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.9.3.tgz", + "integrity": "sha512-QZzD2XrjJDUyIZK+aR2i5DDxCJfdwiYbUKu9GzkCUJpL78uSelAHAPy7m0GuPMVtF/Uo+OKv97W3P9nuWZangQ==", + "dev": true, + "optional": true + }, + "@biomejs/cli-darwin-x64": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.9.3.tgz", + "integrity": "sha512-vSCoIBJE0BN3SWDFuAY/tRavpUtNoqiceJ5PrU3xDfsLcm/U6N93JSM0M9OAiC/X7mPPfejtr6Yc9vSgWlEgVw==", + "dev": true, + "optional": true + }, + "@biomejs/cli-linux-arm64": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.9.3.tgz", + "integrity": "sha512-vJkAimD2+sVviNTbaWOGqEBy31cW0ZB52KtpVIbkuma7PlfII3tsLhFa+cwbRAcRBkobBBhqZ06hXoZAN8NODQ==", + "dev": true, + "optional": true + }, + "@biomejs/cli-linux-arm64-musl": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.9.3.tgz", + "integrity": "sha512-VBzyhaqqqwP3bAkkBrhVq50i3Uj9+RWuj+pYmXrMDgjS5+SKYGE56BwNw4l8hR3SmYbLSbEo15GcV043CDSk+Q==", + "dev": true, + "optional": true + }, + "@biomejs/cli-linux-x64": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-1.9.3.tgz", + "integrity": "sha512-x220V4c+romd26Mu1ptU+EudMXVS4xmzKxPVb9mgnfYlN4Yx9vD5NZraSx/onJnd3Gh/y8iPUdU5CDZJKg9COA==", + "dev": true, + "optional": true + }, + "@biomejs/cli-linux-x64-musl": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.9.3.tgz", + "integrity": "sha512-TJmnOG2+NOGM72mlczEsNki9UT+XAsMFAOo8J0me/N47EJ/vkLXxf481evfHLlxMejTY6IN8SdRSiPVLv6AHlA==", + "dev": true, + "optional": true + }, + "@biomejs/cli-win32-arm64": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.9.3.tgz", + "integrity": "sha512-lg/yZis2HdQGsycUvHWSzo9kOvnGgvtrYRgoCEwPBwwAL8/6crOp3+f47tPwI/LI1dZrhSji7PNsGKGHbwyAhw==", + "dev": true, + "optional": true + }, + "@biomejs/cli-win32-x64": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-1.9.3.tgz", + "integrity": "sha512-cQMy2zanBkVLpmmxXdK6YePzmZx0s5Z7KEnwmrW54rcXK3myCNbQa09SwGZ8i/8sLw0H9F3X7K4rxVNGU8/D4Q==", + "dev": true, + "optional": true + }, "@codemirror/autocomplete": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.2.0.tgz", @@ -21569,7 +19197,8 @@ "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "devOptional": true, + "optional": true, + "peer": true, "requires": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -21578,7 +19207,8 @@ "version": "0.3.9", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "devOptional": true, + "optional": true, + "peer": true, "requires": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -21587,9 +19217,9 @@ } }, "@cypress/request": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.1.tgz", - "integrity": "sha512-TWivJlJi8ZDx2wGOw1dbLuHJKUYX7bWySw377nlnGOW3hP9/MUKIsEdXT/YngWxVdgNCHRBmFlBipE+5/2ZZlQ==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@cypress/request/-/request-3.0.5.tgz", + "integrity": "sha512-v+XHd9XmWbufxF1/bTaVm2yhbxY+TB4YtWRqF2zaXBlDNMkls34KiATz0AVDLavL3iB6bQk9/7n3oY1EoLSWGA==", "dev": true, "requires": { "aws-sign2": "~0.7.0", @@ -21598,31 +19228,18 @@ "combined-stream": "~1.0.6", "extend": "~3.0.2", "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "http-signature": "~1.3.6", + "form-data": "~4.0.0", + "http-signature": "~1.4.0", "is-typedarray": "~1.0.0", "isstream": "~0.1.2", "json-stringify-safe": "~5.0.1", "mime-types": "~2.1.19", "performance-now": "^2.1.0", - "qs": "6.10.4", + "qs": "6.13.0", "safe-buffer": "^5.1.2", "tough-cookie": "^4.1.3", "tunnel-agent": "^0.6.0", "uuid": "^8.3.2" - }, - "dependencies": { - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - } } }, "@cypress/xvfb": { @@ -21776,83 +19393,28 @@ "optional": true }, "@esbuild/sunos-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", - "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", - "optional": true - }, - "@esbuild/win32-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", - "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", - "optional": true - }, - "@esbuild/win32-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", - "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", - "optional": true - }, - "@esbuild/win32-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", - "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", - "optional": true - }, - "@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^3.3.0" - } - }, - "@eslint-community/regexpp": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", - "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", - "dev": true - }, - "@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", - "dev": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - } - } + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "optional": true }, - "@eslint/js": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", - "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", - "dev": true + "@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "optional": true + }, + "@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "optional": true + }, + "@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "optional": true }, "@floating-ui/core": { "version": "1.6.0", @@ -22706,41 +20268,18 @@ "integrity": "sha512-U410sAr92xgxT1idlu9WWOVjndxLdgPUHEB8Schr27C9eh7/xUnITWpCMF93s+lGiG++D4JnbSnrb5A21AdSNg==", "requires": {} }, - "@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", - "dev": true, - "requires": { - "@humanwhocodes/object-schema": "^2.0.2", - "debug": "^4.3.1", - "minimatch": "^3.0.5" - } - }, - "@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true - }, - "@humanwhocodes/object-schema": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", - "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", - "dev": true - }, "@iconify-icon/react": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@iconify-icon/react/-/react-2.0.1.tgz", - "integrity": "sha512-1m6L2yNsSJ25k5baQRqNqh2J0w+91PwOn1WdBIR6ZTwxePbsZC8k3NNVc6m9BJObsIQdUlMA1NGj8el4tfbsVg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@iconify-icon/react/-/react-2.1.0.tgz", + "integrity": "sha512-OuEsW5Y474rg3WlseLFQ0uuJjnyk1DhLN1Ire5JGjF4sF8/rNxGJDLSItEogRcKuUbL+zzuoBsaTUVVInuixRA==", "requires": { - "iconify-icon": "^2.0.0" + "iconify-icon": "^2.1.0" } }, "@iconify-json/mdi": { - "version": "1.1.64", - "resolved": "https://registry.npmjs.org/@iconify-json/mdi/-/mdi-1.1.64.tgz", - "integrity": "sha512-zGeo5TjhNFAY6FmSDBLAzDO811t77r6v/mDi7CAL9w5eXqKez6bIjk8R9AL/RHIeq44ALP4Ozr4lMqFTkHr7ug==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@iconify-json/mdi/-/mdi-1.2.0.tgz", + "integrity": "sha512-E9/3l5Syg3wfuarorFodhn4s8YorxhH3U3U20LaNBNiqw1kFNIDWhF6HymuzAD35k7RH0OBasJ+ZUyFtVVV6eg==", "requires": { "@iconify/types": "*" } @@ -22844,9 +20383,9 @@ "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==" }, "@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==" + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" }, "@jridgewell/trace-mapping": { "version": "0.3.25", @@ -23083,12 +20622,12 @@ "optional": true }, "@playwright/test": { - "version": "1.47.0", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.47.0.tgz", - "integrity": "sha512-SgAdlSwYVpToI4e/IH19IHHWvoijAYH5hu2MWSXptRypLSnzj51PcGD+rsOXFayde4P9ZLi+loXVwArg6IUkCA==", + "version": "1.47.2", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.47.2.tgz", + "integrity": "sha512-jTXRsoSPONAs8Za9QEQdyjFn+0ZQFjCiIztAIF6bi1HqhBzG9Ma7g1WotyiGqFSBRZjIEqMdT8RUlbk1QVhzCQ==", "dev": true, "requires": { - "playwright": "1.47.0" + "playwright": "1.47.2" } }, "@popperjs/core": { @@ -23809,9 +21348,9 @@ } }, "@testing-library/react": { - "version": "14.2.2", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-14.2.2.tgz", - "integrity": "sha512-SOUuM2ysCvjUWBXTNfQ/ztmnKDmqaiPV3SvoIuyxMUca45rbSWWAT/qB8CUs/JQ/ux/8JFs9DNdFQ3f6jH3crA==", + "version": "14.3.1", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-14.3.1.tgz", + "integrity": "sha512-H99XjUhWQw0lTgyMN05W3xQG1Nh4lq574D8keFf1dDoNTJgp66VbJozRaczoF+wsiaPJNt/TcnfpLGufGxSrZQ==", "dev": true, "requires": { "@babel/runtime": "^7.12.5", @@ -23828,25 +21367,29 @@ "version": "1.0.9", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "devOptional": true + "optional": true, + "peer": true }, "@tsconfig/node12": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "devOptional": true + "optional": true, + "peer": true }, "@tsconfig/node14": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "devOptional": true + "optional": true, + "peer": true }, "@tsconfig/node16": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "devOptional": true + "optional": true, + "peer": true }, "@types/aria-query": { "version": "5.0.4", @@ -24000,12 +21543,6 @@ "integrity": "sha512-b7bq23s4fgBB76n34m2b3RBf6M369B0Z9uRR8aHTMd8kZISRkmDEpPD8hhpYvDFzr3bJCPES96cm3Q6qRNDbQw==", "dev": true }, - "@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true - }, "@types/loadable__component": { "version": "5.13.9", "resolved": "https://registry.npmjs.org/@types/loadable__component/-/loadable__component-5.13.9.tgz", @@ -24160,128 +21697,6 @@ "@types/node": "*" } }, - "@typescript-eslint/eslint-plugin": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.16.0.tgz", - "integrity": "sha512-py1miT6iQpJcs1BiJjm54AMzeuMPBSPuKPlnT8HlfudbcS5rYeX5jajpLf3mrdRh9dA/Ec2FVUY0ifeVNDIhZw==", - "dev": true, - "requires": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.16.0", - "@typescript-eslint/type-utils": "7.16.0", - "@typescript-eslint/utils": "7.16.0", - "@typescript-eslint/visitor-keys": "7.16.0", - "graphemer": "^1.4.0", - "ignore": "^5.3.1", - "natural-compare": "^1.4.0", - "ts-api-utils": "^1.3.0" - } - }, - "@typescript-eslint/parser": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.16.0.tgz", - "integrity": "sha512-ar9E+k7CU8rWi2e5ErzQiC93KKEFAXA2Kky0scAlPcxYblLt8+XZuHUZwlyfXILyQa95P6lQg+eZgh/dDs3+Vw==", - "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "7.16.0", - "@typescript-eslint/types": "7.16.0", - "@typescript-eslint/typescript-estree": "7.16.0", - "@typescript-eslint/visitor-keys": "7.16.0", - "debug": "^4.3.4" - } - }, - "@typescript-eslint/scope-manager": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.16.0.tgz", - "integrity": "sha512-8gVv3kW6n01Q6TrI1cmTZ9YMFi3ucDT7i7aI5lEikk2ebk1AEjrwX8MDTdaX5D7fPXMBLvnsaa0IFTAu+jcfOw==", - "dev": true, - "requires": { - "@typescript-eslint/types": "7.16.0", - "@typescript-eslint/visitor-keys": "7.16.0" - } - }, - "@typescript-eslint/type-utils": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.16.0.tgz", - "integrity": "sha512-j0fuUswUjDHfqV/UdW6mLtOQQseORqfdmoBNDFOqs9rvNVR2e+cmu6zJu/Ku4SDuqiJko6YnhwcL8x45r8Oqxg==", - "dev": true, - "requires": { - "@typescript-eslint/typescript-estree": "7.16.0", - "@typescript-eslint/utils": "7.16.0", - "debug": "^4.3.4", - "ts-api-utils": "^1.3.0" - } - }, - "@typescript-eslint/types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.16.0.tgz", - "integrity": "sha512-fecuH15Y+TzlUutvUl9Cc2XJxqdLr7+93SQIbcZfd4XRGGKoxyljK27b+kxKamjRkU7FYC6RrbSCg0ALcZn/xw==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.16.0.tgz", - "integrity": "sha512-a5NTvk51ZndFuOLCh5OaJBELYc2O3Zqxfl3Js78VFE1zE46J2AaVuW+rEbVkQznjkmlzWsUI15BG5tQMixzZLw==", - "dev": true, - "requires": { - "@typescript-eslint/types": "7.16.0", - "@typescript-eslint/visitor-keys": "7.16.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" - }, - "dependencies": { - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - }, - "minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - } - }, - "semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", - "dev": true - } - } - }, - "@typescript-eslint/utils": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.16.0.tgz", - "integrity": "sha512-PqP4kP3hb4r7Jav+NiRCntlVzhxBNWq6ZQ+zQwII1y/G/1gdIPeYDCKr2+dH6049yJQsWZiHU6RlwvIFBXXGNA==", - "dev": true, - "requires": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.16.0", - "@typescript-eslint/types": "7.16.0", - "@typescript-eslint/typescript-estree": "7.16.0" - } - }, - "@typescript-eslint/visitor-keys": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.16.0.tgz", - "integrity": "sha512-rMo01uPy9C7XxG7AFsxa8zLnWXTF8N3PYclekWSrurvhwiw1eW88mrKiAYe6s53AUY57nTRz8dJsuuXdkAhzCg==", - "dev": true, - "requires": { - "@typescript-eslint/types": "7.16.0", - "eslint-visitor-keys": "^3.4.3" - } - }, "@uiw/color-convert": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/@uiw/color-convert/-/color-convert-2.1.1.tgz", @@ -24576,9 +21991,9 @@ } }, "yocto-queue": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", - "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.1.1.tgz", + "integrity": "sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==", "dev": true } } @@ -24612,9 +22027,9 @@ } }, "react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true } } @@ -24667,9 +22082,9 @@ } }, "react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true } } @@ -24744,13 +22159,6 @@ "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "devOptional": true }, - "acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "requires": {} - }, "acorn-walk": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", @@ -24776,18 +22184,6 @@ "indent-string": "^4.0.0" } }, - "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" - } - }, "ansi-colors": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", @@ -24873,119 +22269,12 @@ "is-array-buffer": "^3.0.4" } }, - "array-includes": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", - "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.4", - "is-string": "^1.0.7" - } - }, "array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true }, - "array.prototype.findlast": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", - "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-shim-unscopables": "^1.0.2" - } - }, - "array.prototype.findlastindex": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz", - "integrity": "sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-shim-unscopables": "^1.0.2" - } - }, - "array.prototype.flat": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", - "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - } - }, - "array.prototype.flatmap": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", - "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - } - }, - "array.prototype.toreversed": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/array.prototype.toreversed/-/array.prototype.toreversed-1.1.2.tgz", - "integrity": "sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - } - }, - "array.prototype.tosorted": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.3.tgz", - "integrity": "sha512-/DdH4TiTmOKzyQbp/eadcCVexiCb36xJg7HshYOYJnNZFDj33GEv0P7GxsynpShhq4OLYJzbGcBDkLsDt7MnNg==", - "dev": true, - "requires": { - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.1.0", - "es-shim-unscopables": "^1.0.2" - } - }, - "arraybuffer.prototype.slice": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", - "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", - "dev": true, - "requires": { - "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "es-abstract": "^1.22.3", - "es-errors": "^1.2.1", - "get-intrinsic": "^1.2.3", - "is-array-buffer": "^3.0.4", - "is-shared-array-buffer": "^1.0.2" - } - }, "asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", @@ -25083,9 +22372,9 @@ "dev": true }, "aws4": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", - "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz", + "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==", "dev": true }, "babel-plugin-polyfill-corejs2": { @@ -25273,47 +22562,6 @@ "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", "dev": true }, - "builtin-modules": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz", - "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==", - "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" - }, - "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" - } - }, - "semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dev": true, - "requires": { - "lru-cache": "^6.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 - } - } - }, "busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -25374,9 +22622,9 @@ "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==" }, "caniuse-lite": { - "version": "1.0.30001605", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001605.tgz", - "integrity": "sha512-nXwGlFWo34uliI9z3n6Qc0wZaf7zaZWA1CPZ169La5mV3I/gem7bst0vr5XQH5TJXZIMfDeZyOrZnSlVzKxxHQ==" + "version": "1.0.30001664", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001664.tgz", + "integrity": "sha512-AmE7k4dXiNKQipgn7a2xg558IRqPN3jMQY/rOsbxDhrd0tyChwbITBfiwtnqz8bi2M5mIWbxAYBvk7W7QBUS2g==" }, "capital-case": { "version": "1.0.4", @@ -25401,9 +22649,9 @@ "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==" }, "chai": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.4.1.tgz", - "integrity": "sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", + "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", "dev": true, "requires": { "assertion-error": "^1.1.0", @@ -25412,7 +22660,7 @@ "get-func-name": "^2.0.2", "loupe": "^2.3.6", "pathval": "^1.1.1", - "type-detect": "^4.0.8" + "type-detect": "^4.1.0" } }, "chalk": { @@ -25651,6 +22899,15 @@ "integrity": "sha512-1prg2gv44sYfpHscP26uLT/ePrh0mlmVwMSoSd3zYKQ92Ab3jPRLzyCnpyOCQLJbK+YdNs4HvMRqMNYdy4pMhA==", "requires": {} }, + "cmdk": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cmdk/-/cmdk-1.0.0.tgz", + "integrity": "sha512-gDzVf0a09TvoJ5jnuPvygTB77+XdOSwEmJ88L6XPFPlv7T3RxbP9jgenfylrAMD0+Le1aO0nVjQUzl2g+vjz5Q==", + "requires": { + "@radix-ui/react-dialog": "1.0.5", + "@radix-ui/react-primitive": "1.0.3" + } + }, "codemirror": { "version": "5.65.16", "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.16.tgz", @@ -25717,6 +22974,12 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "confbox": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.7.tgz", + "integrity": "sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==", + "dev": true + }, "constant-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/constant-case/-/constant-case-3.0.4.tgz", @@ -25770,7 +23033,8 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "devOptional": true + "optional": true, + "peer": true }, "crelt": { "version": "1.0.6", @@ -25879,12 +23143,12 @@ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, "cypress": { - "version": "13.7.3", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.7.3.tgz", - "integrity": "sha512-uoecY6FTCAuIEqLUYkTrxamDBjMHTYak/1O7jtgwboHiTnS1NaMOoR08KcTrbRZFCBvYOiS4tEkQRmsV+xcrag==", + "version": "13.15.0", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.15.0.tgz", + "integrity": "sha512-53aO7PwOfi604qzOkCSzNlWquCynLlKE/rmmpSPcziRH6LNfaDUAklQT6WJIsD8ywxlIy+uVZsnTMCCQVd2kTw==", "dev": true, "requires": { - "@cypress/request": "^3.0.0", + "@cypress/request": "^3.0.4", "@cypress/xvfb": "^1.2.4", "@types/sinonjs__fake-timers": "8.1.1", "@types/sizzle": "^2.3.2", @@ -25923,7 +23187,7 @@ "request-progress": "^3.0.0", "semver": "^7.5.3", "supports-color": "^8.1.1", - "tmp": "~0.2.1", + "tmp": "~0.2.3", "untildify": "^4.0.0", "yauzl": "^2.10.0" }, @@ -26092,39 +23356,6 @@ "whatwg-url": "^14.0.0" } }, - "data-view-buffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", - "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", - "dev": true, - "requires": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - } - }, - "data-view-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", - "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - } - }, - "data-view-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", - "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", - "dev": true, - "requires": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - } - }, "dataloader": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/dataloader/-/dataloader-2.2.2.tgz", @@ -26187,9 +23418,9 @@ "integrity": "sha512-+8VxcR21HhTy8nOt6jf20w0c9CADrw1O8d+VZ/YzzCt4bJ3uBjw+D1q2osAB8RnpwwaeYBxy0HyKQxD5JBMuuQ==" }, "deep-eql": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", - "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", + "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", "dev": true, "requires": { "type-detect": "^4.0.0" @@ -26219,13 +23450,7 @@ "which-boxed-primitive": "^1.0.2", "which-collection": "^1.0.1", "which-typed-array": "^1.1.13" - } - }, - "deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true + } }, "deepmerge": { "version": "4.3.1", @@ -26281,816 +23506,267 @@ "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==" }, "detect-indent": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", - "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", - "dev": true - }, - "detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", - "dev": true, - "optional": true, - "peer": true - }, - "detect-node-es": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", - "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==" - }, - "devlop": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", - "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", - "requires": { - "dequal": "^2.0.0" - } - }, - "didyoumean": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" - }, - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "devOptional": true - }, - "diff-match-patch": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz", - "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==" - }, - "diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "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==" - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "dom-accessibility-api": { - "version": "0.5.16", - "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", - "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", - "dev": true - }, - "dom-helpers": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", - "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", - "requires": { - "@babel/runtime": "^7.8.7", - "csstype": "^3.0.2" - } - }, - "dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "requires": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - } - }, - "domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==" - }, - "domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "requires": { - "domelementtype": "^2.3.0" - } - }, - "domutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", - "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", - "requires": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - } - }, - "dot-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", - "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", - "requires": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "dotenv": { - "version": "16.4.5", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", - "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", - "dev": true - }, - "dset": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.4.tgz", - "integrity": "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==", - "dev": true - }, - "eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" - }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", - "dev": true, - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "electron-to-chromium": { - "version": "1.4.724", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.724.tgz", - "integrity": "sha512-RTRvkmRkGhNBPPpdrgtDKvmOEYTrPlXDfc0J/Nfq5s29tEahAwhiX4mmhNzj6febWMleulxVYPh7QwCSL/EldA==" - }, - "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==" - }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, - "enquirer": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", - "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", - "dev": true, - "requires": { - "ansi-colors": "^4.1.1", - "strip-ansi": "^6.0.1" - } - }, - "entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==" - }, - "environment": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", - "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", - "dev": true - }, - "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==", - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "es-abstract": { - "version": "1.23.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", - "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", - "dev": true, - "requires": { - "array-buffer-byte-length": "^1.0.1", - "arraybuffer.prototype.slice": "^1.0.3", - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "data-view-buffer": "^1.0.1", - "data-view-byte-length": "^1.0.1", - "data-view-byte-offset": "^1.0.0", - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-set-tostringtag": "^2.0.3", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.4", - "get-symbol-description": "^1.0.2", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", - "has-symbols": "^1.0.3", - "hasown": "^2.0.2", - "internal-slot": "^1.0.7", - "is-array-buffer": "^3.0.4", - "is-callable": "^1.2.7", - "is-data-view": "^1.0.1", - "is-negative-zero": "^2.0.3", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.3", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.13", - "is-weakref": "^1.0.2", - "object-inspect": "^1.13.1", - "object-keys": "^1.1.1", - "object.assign": "^4.1.5", - "regexp.prototype.flags": "^1.5.2", - "safe-array-concat": "^1.1.2", - "safe-regex-test": "^1.0.3", - "string.prototype.trim": "^1.2.9", - "string.prototype.trimend": "^1.0.8", - "string.prototype.trimstart": "^1.0.8", - "typed-array-buffer": "^1.0.2", - "typed-array-byte-length": "^1.0.1", - "typed-array-byte-offset": "^1.0.2", - "typed-array-length": "^1.0.6", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.15" - } - }, - "es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "dev": true, - "requires": { - "get-intrinsic": "^1.2.4" - } - }, - "es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true - }, - "es-get-iterator": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", - "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "has-symbols": "^1.0.3", - "is-arguments": "^1.1.1", - "is-map": "^2.0.2", - "is-set": "^2.0.2", - "is-string": "^1.0.7", - "isarray": "^2.0.5", - "stop-iteration-iterator": "^1.0.0" - } - }, - "es-iterator-helpers": { - "version": "1.0.18", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.18.tgz", - "integrity": "sha512-scxAJaewsahbqTYrGKJihhViaM6DDZDDoucfvzNbK0pOren1g/daDQ3IAhzn+1G14rBG7w+i5N+qul60++zlKA==", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.0", - "es-errors": "^1.3.0", - "es-set-tostringtag": "^2.0.3", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "globalthis": "^1.0.3", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.0.3", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.7", - "iterator.prototype": "^1.1.2", - "safe-array-concat": "^1.1.2" - } + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", + "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", + "dev": true }, - "es-object-atoms": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", - "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", "dev": true, - "requires": { - "es-errors": "^1.3.0" - } + "optional": true, + "peer": true }, - "es-set-tostringtag": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", - "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", - "dev": true, - "requires": { - "get-intrinsic": "^1.2.4", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.1" - } + "detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==" }, - "es-shim-unscopables": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", - "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", - "dev": true, + "devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", "requires": { - "hasown": "^2.0.0" + "dequal": "^2.0.0" } }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } + "didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" }, - "esbuild": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", - "requires": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" - } + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "optional": true, + "peer": true }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" + "diff-match-patch": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz", + "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==" }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", "dev": true }, - "eslint": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", - "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "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": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.0", - "@humanwhocodes/config-array": "^0.11.14", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.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" - } - }, - "globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - } + "path-type": "^4.0.0" } }, - "eslint-compat-utils": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.5.0.tgz", - "integrity": "sha512-dc6Y8tzEcSYZMHa+CMPLi/hyo1FzNeonbhJL7Ol0ccuKQkwopJcJBA9YL/xmMTLU1eKigXo9vj9nALElWYSowg==", - "dev": true, - "requires": { - "semver": "^7.5.4" - }, - "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" - } - }, - "semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dev": true, - "requires": { - "lru-cache": "^6.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 - } - } + "dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" }, - "eslint-config-prettier": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", - "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", - "dev": true, - "requires": {} + "dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "dev": true }, - "eslint-import-resolver-node": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", - "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", - "dev": true, + "dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", "requires": { - "debug": "^3.2.7", - "is-core-module": "^2.13.0", - "resolve": "^1.22.4" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - } + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" } }, - "eslint-module-utils": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.1.tgz", - "integrity": "sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==", - "dev": true, + "dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", "requires": { - "debug": "^3.2.7" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - } + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" } }, - "eslint-plugin-cypress": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-cypress/-/eslint-plugin-cypress-2.15.1.tgz", - "integrity": "sha512-eLHLWP5Q+I4j2AWepYq0PgFEei9/s5LvjuSqWrxurkg1YZ8ltxdvMNmdSf0drnsNo57CTgYY/NIHHLRSWejR7w==", - "dev": true, + "domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==" + }, + "domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", "requires": { - "globals": "^13.20.0" - }, - "dependencies": { - "globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - } + "domelementtype": "^2.3.0" } }, - "eslint-plugin-es-x": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-es-x/-/eslint-plugin-es-x-7.6.0.tgz", - "integrity": "sha512-I0AmeNgevgaTR7y2lrVCJmGYF0rjoznpDvqV/kIkZSZbZ8Rw3eu4cGlvBBULScfkSOCzqKbff5LR4CNrV7mZHA==", - "dev": true, + "domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", "requires": { - "@eslint-community/eslint-utils": "^4.1.2", - "@eslint-community/regexpp": "^4.6.0", - "eslint-compat-utils": "^0.5.0" + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" } }, - "eslint-plugin-import": { - "version": "2.29.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", - "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", - "dev": true, + "dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", "requires": { - "array-includes": "^3.1.7", - "array.prototype.findlastindex": "^1.2.3", - "array.prototype.flat": "^1.3.2", - "array.prototype.flatmap": "^1.3.2", - "debug": "^3.2.7", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.9", - "eslint-module-utils": "^2.8.0", - "hasown": "^2.0.0", - "is-core-module": "^2.13.1", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.fromentries": "^2.0.7", - "object.groupby": "^1.0.1", - "object.values": "^1.1.7", - "semver": "^6.3.1", - "tsconfig-paths": "^3.15.0" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - } + "no-case": "^3.0.4", + "tslib": "^2.0.3" } }, - "eslint-plugin-n": { - "version": "16.6.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-16.6.2.tgz", - "integrity": "sha512-6TyDmZ1HXoFQXnhCTUjVFULReoBPOAjpuiKELMkeP40yffI/1ZRO+d9ug/VC6fqISo2WkuIBk3cvuRPALaWlOQ==", + "dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "dev": true + }, + "dset": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.4.tgz", + "integrity": "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==", + "dev": true + }, + "eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", "dev": true, "requires": { - "@eslint-community/eslint-utils": "^4.4.0", - "builtins": "^5.0.1", - "eslint-plugin-es-x": "^7.5.0", - "get-tsconfig": "^4.7.0", - "globals": "^13.24.0", - "ignore": "^5.2.4", - "is-builtin-module": "^3.2.1", - "is-core-module": "^2.12.1", - "minimatch": "^3.1.2", - "resolve": "^1.22.2", - "semver": "^7.5.3" - }, - "dependencies": { - "globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "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" - } - }, - "semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" } }, - "eslint-plugin-promise": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.1.1.tgz", - "integrity": "sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig==", - "dev": true, - "requires": {} + "electron-to-chromium": { + "version": "1.4.724", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.724.tgz", + "integrity": "sha512-RTRvkmRkGhNBPPpdrgtDKvmOEYTrPlXDfc0J/Nfq5s29tEahAwhiX4mmhNzj6febWMleulxVYPh7QwCSL/EldA==" }, - "eslint-plugin-react": { - "version": "7.34.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.1.tgz", - "integrity": "sha512-N97CxlouPT1AHt8Jn0mhhN2RrADlUAsk1/atcT2KyA/l9Q/E6ll7OIGwNumFmWfZ9skV3XXccYS19h80rHtgkw==", - "dev": true, - "requires": { - "array-includes": "^3.1.7", - "array.prototype.findlast": "^1.2.4", - "array.prototype.flatmap": "^1.3.2", - "array.prototype.toreversed": "^1.1.2", - "array.prototype.tosorted": "^1.1.3", - "doctrine": "^2.1.0", - "es-iterator-helpers": "^1.0.17", - "estraverse": "^5.3.0", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.1.2", - "object.entries": "^1.1.7", - "object.fromentries": "^2.0.7", - "object.hasown": "^1.1.3", - "object.values": "^1.1.7", - "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.5", - "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.10" - }, - "dependencies": { - "doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "resolve": { - "version": "2.0.0-next.5", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", - "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", - "dev": true, - "requires": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - } - } + "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==" }, - "eslint-plugin-unused-imports": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-3.2.0.tgz", - "integrity": "sha512-6uXyn6xdINEpxE1MtDjxQsyXB37lfyO2yKGVVgtD7WEWQGORSOZjgrD6hBhvGv4/SO+TOlS+UnC6JppRqbuwGQ==", + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", "dev": true, "requires": { - "eslint-rule-composer": "^0.3.0" + "once": "^1.4.0" } }, - "eslint-rule-composer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz", - "integrity": "sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg==", - "dev": true - }, - "eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "enquirer": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", + "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", "dev": true, "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" + "ansi-colors": "^4.1.1", + "strip-ansi": "^6.0.1" } }, - "eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==" + }, + "environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", "dev": true }, - "espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, + "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==", "requires": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" + "is-arrayish": "^0.2.1" } }, - "esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", "dev": true, "requires": { - "estraverse": "^5.1.0" + "get-intrinsic": "^1.2.4" } }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true + }, + "es-get-iterator": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", + "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", "dev": true, "requires": { - "estraverse": "^5.2.0" + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "is-arguments": "^1.1.1", + "is-map": "^2.0.2", + "is-set": "^2.0.2", + "is-string": "^1.0.7", + "isarray": "^2.0.5", + "stop-iteration-iterator": "^1.0.0" + } + }, + "esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "requires": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" } }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" }, "estree-util-is-identifier-name": { "version": "3.0.0", @@ -27225,18 +23901,6 @@ "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 - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, "fast-querystring": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/fast-querystring/-/fast-querystring-1.1.2.tgz", @@ -27330,15 +23994,6 @@ } } }, - "file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "requires": { - "flat-cache": "^3.0.4" - } - }, "fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -27352,33 +24007,6 @@ "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-5.1.0.tgz", "integrity": "sha512-qWeTREPoT7I0bifpPUXtxkZJ1XJzxWtfoWWkdVGqa+eCr3SHW/Ocp89o8vLvbUuQnadybJpjOKu4V+RwO6sGng==" }, - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", - "dev": true, - "requires": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" - } - }, - "flatted": { - "version": "3.2.9", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", - "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", - "dev": true - }, "for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -27461,18 +24089,6 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" }, - "function.prototype.name": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", - "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" - } - }, "functions-have-names": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", @@ -27529,26 +24145,6 @@ "pump": "^3.0.0" } }, - "get-symbol-description": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", - "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", - "dev": true, - "requires": { - "call-bind": "^1.0.5", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4" - } - }, - "get-tsconfig": { - "version": "4.7.3", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.3.tgz", - "integrity": "sha512-ZvkrzoUA0PQZM6fy6+/Hce561s+faD1rsNwhnO5FelNjyy7EMGJ3Rz1AQ8GYDWjhRs/7dBLOEJvhK8MiEJOAFg==", - "dev": true, - "requires": { - "resolve-pkg-maps": "^1.0.0" - } - }, "getos": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/getos/-/getos-3.2.1.tgz", @@ -27608,15 +24204,6 @@ "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" }, - "globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", - "dev": true, - "requires": { - "define-properties": "^1.1.3" - } - }, "globby": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", @@ -27651,12 +24238,6 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, - "graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true - }, "graphiql": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/graphiql/-/graphiql-3.1.2.tgz", @@ -27760,23 +24341,6 @@ "wordwrap": "^1.0.0" } }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "dev": true - } - } - }, "has-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", @@ -27911,14 +24475,14 @@ } }, "http-signature": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz", - "integrity": "sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.4.0.tgz", + "integrity": "sha512-G5akfn7eKbpDN+8nPS/cb57YeA1jLTVxjpCj7tmm3QKPdyDy7T+qSC40e9ptydSWvkwjSXw1VbkpyEm39ukeAg==", "dev": true, "requires": { "assert-plus": "^1.0.0", "jsprim": "^2.0.2", - "sshpk": "^1.14.1" + "sshpk": "^1.18.0" } }, "https-proxy-agent": { @@ -27944,9 +24508,9 @@ "dev": true }, "iconify-icon": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/iconify-icon/-/iconify-icon-2.0.0.tgz", - "integrity": "sha512-38ArOkxmyD9oDbJBkxaFpE6eZ0K3F9Sk+3x4mWGfjMJaxi3EKrix9Du4iWhgBFT3imKC4FJJE34ur2Rc7Xm+Uw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/iconify-icon/-/iconify-icon-2.1.0.tgz", + "integrity": "sha512-lto4XU3bwTQnb+D/CsJ4dWAo0aDe+uPMxEtxyOodw9l7R9QnJUUab3GCehlw2M8mDHdeUu/ufx8PvRQiJphhXg==", "requires": { "@iconify/types": "^2.0.0" } @@ -28000,12 +24564,6 @@ "integrity": "sha512-P9J71vT5nLlDeV8FHs5nNxaLbrpfAV5cF5srvbZfpwpcJoM/xZR3hiv+q+SAnuSmuGbXMWud063iIMx/V/EWZQ==", "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", @@ -28141,15 +24699,6 @@ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" }, - "is-async-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", - "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, "is-bigint": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", @@ -28177,15 +24726,6 @@ "has-tostringtag": "^1.0.0" } }, - "is-builtin-module": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz", - "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==", - "dev": true, - "requires": { - "builtin-modules": "^3.3.0" - } - }, "is-callable": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", @@ -28209,15 +24749,6 @@ "hasown": "^2.0.0" } }, - "is-data-view": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", - "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", - "dev": true, - "requires": { - "is-typed-array": "^1.1.13" - } - }, "is-date-object": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", @@ -28234,32 +24765,14 @@ }, "is-extglob": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" - }, - "is-finalizationregistry": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz", - "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2" - } + "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==" }, - "is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, "is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -28304,12 +24817,6 @@ "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", "dev": true }, - "is-negative-zero": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", - "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", - "dev": true - }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -28412,15 +24919,6 @@ "has-symbols": "^1.0.2" } }, - "is-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", - "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", - "dev": true, - "requires": { - "which-typed-array": "^1.1.14" - } - }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -28457,15 +24955,6 @@ "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", "dev": true }, - "is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2" - } - }, "is-weakset": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", @@ -28554,19 +25043,6 @@ "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.3.0.tgz", "integrity": "sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg==" }, - "iterator.prototype": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", - "integrity": "sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==", - "dev": true, - "requires": { - "define-properties": "^1.2.1", - "get-intrinsic": "^1.2.1", - "has-symbols": "^1.0.3", - "reflect.getprototypeof": "^1.0.4", - "set-function-name": "^2.0.1" - } - }, "jackspeak": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", @@ -28588,9 +25064,9 @@ "dev": true }, "jotai": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/jotai/-/jotai-2.7.2.tgz", - "integrity": "sha512-6Ft5kpNu8p93Ssf1Faoza3hYQZRIYp7rioK8MwTTFnbQKwUyZElwquPwl1h6U0uo9hC0jr+ghO3gcSjc6P35/Q==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/jotai/-/jotai-2.10.0.tgz", + "integrity": "sha512-8W4u0aRlOIwGlLQ0sqfl/c6+eExl5D8lZgAUolirZLktyaj4WnxO/8a0HEPmtriQAB6X5LMhXzZVmw02X0P0qQ==", "requires": {} }, "js-levenshtein": { @@ -28652,12 +25128,6 @@ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" }, - "json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true - }, "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", @@ -28669,12 +25139,6 @@ "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", "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 - }, "json-stable-stringify": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.1.1.tgz", @@ -28687,12 +25151,6 @@ "object-keys": "^1.1.1" } }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true - }, "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", @@ -28719,12 +25177,6 @@ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==" }, - "jsonc-parser": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", - "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==", - "dev": true - }, "jsonfile": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", @@ -28753,43 +25205,12 @@ "verror": "1.10.0" } }, - "jsx-ast-utils": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", - "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", - "dev": true, - "requires": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "object.assign": "^4.1.4", - "object.values": "^1.1.6" - } - }, - "keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "requires": { - "json-buffer": "3.0.1" - } - }, "lazy-ass": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz", "integrity": "sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==", "dev": true }, - "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, "lilconfig": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", @@ -29124,15 +25545,6 @@ "pkg-types": "^1.0.3" } }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "requires": { - "p-locate": "^5.0.0" - } - }, "lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", @@ -29149,12 +25561,6 @@ "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", "dev": true }, - "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.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", @@ -29202,70 +25608,6 @@ } } }, - "loglevel": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.1.tgz", - "integrity": "sha512-hP3I3kCrDIMuRwAwHltphhDM1r8i55H33GgqjXbrisuJhF4kRhW1dNuxsRklp4bXl8DSdLaNLuiL4A/LWRfxvg==", - "dev": true - }, - "loglevel-colored-level-prefix": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/loglevel-colored-level-prefix/-/loglevel-colored-level-prefix-1.0.0.tgz", - "integrity": "sha512-u45Wcxxc+SdAlh4yeF/uKlC1SPUPCy0gullSNKXod5I4bmifzk+Q4lSLExNEVn19tGaJipbZ4V4jbFn79/6mVA==", - "dev": true, - "requires": { - "chalk": "^1.1.3", - "loglevel": "^1.4.1" - }, - "dependencies": { - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "dev": true - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", - "dev": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", - "dev": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "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 - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", - "dev": true - } - } - }, "longest-streak": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", @@ -29320,12 +25662,12 @@ "dev": true }, "magic-string": { - "version": "0.30.8", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", - "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==", + "version": "0.30.11", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz", + "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==", "dev": true, "requires": { - "@jridgewell/sourcemap-codec": "^1.4.15" + "@jridgewell/sourcemap-codec": "^1.5.0" } }, "magicast": { @@ -29378,7 +25720,8 @@ "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "devOptional": true + "optional": true, + "peer": true }, "map-cache": { "version": "0.2.2", @@ -29997,23 +26340,17 @@ "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==" }, "mlly": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.6.1.tgz", - "integrity": "sha512-vLgaHvaeunuOXHSmEbZ9izxPx3USsk8KCQ8iC+aTlp5sKRSoZvwhHh5L9VbKSaVC6sJDqbyohIS76E2VmHIPAA==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.1.tgz", + "integrity": "sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==", "dev": true, "requires": { "acorn": "^8.11.3", "pathe": "^1.1.2", - "pkg-types": "^1.0.3", - "ufo": "^1.3.2" + "pkg-types": "^1.1.1", + "ufo": "^1.5.3" } }, - "mri": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", - "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", - "dev": true - }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -30040,12 +26377,6 @@ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==" }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, "neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", @@ -30163,13 +26494,13 @@ "dev": true }, "object-is": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", - "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" } }, "object-keys": { @@ -30190,62 +26521,6 @@ "object-keys": "^1.1.1" } }, - "object.entries": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz", - "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - } - }, - "object.fromentries": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", - "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" - } - }, - "object.groupby": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", - "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2" - } - }, - "object.hasown": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.4.tgz", - "integrity": "sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==", - "dev": true, - "requires": { - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" - } - }, - "object.values": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", - "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - } - }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -30329,20 +26604,6 @@ } } }, - "optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", - "dev": true, - "requires": { - "@aashutoshrathi/word-wrap": "^1.2.3", - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" - } - }, "ora": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", @@ -30381,15 +26642,6 @@ "yocto-queue": "^0.1.0" } }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "requires": { - "p-limit": "^3.0.2" - } - }, "p-map": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", @@ -30605,24 +26857,24 @@ "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==" }, "pkg-types": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.0.3.tgz", - "integrity": "sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.2.0.tgz", + "integrity": "sha512-+ifYuSSqOQ8CqP4MbZA5hDpb97n3E8SVWdJe+Wms9kj745lmd3b7EZJiqvmLwAlmRfjrI7Hi5z3kdBJ93lFNPA==", "dev": true, "requires": { - "jsonc-parser": "^3.2.0", - "mlly": "^1.2.0", - "pathe": "^1.1.0" + "confbox": "^0.1.7", + "mlly": "^1.7.1", + "pathe": "^1.1.2" } }, "playwright": { - "version": "1.47.0", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.47.0.tgz", - "integrity": "sha512-jOWiRq2pdNAX/mwLiwFYnPHpEZ4rM+fRSQpRHwEwZlP2PUANvL3+aJOF/bvISMhFD30rqMxUB4RJx9aQbfh4Ww==", + "version": "1.47.2", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.47.2.tgz", + "integrity": "sha512-nx1cLMmQWqmA3UsnjaaokyoUpdVaaDhJhMoxX2qj3McpjnsqFHs516QAKYhqHAgOP+oCFTEOCOAaD1RgD/RQfA==", "dev": true, "requires": { "fsevents": "2.3.2", - "playwright-core": "1.47.0" + "playwright-core": "1.47.2" }, "dependencies": { "fsevents": { @@ -30635,9 +26887,9 @@ } }, "playwright-core": { - "version": "1.47.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.47.0.tgz", - "integrity": "sha512-1DyHT8OqkcfCkYUD9zzUTfg7EfTd+6a8MkD/NWOvjo0u/SCNd5YmY/lJwFvUZOxJbWNds+ei7ic2+R/cRz/PDg==", + "version": "1.47.2", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.47.2.tgz", + "integrity": "sha512-3JvMfF+9LJfe16l7AbSmU555PaTl2tPyQsVInqm3id16pdDfvZ8TTZ/pyzmkbDrZTQefyzU7AIHlZqQnxpqHVQ==", "dev": true }, "pluralize": { @@ -30702,175 +26954,33 @@ "dependencies": { "lilconfig": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.0.0.tgz", - "integrity": "sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g==" - } - } - }, - "postcss-nested": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", - "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", - "requires": { - "postcss-selector-parser": "^6.0.11" - } - }, - "postcss-selector-parser": { - "version": "6.0.15", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz", - "integrity": "sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==", - "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==" - }, - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "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-eslint": { - "version": "16.3.0", - "resolved": "https://registry.npmjs.org/prettier-eslint/-/prettier-eslint-16.3.0.tgz", - "integrity": "sha512-Lh102TIFCr11PJKUMQ2kwNmxGhTsv/KzUg9QYF2Gkw259g/kPgndZDWavk7/ycbRvj2oz4BPZ1gCU8bhfZH/Xg==", - "dev": true, - "requires": { - "@typescript-eslint/parser": "^6.7.5", - "common-tags": "^1.4.0", - "dlv": "^1.1.0", - "eslint": "^8.7.0", - "indent-string": "^4.0.0", - "lodash.merge": "^4.6.0", - "loglevel-colored-level-prefix": "^1.0.0", - "prettier": "^3.0.1", - "pretty-format": "^29.7.0", - "require-relative": "^0.8.7", - "typescript": "^5.2.2", - "vue-eslint-parser": "^9.1.0" - }, - "dependencies": { - "@typescript-eslint/parser": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", - "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", - "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", - "debug": "^4.3.4" - } - }, - "@typescript-eslint/scope-manager": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", - "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", - "dev": true, - "requires": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0" - } - }, - "@typescript-eslint/types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", - "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", - "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", - "dev": true, - "requires": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "9.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - } - }, - "@typescript-eslint/visitor-keys": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", - "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", - "dev": true, - "requires": { - "@typescript-eslint/types": "6.21.0", - "eslint-visitor-keys": "^3.4.1" - } - }, - "ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true - }, - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - }, - "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" - } - }, - "prettier": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.2.tgz", - "integrity": "sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==", - "dev": true - }, - "pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "requires": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - } - }, - "react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true - }, - "semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", - "dev": true + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.0.0.tgz", + "integrity": "sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g==" } } }, + "postcss-nested": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", + "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", + "requires": { + "postcss-selector-parser": "^6.0.11" + } + }, + "postcss-selector-parser": { + "version": "6.0.15", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz", + "integrity": "sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==", + "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==" + }, "pretty-bytes": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", @@ -30902,66 +27012,6 @@ } } }, - "pretty-quick": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/pretty-quick/-/pretty-quick-3.3.1.tgz", - "integrity": "sha512-3b36UXfYQ+IXXqex6mCca89jC8u0mYLqFAN5eTQKoXO6oCQYcIVYZEB/5AlBHI7JPYygReM2Vv6Vom/Gln7fBg==", - "dev": true, - "requires": { - "execa": "^4.1.0", - "find-up": "^4.1.0", - "ignore": "^5.3.0", - "mri": "^1.2.0", - "picocolors": "^1.0.0", - "picomatch": "^3.0.1", - "tslib": "^2.6.2" - }, - "dependencies": { - "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" - } - }, - "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" - } - }, - "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" - } - }, - "picomatch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-3.0.1.tgz", - "integrity": "sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag==", - "dev": true - } - } - }, "prismjs": { "version": "1.29.0", "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", @@ -31035,12 +27085,12 @@ "dev": true }, "qs": { - "version": "6.10.4", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.4.tgz", - "integrity": "sha512-OQiU+C+Ds5qiH91qh/mg0w+8nwQuLjM4F4M/PbmhDOoYehPh+Fb0bDjtR1sOvy7YKxvj28Y/M0PhP5uVX0kB+g==", + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", "dev": true, "requires": { - "side-channel": "^1.0.4" + "side-channel": "^1.0.6" } }, "query-string": { @@ -31379,21 +27429,6 @@ "decimal.js-light": "^2.4.1" } }, - "reflect.getprototypeof": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz", - "integrity": "sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.1", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "globalthis": "^1.0.3", - "which-builtin-type": "^1.1.3" - } - }, "regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -31583,12 +27618,6 @@ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", "dev": true }, - "require-relative": { - "version": "0.8.7", - "resolved": "https://registry.npmjs.org/require-relative/-/require-relative-0.8.7.tgz", - "integrity": "sha512-AKGr4qvHiryxRb19m3PsLRGuKVAbJLUD7E6eOaHkfKhwc+vSgVOCY5xNvm9EkolBKTOf0GrQAZKLimOCz81Khg==", - "dev": true - }, "requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", @@ -31611,12 +27640,6 @@ "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true }, - "resolve-pkg-maps": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "dev": true - }, "response-iterator": { "version": "0.2.6", "resolved": "https://registry.npmjs.org/response-iterator/-/response-iterator-0.2.6.tgz", @@ -31643,15 +27666,6 @@ "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", "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" - } - }, "rollup": { "version": "4.22.4", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.22.4.tgz", @@ -31706,35 +27720,12 @@ "tslib": "^2.1.0" } }, - "safe-array-concat": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", - "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "get-intrinsic": "^1.2.4", - "has-symbols": "^1.0.3", - "isarray": "^2.0.5" - } - }, "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 }, - "safe-regex-test": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", - "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", - "dev": true, - "requires": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-regex": "^1.1.4" - } - }, "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -32035,60 +28026,6 @@ "strip-ansi": "^6.0.1" } }, - "string.prototype.matchall": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz", - "integrity": "sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.7", - "regexp.prototype.flags": "^1.5.2", - "set-function-name": "^2.0.2", - "side-channel": "^1.0.6" - } - }, - "string.prototype.trim": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", - "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.0", - "es-object-atoms": "^1.0.0" - } - }, - "string.prototype.trimend": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", - "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - } - }, - "string.prototype.trimstart": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", - "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - } - }, "stringify-entities": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.3.tgz", @@ -32114,24 +28051,12 @@ "ansi-regex": "^5.0.1" } }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true - }, "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 }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, "strip-literal": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.1.0.tgz", @@ -32382,12 +28307,6 @@ "minimatch": "^3.0.4" } }, - "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", @@ -32422,15 +28341,15 @@ "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==" }, "tinybench": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.6.0.tgz", - "integrity": "sha512-N8hW3PG/3aOoZAN5V/NSAEDz0ZixDSSt5b/a05iqtpgfLWMSVuCo7w0k2vVvEjdrIoeGqZzweX2WlyioNIHchA==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", "dev": true }, "tinypool": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.3.tgz", - "integrity": "sha512-Ud7uepAklqRH1bvwy22ynrliC7Dljz7Tm8M/0RBUW+YRa4YHhZ6e4PpgE+fu1zr/WqB1kbeuVrdfeuyIBpy4tw==", + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.4.tgz", + "integrity": "sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==", "dev": true }, "tinyspy": { @@ -32449,13 +28368,10 @@ } }, "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" - } + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "dev": true }, "to-fast-properties": { "version": "2.0.0", @@ -32528,13 +28444,6 @@ "resolved": "https://registry.npmjs.org/trough/-/trough-2.1.0.tgz", "integrity": "sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==" }, - "ts-api-utils": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", - "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", - "dev": true, - "requires": {} - }, "ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", @@ -32558,7 +28467,8 @@ "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "devOptional": true, + "optional": true, + "peer": true, "requires": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -32579,7 +28489,8 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "devOptional": true + "optional": true, + "peer": true } } }, @@ -32595,29 +28506,6 @@ "integrity": "sha512-CMjc5zMnyAjcS9sPLytrbFmj89st2g+JYtY/c02ug4Q+CZaAtCgbyviI0n1YvjZE/pzoc6FbNsINS13DOL1B9w==", "requires": {} }, - "tsconfig-paths": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", - "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", - "dev": true, - "requires": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - }, - "dependencies": { - "json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - } - } - }, "tslib": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", @@ -32638,19 +28526,10 @@ "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", "dev": true }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1" - } - }, "type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", "dev": true }, "type-fest": { @@ -32659,58 +28538,6 @@ "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true }, - "typed-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", - "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "is-typed-array": "^1.1.13" - } - }, - "typed-array-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", - "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" - } - }, - "typed-array-byte-offset": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", - "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", - "dev": true, - "requires": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13" - } - }, - "typed-array-length": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", - "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", - "dev": true, - "requires": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-proto": "^1.0.3", - "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0" - } - }, "types-ramda": { "version": "0.29.10", "resolved": "https://registry.npmjs.org/types-ramda/-/types-ramda-0.29.10.tgz", @@ -32726,17 +28553,6 @@ "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", "devOptional": true }, - "typescript-eslint": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-7.16.0.tgz", - "integrity": "sha512-kaVRivQjOzuoCXU6+hLnjo3/baxyzWVO5GrnExkFzETRYJKVHYkrJglOu2OCm8Hi9RPDWX1PTNNTpU5KRV0+RA==", - "dev": true, - "requires": { - "@typescript-eslint/eslint-plugin": "7.16.0", - "@typescript-eslint/parser": "7.16.0", - "@typescript-eslint/utils": "7.16.0" - } - }, "ua-parser-js": { "version": "1.0.37", "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.37.tgz", @@ -32749,9 +28565,9 @@ "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==" }, "ufo": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.3.tgz", - "integrity": "sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==", + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz", + "integrity": "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==", "dev": true }, "uglify-js": { @@ -32760,18 +28576,6 @@ "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", "optional": true }, - "unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - } - }, "unc-path-regex": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", @@ -33021,7 +28825,8 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "devOptional": true + "optional": true, + "peer": true }, "v8-to-istanbul": { "version": "9.2.0", @@ -33237,29 +29042,6 @@ "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==" }, - "vue-eslint-parser": { - "version": "9.4.3", - "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.4.3.tgz", - "integrity": "sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==", - "dev": true, - "requires": { - "debug": "^4.3.4", - "eslint-scope": "^7.1.1", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.3.1", - "esquery": "^1.4.0", - "lodash": "^4.17.21", - "semver": "^7.3.6" - }, - "dependencies": { - "semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", - "dev": true - } - } - }, "w3c-keyname": { "version": "2.2.8", "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", @@ -33297,11 +29079,6 @@ "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", "dev": true }, - "web-vitals": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-2.1.4.tgz", - "integrity": "sha512-sVWcwhU5mX6crfI5Vd2dC4qchyTqxV8URinzt25XqVh+bHEPGH4C3NPrNionCP7Obx59wrYEbNlw4Z8sjALzZg==" - }, "webcrypto-core": { "version": "1.7.9", "resolved": "https://registry.npmjs.org/webcrypto-core/-/webcrypto-core-1.7.9.tgz", @@ -33378,26 +29155,6 @@ "is-symbol": "^1.0.3" } }, - "which-builtin-type": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.3.tgz", - "integrity": "sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==", - "dev": true, - "requires": { - "function.prototype.name": "^1.1.5", - "has-tostringtag": "^1.0.0", - "is-async-function": "^2.0.0", - "is-date-object": "^1.0.5", - "is-finalizationregistry": "^1.0.2", - "is-generator-function": "^1.0.10", - "is-regex": "^1.1.4", - "is-weakref": "^1.0.2", - "isarray": "^2.0.5", - "which-boxed-primitive": "^1.0.2", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.9" - } - }, "which-collection": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", @@ -33430,9 +29187,9 @@ } }, "why-is-node-running": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.2.2.tgz", - "integrity": "sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", "dev": true, "requires": { "siginfo": "^2.0.0", @@ -33549,7 +29306,8 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "devOptional": true + "optional": true, + "peer": true }, "yocto-queue": { "version": "0.1.0", diff --git a/frontend/app/package.json b/frontend/app/package.json index 9ad1a9f3fc..35890d8acb 100644 --- a/frontend/app/package.json +++ b/frontend/app/package.json @@ -23,11 +23,8 @@ "cypress:run": "npm run cypress:run:component", "cypress:run:component": "cypress run --component", "cypress:run:spec": "ELECTRON_ENABLE_LOGGING=1 cypress run --spec", - "prettier": "prettier './**/*.{js,jsx,ts,tsx,css,md,json}' --config ./.prettierrc.json", - "prettier:fix": "prettier --write './**/*.{js,jsx,ts,tsx,css,md,json}' --config ./.prettierrc.json", - "eslint": "eslint .", - "eslint:fix": "eslint . --fix", - "format-code": "npm run prettier:fix && npm run eslint:fix" + "biome": "biome check .", + "biome:fix": "biome check --apply ." }, "dependencies": { "@apollo/client": "^3.9.10", @@ -40,8 +37,8 @@ "@headlessui/react": "^1.7.18", "@heroicons/react": "^2.1.3", "@hookform/error-message": "^2.0.1", - "@iconify-icon/react": "^2.0.1", - "@iconify-json/mdi": "^1.1.64", + "@iconify-icon/react": "^2.1.0", + "@iconify-json/mdi": "^1.2.0", "@popperjs/core": "^2.11.8", "@radix-ui/react-dropdown-menu": "^2.0.6", "@radix-ui/react-label": "^2.0.2", @@ -58,12 +55,13 @@ "clsx": "^2.1.0", "cm6-graphql": "^0.0.14", "cm6-theme-basic-light": "^0.2.0", + "cmdk": "^1.0.0", "cross-fetch": "^4.0.0", "date-fns": "^3.6.0", "graphiql": "^3.1.2", "graphql": "^16.8.1", "handlebars": "^4.7.8", - "jotai": "^2.7.2", + "jotai": "^2.10.0", "json-to-graphql-query": "^2.2.5", "prismjs": "^1.29.0", "query-string": "^9.0.0", @@ -91,13 +89,13 @@ "unidiff": "^1.0.4", "use-query-params": "^2.2.1", "vite": "^5.2.8", - "vite-tsconfig-paths": "^4.3.2", - "web-vitals": "^2.1.4" + "vite-tsconfig-paths": "^4.3.2" }, "devDependencies": { + "@biomejs/biome": "1.9.3", "@graphql-codegen/cli": "^5.0.2", "@graphql-codegen/typescript": "^4.0.9", - "@playwright/test": "^1.47.0", + "@playwright/test": "^1.47.2", "@testing-library/react": "^14.2.2", "@types/loadable__component": "^5.13.9", "@types/node": "^20.12.3", @@ -108,30 +106,16 @@ "@types/react-dom": "^18.2.23", "@types/react-test-renderer": "^18.0.7", "@types/sha1": "^1.1.5", - "@typescript-eslint/eslint-plugin": "^7.16.0", "@vitest/coverage-v8": "^1.4.0", - "cypress": "^13.7.2", - "eslint": "^8.57.0", - "eslint-config-prettier": "^9.1.0", - "eslint-plugin-cypress": "^2.15.1", - "eslint-plugin-import": "^2.29.1", - "eslint-plugin-n": "^16.6.2", - "eslint-plugin-promise": "^6.1.1", - "eslint-plugin-react": "^7.34.1", - "eslint-plugin-unused-imports": "^3.2.0", + "cypress": "^13.15.0", "husky": "^8.0.3", "jsdom": "^24.0.0", "lint-staged": "^15.2.10", "openapi-typescript": "^7.0.2", "postcss": "^8.4.23", - "prettier": "2.8.8", - "prettier-eslint": "^16.3.0", - "pretty-quick": "^3.1.3", "react-test-renderer": "^18.2.0", "tailwindcss": "^3.4.3", - "ts-node": "^10.9.2", "typescript": "^5.5.3", - "typescript-eslint": "^7.16.0", "vitest": "^1.4.0" }, "overrides": { @@ -146,15 +130,7 @@ } }, "browserslist": { - "production": [ - ">0.2%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] + "production": [">0.2%", "not dead", "not op_mini all"], + "development": ["last 1 chrome version", "last 1 firefox version", "last 1 safari version"] } } diff --git a/frontend/app/src/App.tsx b/frontend/app/src/App.tsx index a911334429..c8e4bc3957 100644 --- a/frontend/app/src/App.tsx +++ b/frontend/app/src/App.tsx @@ -3,21 +3,25 @@ import { ErrorBoundary } from "react-error-boundary"; import { RouterProvider } from "react-router-dom"; import { Slide, ToastContainer } from "react-toastify"; -import { ApolloProvider } from "@apollo/client"; import graphqlClient from "@/graphql/graphqlClientApollo"; import { AuthProvider } from "@/hooks/useAuth"; import { router } from "@/router"; import ErrorFallback from "@/screens/errors/error-fallback"; import { store } from "@/state"; +import { ApolloProvider } from "@apollo/client"; +import { addCollection } from "@iconify-icon/react"; +import mdiIcons from "@iconify-json/mdi/icons.json"; import "./styles/index.css"; import "react-toastify/dist/ReactToastify.css"; +addCollection(mdiIcons); + export function App() { return ( - - + + - - + + ); } diff --git a/frontend/app/src/Root.tsx b/frontend/app/src/Root.tsx index 381f9091c2..f431fe73f1 100644 --- a/frontend/app/src/Root.tsx +++ b/frontend/app/src/Root.tsx @@ -1,17 +1,11 @@ import { ALERT_TYPES, Alert } from "@/components/ui/alert"; import { CONFIG } from "@/config/config"; +import LoadingScreen from "@/screens/loading-screen/loading-screen"; +import { fetchUrl } from "@/utils/fetch"; import { useSetAtom } from "jotai"; import { ReactNode, useEffect, useState } from "react"; import { toast } from "react-toastify"; -import reportWebVitals from "./reportWebVitals"; import { Config, configState } from "./state/atoms/config.atom"; -import LoadingScreen from "@/screens/loading-screen/loading-screen"; -import { fetchUrl } from "@/utils/fetch"; - -import { addCollection } from "@iconify-icon/react"; -import mdiIcons from "@iconify-json/mdi/icons.json"; - -addCollection(mdiIcons); export const Root = ({ children }: { children?: ReactNode }) => { const setConfig = useSetAtom(configState); @@ -63,8 +57,3 @@ export const Root = ({ children }: { children?: ReactNode }) => { return children; }; - -// If you want to start measuring performance in your app, pass a function -// to log results (for example: reportWebVitals(console.log)) -// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals -reportWebVitals(); diff --git a/frontend/app/src/components/account-menu.tsx b/frontend/app/src/components/account-menu.tsx index f9b4412526..e37cf2d67c 100644 --- a/frontend/app/src/components/account-menu.tsx +++ b/frontend/app/src/components/account-menu.tsx @@ -1,111 +1,200 @@ +import { Button } from "@/components/buttons/button-primitive"; import { Avatar } from "@/components/display/avatar"; +import { Skeleton } from "@/components/skeleton"; +import { ALERT_TYPES, Alert } from "@/components/ui/alert"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuDivider, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { INFRAHUB_DOC_LOCAL, INFRAHUB_GITHUB_URL, INFRAHUB_SWAGGER_DOC_URL } from "@/config/config"; +import { ACCOUNT_GENERIC_OBJECT } from "@/config/constants"; import { getProfileDetails } from "@/graphql/queries/accounts/getProfileDetails"; import { useAuth } from "@/hooks/useAuth"; -import useQuery from "@/hooks/useQuery"; -import { userNavigation } from "@/screens/layout/navigation-list"; -import { genericsState, IModelSchema } from "@/state/atoms/schema.atom"; -import { classNames } from "@/utils/common"; -import { gql } from "@apollo/client"; -import { Menu, Transition } from "@headlessui/react"; -import { Fragment, useEffect } from "react"; +import { AppVersion } from "@/screens/layout/app-version"; +import { IModelSchema, genericsState } from "@/state/atoms/schema.atom"; +import { constructPath } from "@/utils/fetch"; +import { gql, useQuery } from "@apollo/client"; +import { Icon } from "@iconify-icon/react"; +import { useAtomValue } from "jotai"; +import { useEffect } from "react"; import { Link, useLocation } from "react-router-dom"; -import { useAtomValue } from "jotai/index"; -import { ACCOUNT_OBJECT } from "@/config/constants"; import { toast } from "react-toastify"; -import { Alert, ALERT_TYPES } from "@/components/ui/alert"; - -const customId = "profile-alert"; export const AccountMenu = () => { - const { isAuthenticated } = useAuth(); - - const location = useLocation(); + const { isAuthenticated, signOut } = useAuth(); const generics = useAtomValue(genericsState); - const schema = generics.find((s) => s.kind === ACCOUNT_OBJECT); + const schema = generics.find((s) => s.kind === ACCOUNT_GENERIC_OBJECT); if (!isAuthenticated) { - return ( - - Sign in - - ); + return ; } if (!schema) { - return ; + return ; } - return ; + return ; }; -const AccountAvatarWithMenu = ({ schema }: { schema: IModelSchema }) => { - const { signOut } = useAuth(); +const CommonMenuItems = () => ( + <> + + + + GitHub Repository + + + + + + + Infrahub documentation + + + + + + + GraphQL Sandbox + + + + + + + Swagger documentation + + + +); + +const UnauthenticatedAccountMenu = () => { + const location = useLocation(); + + return ( + + +
+ +
+ +
+ Log in + anonymous +
+ { + event.preventDefault(); + }} + asChild + > + + + + + + + + + + + Log in + + + + + +
+ ); +}; + +const AuthenticatedAccountMenu = ({ + schema, + signOut, +}: { + schema: IModelSchema; + signOut: () => void; +}) => { const query = gql(getProfileDetails({ ...schema })); const { error, loading, data } = useQuery(query); useEffect(() => { if (error) { toast(, { - toastId: customId, + toastId: "profile-alert", }); - - // Sign out because there is nothing from the API for that user id signOut(); } - }, [error]); + }, [error, signOut]); - if (loading || !schema) { - return ; + if (loading) { + return ; } const profile = data?.AccountProfile; return ( - - - - - - - - {userNavigation.map((item) => ( - - {({ active }) => ( - - {item.name} - - )} - - ))} - - - - - - - + + + + + + + + + + Account settings + + + + + + + + Logout + + + + + + ); +}; + +const AccountMenuSkeleton = () => { + return ( +
+ + +
+ + +
+
); }; diff --git a/frontend/app/src/components/branch-selector.tsx b/frontend/app/src/components/branch-selector.tsx index a73f503747..29521dd781 100644 --- a/frontend/app/src/components/branch-selector.tsx +++ b/frontend/app/src/components/branch-selector.tsx @@ -1,132 +1,188 @@ -import { DateDisplay } from "@/components/display/date-display"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; import { QSP } from "@/config/qsp"; import { Branch } from "@/generated/graphql"; import { usePermission } from "@/hooks/usePermission"; import { branchesState, currentBranchAtom } from "@/state/atoms/branches.atom"; import { branchesToSelectOptions } from "@/utils/branches"; -import { classNames } from "@/utils/common"; import { Icon } from "@iconify-icon/react"; import { useAtomValue } from "jotai/index"; -import React from "react"; +import React, { useEffect, useState } from "react"; import { StringParam, useQueryParam } from "use-query-params"; -import { ButtonWithTooltip } from "./buttons/button-primitive"; -import { SelectButton } from "./buttons/select-button"; +import { ComboboxItem } from "@/components/ui/combobox"; +import { Command, CommandEmpty, CommandInput, CommandList } from "@/components/ui/command"; +import graphqlClient from "@/graphql/graphqlClientApollo"; +import { constructPath } from "@/utils/fetch"; +import { useSetAtom } from "jotai"; +import { Button, ButtonWithTooltip, LinkButton } from "./buttons/button-primitive"; import BranchCreateForm from "./form/branch-create-form"; -import { SelectOption } from "./inputs/select"; - -const getBranchIcon = (branch: Branch | null, active?: Boolean) => - branch && ( - <> - {branch.has_schema_changes && ( - - )} - - {branch.is_default && ( - - )} - - {branch.sync_with_git && ( - - )} - - ); export default function BranchSelector() { - const branches = useAtomValue(branchesState); - const branch = useAtomValue(currentBranchAtom); - const [, setBranchInQueryString] = useQueryParam(QSP.BRANCH, StringParam); + const currentBranch = useAtomValue(currentBranchAtom); + const [isOpen, setIsOpen] = useState(false); + const [displayForm, setDisplayForm] = useState(false); - const valueLabel = ( -
- {getBranchIcon(branch, true)} + useEffect(() => { + if (isOpen) graphqlClient.refetchQueries({ include: ["GetBranches"] }); + }, [isOpen]); -

{branch?.name}

-
+ return ( + { + setDisplayForm(false); + setIsOpen(open); + }} + > + + + + + + {displayForm ? ( + { + setDisplayForm(false); + }} + onSuccess={() => { + setDisplayForm(false); + setIsOpen(false); + }} + data-testid="branch-create-form" + /> + ) : ( + + )} + + ); +} - const branchesOptions: SelectOption[] = branchesToSelectOptions(branches); +function BranchSelect({ + setPopoverOpen, + setFormOpen, +}: { + setPopoverOpen: (open: boolean) => void; + setFormOpen: (open: boolean) => void; +}) { + const branches = useAtomValue(branchesState); + const setCurrentBranch = useSetAtom(currentBranchAtom); + const [, setBranchInQueryString] = useQueryParam(QSP.BRANCH, StringParam); - const onBranchChange = (branch: Branch) => { - if (branch?.is_default) { - // undefined is needed to remove a parameter from the QSP - setBranchInQueryString(undefined); + const handleBranchChange = (branch: Branch) => { + if (branch.is_default) { + setBranchInQueryString(undefined); // undefined is needed to remove a parameter from the QSP } else { setBranchInQueryString(branch.name); } + setCurrentBranch(branch); + setPopoverOpen(false); }; - const renderOption = ({ option, active, selected }: any) => ( -
-
{getBranchIcon(option, active)}
- -
-

{option.name}

- {selected ? ( - - - - ) : null} + return ( + <> + +
+ + + +
+ + + No branch found + {branchesToSelectOptions(branches).map((branch) => ( + handleBranchChange(branch)} + /> + ))} + +
+
+ setPopoverOpen(false)} + > + View all branches +
- - {option?.created_at && } -
+ ); +} - /** - * There's always a main branch present at least. - */ - if (!branches.length) { - return null; - } +function BranchOption({ branch, onChange }: { branch: Branch; onChange: () => void }) { + const currentBranch = useAtomValue(currentBranchAtom); return ( -
- - - -
+ +
+ {branch.name} + +
+ {branch.is_default && ( + default + )} + {branch.sync_with_git && ( + + )} +
+
+
); } -export const BranchFormTrigger = () => { +export const BranchFormTriggerButton = ({ setOpen }: { setOpen: (open: boolean) => void }) => { const permission = usePermission(); - const [open, setOpen] = React.useState(false); return ( - - - - - - - - -
Create a branch
-
- setOpen(false)} onSuccess={() => setOpen(false)} /> - - + { + if (e.key === "Enter") { + e.stopPropagation(); + setOpen(true); + } + }} + onClick={(e) => { + e.stopPropagation(); + setOpen(true); + }} + data-testid="create-branch-button" + > + + ); }; diff --git a/frontend/app/src/components/buttons/button-primitive.tsx b/frontend/app/src/components/buttons/button-primitive.tsx index 537df4a76a..2d2943105e 100644 --- a/frontend/app/src/components/buttons/button-primitive.tsx +++ b/frontend/app/src/components/buttons/button-primitive.tsx @@ -1,13 +1,13 @@ +import { Spinner } from "@/components/ui/spinner"; import { focusStyle } from "@/components/ui/style"; import { Tooltip, TooltipProps } from "@/components/ui/tooltip"; import { classNames } from "@/utils/common"; -import { cva, type VariantProps } from "class-variance-authority"; +import { type VariantProps, cva } from "class-variance-authority"; import { ButtonHTMLAttributes, forwardRef } from "react"; import { Link, LinkProps } from "react-router-dom"; -import { Spinner } from "@/components/ui/spinner"; const buttonVariants = cva( - "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium disabled:opacity-60 disabled:cursor-not-allowed", + "inline-flex items-center justify-center whitespace-nowrap rounded text-sm font-medium disabled:opacity-60 disabled:cursor-not-allowed", { variants: { variant: { @@ -25,7 +25,7 @@ const buttonVariants = cva( xs: "h-7 px-2 text-xs", sm: "h-7 px-2 text-sm", icon: "h-7 w-7 rounded-full", - square: "h-9 w-9 rounded-md", + square: "h-9 w-9", }, }, defaultVariants: { @@ -48,7 +48,8 @@ export const Button = forwardRef( type={type} className={classNames(focusStyle, buttonVariants({ variant, size, className }))} ref={ref} - {...props}> + {...props} + > {isLoading && } {children} diff --git a/frontend/app/src/components/buttons/button.tsx b/frontend/app/src/components/buttons/button.tsx index ada844957c..d25b4f4002 100644 --- a/frontend/app/src/components/buttons/button.tsx +++ b/frontend/app/src/components/buttons/button.tsx @@ -107,7 +107,8 @@ export const Button = forwardRef((props: ButtonProps, ref: any) => { className={classNames(DEFAULT_CLASS(className, buttonType), customClassName, className)} {...propsToPass} onClick={handleClick} - disabled={isLoading ? true : propsToPass.disabled}> + disabled={isLoading ? true : propsToPass.disabled} + > {isLoading && } {!isLoading && children} diff --git a/frontend/app/src/components/buttons/retry.tsx b/frontend/app/src/components/buttons/retry.tsx index 4ce2ee4fa4..15bd14e18f 100644 --- a/frontend/app/src/components/buttons/retry.tsx +++ b/frontend/app/src/components/buttons/retry.tsx @@ -32,7 +32,8 @@ export const Retry = (props: tRetryProps) => { isLoading ? "animate-spin" : "", isLoading || isDisabled ? "!cursor-not-allowed" : "" )} - onClick={handleClick}> + onClick={handleClick} + > ((props: type="button" className={classNames(DEFAULT_CLASS(className), customClassName, className)} onClick={onClick} - {...propsToPass}> + {...propsToPass} + > {props.children} ); diff --git a/frontend/app/src/components/buttons/select-button.tsx b/frontend/app/src/components/buttons/select-button.tsx index b98d09094a..8d1dc39d21 100644 --- a/frontend/app/src/components/buttons/select-button.tsx +++ b/frontend/app/src/components/buttons/select-button.tsx @@ -21,7 +21,8 @@ export const SelectButton = (props: any) => { as={Button} buttonType={BUTTON_TYPES.MAIN} data-cy="branch-list-display-button" - data-testid="branch-list-display-button"> + data-testid="branch-list-display-button" + >
{valueLabel}
@@ -35,11 +36,13 @@ export const SelectButton = (props: any) => { as={Fragment} leave="transition ease-in duration-100" leaveFrom="opacity-100" - leaveTo="opacity-0"> + leaveTo="opacity-0" + > + data-testid="branch-list-dropdown" + > {options.map((option: any) => ( { "cursor-pointer select-none p-4 text-sm" ) } - value={option}> + value={option} + > {(attributes) => renderOption({ option, diff --git a/frontend/app/src/components/buttons/tabs-buttons.tsx b/frontend/app/src/components/buttons/tabs-buttons.tsx index e3447ab444..794bc980be 100644 --- a/frontend/app/src/components/buttons/tabs-buttons.tsx +++ b/frontend/app/src/components/buttons/tabs-buttons.tsx @@ -1,7 +1,7 @@ import { QSP } from "@/config/qsp"; +import { ReactNode } from "react"; import { StringParam, useQueryParam } from "use-query-params"; import { Button } from "./button-primitive"; -import { ReactNode } from "react"; type Tab = { name?: string; @@ -31,7 +31,8 @@ export const TabsButtons = (props: TabsProps) => { (qspTab && qspTab === tab.name) || (!qspTab && index === 0) ? "active" : "outline" } disabled={tab.disabled} - className={"border-0 px-4 py-2 rounded-none"}> + className={"border-0 px-4 py-2 rounded-none"} + > {tab.label} ))} diff --git a/frontend/app/src/components/buttons/toggle-buttons.tsx b/frontend/app/src/components/buttons/toggle-buttons.tsx index 0c7b204dc6..b90094ae26 100644 --- a/frontend/app/src/components/buttons/toggle-buttons.tsx +++ b/frontend/app/src/components/buttons/toggle-buttons.tsx @@ -23,7 +23,8 @@ export const ToggleButtons = ({ tabs, ...props }: TabsProps) => { size={"sm"} variant={tab.isActive ? "active" : "ghost"} className={"cursor-pointer border-0 px-4 py-2 rounded-none"} - {...props}> + {...props} + > {tab.label} ))} diff --git a/frontend/app/src/components/conversations/add-comment.tsx b/frontend/app/src/components/conversations/add-comment.tsx index f15242bc9d..c25b964185 100644 --- a/frontend/app/src/components/conversations/add-comment.tsx +++ b/frontend/app/src/components/conversations/add-comment.tsx @@ -1,11 +1,11 @@ import { Button, LinkButton } from "@/components/buttons/button-primitive"; +import TextareaField from "@/components/form/fields/textarea.field"; +import { isRequired } from "@/components/form/utils/validation"; +import { Form, FormRef, FormSubmit } from "@/components/ui/form"; import { useAuth } from "@/hooks/useAuth"; import { constructPath } from "@/utils/fetch"; import React, { forwardRef, ReactElement } from "react"; import { useLocation } from "react-router-dom"; -import { Form, FormRef, FormSubmit } from "@/components/ui/form"; -import TextareaField from "@/components/form/fields/textarea.field"; -import { isRequired } from "@/components/form/utils/validation"; type CommentFormData = { comment: string; @@ -30,7 +30,8 @@ export const AddComment = forwardRef(({ onSubmit, onCancel comment: comment.value as string, }; await onSubmit(commentFormData); - }}> + }} + > (({ onSubmit, onCancel size="sm" variant="primary" to={constructPath("/signin")} - state={{ from: location }}> + state={{ from: location }} + > Sign in {" "} to be able to add a comment. diff --git a/frontend/app/src/components/conversations/comment.tsx b/frontend/app/src/components/conversations/comment.tsx index 8a1898b16d..600fae51d2 100644 --- a/frontend/app/src/components/conversations/comment.tsx +++ b/frontend/app/src/components/conversations/comment.tsx @@ -14,7 +14,8 @@ export const Comment: React.FC = ({ author, createdAt, content, cl return (
+ data-testid="comment" + >
diff --git a/frontend/app/src/components/conversations/thread.tsx b/frontend/app/src/components/conversations/thread.tsx index 56a07c7fde..d03a43b71e 100644 --- a/frontend/app/src/components/conversations/thread.tsx +++ b/frontend/app/src/components/conversations/thread.tsx @@ -2,6 +2,7 @@ import { Button } from "@/components/buttons/button"; import { Checkbox } from "@/components/inputs/checkbox"; import ModalConfirm from "@/components/modals/modal-confirm"; import { ALERT_TYPES, Alert } from "@/components/ui/alert"; +import { Card } from "@/components/ui/card"; import { Tooltip } from "@/components/ui/tooltip"; import { PROPOSED_CHANGES_THREAD_COMMENT_OBJECT } from "@/config/constants"; import graphqlClient from "@/graphql/graphqlClientApollo"; @@ -21,7 +22,6 @@ import { useState } from "react"; import { toast } from "react-toastify"; import { AddComment } from "./add-comment"; import { Comment } from "./comment"; -import { Card } from "@/components/ui/card"; type tThread = { thread: any; @@ -177,7 +177,8 @@ export const Thread = (props: tThread) => { + data-cy="thread" + > {displayContext && getThreadTitle(thread)} {sortedComments.map((comment: any, index: number) => ( diff --git a/frontend/app/src/components/display/accordion.tsx b/frontend/app/src/components/display/accordion.tsx index 5d2d401568..3a402ee152 100644 --- a/frontend/app/src/components/display/accordion.tsx +++ b/frontend/app/src/components/display/accordion.tsx @@ -1,6 +1,6 @@ +import { classNames } from "@/utils/common"; import { Icon } from "@iconify-icon/react"; import { CSSProperties, useState } from "react"; -import { classNames } from "@/utils/common"; export type AccordionProps = { title?: any; @@ -30,13 +30,15 @@ export default function Accordion({
setIsOpen(!open)}> + onClick={() => setIsOpen(!open)} + > + )} + > {open ? : } diff --git a/frontend/app/src/components/display/avatar.tsx b/frontend/app/src/components/display/avatar.tsx index f73c13ca84..6d5c45d61b 100644 --- a/frontend/app/src/components/display/avatar.tsx +++ b/frontend/app/src/components/display/avatar.tsx @@ -1,15 +1,13 @@ import LoadingScreen from "@/screens/loading-screen/loading-screen"; import { classNames } from "@/utils/common"; -import { cva, type VariantProps } from "class-variance-authority"; +import { type VariantProps, cva } from "class-variance-authority"; -export const initials = (name?: string) => +export const initials = (name: string) => name - ? name - .split(" ") - .map((word) => word[0]) - .join("") - .toUpperCase() - : "-"; + .split(" ") + .map((word) => word[0]) + .join("") + .toUpperCase(); const avatarVariants = cva("rounded-full flex justify-center items-center", { variants: { @@ -20,6 +18,7 @@ const avatarVariants = cva("rounded-full flex justify-center items-center", { size: { default: "h-12 w-12", sm: "h-6 w-6 text-xs", + md: "h-8 w-8 text-xs", }, }, defaultVariants: { @@ -30,12 +29,13 @@ const avatarVariants = cva("rounded-full flex justify-center items-center", { interface tAvatar extends VariantProps { name?: string; + text?: string; className?: string; isLoading?: boolean; } export const Avatar = (props: tAvatar) => { - const { name, variant, size, className, isLoading, ...otherProps } = props; + const { name, text, variant, size, className, isLoading, ...otherProps } = props; if (isLoading) { return ( @@ -47,7 +47,9 @@ export const Avatar = (props: tAvatar) => { return (
- {initials(name)} + {name && initials(name)} + {text} + {!name && !text && "-"}
); }; diff --git a/frontend/app/src/components/display/badge-circle.tsx b/frontend/app/src/components/display/badge-circle.tsx index 8ce1d18ef0..18d5392de2 100644 --- a/frontend/app/src/components/display/badge-circle.tsx +++ b/frontend/app/src/components/display/badge-circle.tsx @@ -131,7 +131,8 @@ export const BadgeCircle = (props: tBadgeCircleProps) => { className, onDelete ? "cursor-pointer" : "" )} - onClick={handleClick}> + onClick={handleClick} + > {children} diff --git a/frontend/app/src/components/display/badge.tsx b/frontend/app/src/components/display/badge.tsx index 918d62b2f9..c08178c66a 100644 --- a/frontend/app/src/components/display/badge.tsx +++ b/frontend/app/src/components/display/badge.tsx @@ -118,7 +118,8 @@ export const Badge = ({ onDelete && !disabled ? "cursor-pointer" : "" )} onClick={handleClick} - {...props}> + {...props} + > {children} {onDelete && ( diff --git a/frontend/app/src/components/display/circle.tsx b/frontend/app/src/components/display/circle.tsx index 143aae4356..97ef41eabb 100644 --- a/frontend/app/src/components/display/circle.tsx +++ b/frontend/app/src/components/display/circle.tsx @@ -13,7 +13,8 @@ export const Circle = (props: tCircleProps) => { ); diff --git a/frontend/app/src/components/display/color-display.tsx b/frontend/app/src/components/display/color-display.tsx index d5d95aa8b1..8f1d4d703c 100644 --- a/frontend/app/src/components/display/color-display.tsx +++ b/frontend/app/src/components/display/color-display.tsx @@ -1,12 +1,30 @@ import { getTextColor } from "@/utils/common"; +import { Tooltip } from "../ui/tooltip"; type tColorDisplay = { color?: string | null; value?: string | null; + description?: string | null; }; export const ColorDisplay = (props: tColorDisplay) => { - const { color, value } = props; + const { color, value, description } = props; + + if (description) { + return ( + +
+ {value} +
+
+ ); + } return (
{ style={{ backgroundColor: color || "", color: color ? getTextColor(color) : "", - }}> + }} + > {value}
); diff --git a/frontend/app/src/components/display/meta-details-tooltips.tsx b/frontend/app/src/components/display/meta-details-tooltips.tsx index 19ea5f0c25..2729db33be 100644 --- a/frontend/app/src/components/display/meta-details-tooltips.tsx +++ b/frontend/app/src/components/display/meta-details-tooltips.tsx @@ -76,7 +76,8 @@ export default function MetaDetailsTooltip({ variant="ghost" className="text-gray-500 focus-visible:ring-0" data-cy="metadata-button" - data-testid="view-metadata-button"> + data-testid="view-metadata-button" + > diff --git a/frontend/app/src/components/display/pie-chart.tsx b/frontend/app/src/components/display/pie-chart.tsx index 154965d81a..650ca6fd97 100644 --- a/frontend/app/src/components/display/pie-chart.tsx +++ b/frontend/app/src/components/display/pie-chart.tsx @@ -67,7 +67,8 @@ export const PieChart = (props: tPieChart) => { startAngle={90} endAngle={-270} // label={renderCustomizedLabel} - labelLine={false}> + labelLine={false} + > {data.map((entry, index) => ( ))} diff --git a/frontend/app/src/components/display/pill.tsx b/frontend/app/src/components/display/pill.tsx index 69ba0aceea..e688fda736 100644 --- a/frontend/app/src/components/display/pill.tsx +++ b/frontend/app/src/components/display/pill.tsx @@ -1,12 +1,14 @@ import LoadingScreen from "@/screens/loading-screen/loading-screen"; import { classNames } from "@/utils/common"; -import { ReactElement } from "react"; +import { Icon } from "@iconify-icon/react"; +import { ReactNode } from "react"; type tPill = { type?: PILL_TYPES; className?: string; - children?: ReactElement | string | ReactElement[] | string[]; + children?: ReactNode; isLoading?: boolean; + error?: boolean; }; export enum PILL_TYPES { @@ -40,13 +42,14 @@ const getClassName = (type?: PILL_TYPES) => { }; export const Pill = (props: tPill) => { - const { type, className = "", children, isLoading } = props; + const { type, className = "", children, isLoading, error } = props; const customClassName = getClassName(type); return ( {isLoading && } + {error && } {!isLoading && children} ); diff --git a/frontend/app/src/components/display/popover.tsx b/frontend/app/src/components/display/popover.tsx index cdb38dc401..600ad15b99 100644 --- a/frontend/app/src/components/display/popover.tsx +++ b/frontend/app/src/components/display/popover.tsx @@ -75,7 +75,8 @@ const PopOverPanel = forwardRef( maxHeightClass[maxHeight ?? POPOVER_SIZE.NONE] )} style={style} - static={staticProp}> + static={staticProp} + > {({ close }) => ( <> {title &&
{title}
} diff --git a/frontend/app/src/components/display/properties-popover.tsx b/frontend/app/src/components/display/properties-popover.tsx index 4020f5ad96..2b887d39f4 100644 --- a/frontend/app/src/components/display/properties-popover.tsx +++ b/frontend/app/src/components/display/properties-popover.tsx @@ -59,7 +59,8 @@ const PropertiesPopover = ({ disabled={!permission.write.allow} tooltipEnabled={!permission.write.allow} tooltipContent={permission.write.message ?? undefined} - data-testid="properties-edit-button"> + data-testid="properties-edit-button" + >
@@ -77,7 +78,8 @@ const PropertiesPopover = ({ /> } open={showMetaEditModal} - setOpen={setShowMetaEditModal}> + setOpen={setShowMetaEditModal} + > setShowMetaEditModal(false)} onUpdateComplete={() => refetch && refetch()} diff --git a/frontend/app/src/components/display/question-mark.tsx b/frontend/app/src/components/display/question-mark.tsx index 71414370d9..e9287731a9 100644 --- a/frontend/app/src/components/display/question-mark.tsx +++ b/frontend/app/src/components/display/question-mark.tsx @@ -1,5 +1,5 @@ -import { Tooltip } from "@/components/ui/tooltip"; import { Button } from "@/components/buttons/button-primitive"; +import { Tooltip } from "@/components/ui/tooltip"; import { classNames } from "@/utils/common"; type tQuestionMark = { @@ -16,7 +16,8 @@ export const QuestionMark = ({ className, message }: tQuestionMark) => { size="icon" variant="outline" className={classNames("h-4 w-4 p-2 text-[10px]", className)} - data-cy="question-mark"> + data-cy="question-mark" + > ? diff --git a/frontend/app/src/components/display/slide-over.tsx b/frontend/app/src/components/display/slide-over.tsx index e9990959cb..8668312447 100644 --- a/frontend/app/src/components/display/slide-over.tsx +++ b/frontend/app/src/components/display/slide-over.tsx @@ -1,11 +1,11 @@ -import { Dialog, Transition } from "@headlessui/react"; -import React, { Fragment, useRef, useState } from "react"; -import { Badge } from "@/components/ui/badge"; -import { Icon } from "@iconify-icon/react"; import { ObjectHelpButton } from "@/components/menu/object-help-button"; -import { useAtomValue } from "jotai/index"; +import { Badge } from "@/components/ui/badge"; import { currentBranchAtom } from "@/state/atoms/branches.atom"; import { IModelSchema } from "@/state/atoms/schema.atom"; +import { Dialog, Transition } from "@headlessui/react"; +import { Icon } from "@iconify-icon/react"; +import { useAtomValue } from "jotai/index"; +import React, { Fragment, useRef, useState } from "react"; import ModalDelete from "../modals/modal-delete"; interface Props { @@ -51,7 +51,8 @@ export default function SlideOver(props: Props) { enterTo="opacity-100" leave="ease-in-out duration-500" leaveFrom="opacity-100" - leaveTo="opacity-0"> + leaveTo="opacity-0" + >
+ leaveTo="translate-x-full" + > + data-testid="side-panel-container" + >
{title} @@ -103,7 +106,7 @@ export default function SlideOver(props: Props) { type SlideOverTitleProps = { schema: IModelSchema; - currentObjectLabel?: string; + currentObjectLabel?: string | null; title?: React.ReactNode; subtitle?: React.ReactNode; }; diff --git a/frontend/app/src/components/display/text-display.tsx b/frontend/app/src/components/display/text-display.tsx index 9a07ce0141..ffc187572f 100644 --- a/frontend/app/src/components/display/text-display.tsx +++ b/frontend/app/src/components/display/text-display.tsx @@ -1,6 +1,6 @@ import { classNames } from "@/utils/common"; import { Icon } from "@iconify-icon/react"; -import { forwardRef, HTMLAttributes, useState } from "react"; +import { HTMLAttributes, forwardRef, useState } from "react"; const MAX_TEXT_LENGTH = 200; diff --git a/frontend/app/src/components/editor/code-editor.tsx b/frontend/app/src/components/editor/code-editor.tsx index 6368d72869..912d3c47ca 100644 --- a/frontend/app/src/components/editor/code-editor.tsx +++ b/frontend/app/src/components/editor/code-editor.tsx @@ -35,7 +35,8 @@ export const CodeEditor = (props: any) => { diff --git a/frontend/app/src/components/editor/index.tsx b/frontend/app/src/components/editor/index.tsx index 28175abc0a..e94e3da49d 100644 --- a/frontend/app/src/components/editor/index.tsx +++ b/frontend/app/src/components/editor/index.tsx @@ -65,7 +65,8 @@ export const MarkdownEditor: FC = forwardRef< focus-within:outline focus-within:outline-custom-blue-600 focus-within:border-custom-blue-600 `, className - )}> + )} + > void; diff --git a/frontend/app/src/components/editor/markdown-editor-header.tsx b/frontend/app/src/components/editor/markdown-editor-header.tsx index bb315e1d75..7fd8e868c5 100644 --- a/frontend/app/src/components/editor/markdown-editor-header.tsx +++ b/frontend/app/src/components/editor/markdown-editor-header.tsx @@ -2,7 +2,7 @@ import { Button } from "@/components/buttons/button"; import { UseCodeMirror } from "@/hooks/useCodeMirror"; import { Icon } from "@iconify-icon/react"; import React, { FC } from "react"; -import { boldCommand, EditorCommand, italicCommand, strikethroughCommand } from "./commands"; +import { EditorCommand, boldCommand, italicCommand, strikethroughCommand } from "./commands"; type ToolbarProps = { codeMirror: UseCodeMirror }; @@ -23,7 +23,8 @@ const ToolBar: FC = ({ codeMirror }) => { className="bg-white border-none p-0 text-xl shadow-none" type="button" aria-label={label} - onMouseDown={handleButtonMouseDown(onClick)}> + onMouseDown={handleButtonMouseDown(onClick)} + > ))} diff --git a/frontend/app/src/components/filters/filter-form.tsx b/frontend/app/src/components/filters/filter-form.tsx new file mode 100644 index 0000000000..6d75d94ca8 --- /dev/null +++ b/frontend/app/src/components/filters/filter-form.tsx @@ -0,0 +1,53 @@ +import { Button } from "@/components/buttons/button-primitive"; +import { FilterKindSelector } from "@/components/filters/filter-kind-selector"; +import { getObjectFromFilters } from "@/components/filters/utils/getObjectFromFilters"; +import { DynamicInput } from "@/components/form/dynamic-form"; +import { getFormFieldsFromSchema } from "@/components/form/utils/getFormFieldsFromSchema"; +import { Form, FormProps, FormRef, FormSubmit } from "@/components/ui/form"; +import { Filter } from "@/hooks/useFilters"; +import { IModelSchema } from "@/state/atoms/schema.atom"; +import { classNames, isGeneric } from "@/utils/common"; +import React, { forwardRef } from "react"; + +export interface FilterFormProps extends FormProps { + schema: IModelSchema; + filters: Array; + onCancel?: () => void; +} + +export const FilterForm = forwardRef( + ({ filters, className, schema, onSubmit, onCancel, ...props }, ref) => { + const fields = getFormFieldsFromSchema({ + schema, + isFilterForm: true, + initialObject: getObjectFromFilters(schema, filters), + }); + + return ( +
+ {isGeneric(schema) && schema.used_by?.length ? ( + + ) : null} + + {fields.map((field) => ( + + ))} + +
+ {onCancel && ( + + )} + + Apply filters +
+ + ); + } +); diff --git a/frontend/app/src/components/filters/filter-kind-selector.tsx b/frontend/app/src/components/filters/filter-kind-selector.tsx new file mode 100644 index 0000000000..d7b35d5294 --- /dev/null +++ b/frontend/app/src/components/filters/filter-kind-selector.tsx @@ -0,0 +1,97 @@ +import { DEFAULT_FORM_FIELD_VALUE } from "@/components/form/constants"; +import { LabelFormField } from "@/components/form/fields/common"; +import { updateFormFieldValue } from "@/components/form/utils/updateFormFieldValue"; +import { Badge } from "@/components/ui/badge"; +import { + Combobox, + ComboboxContent, + ComboboxItem, + ComboboxList, + ComboboxTrigger, +} from "@/components/ui/combobox"; +import { FormField, FormInput, FormMessage } from "@/components/ui/form"; +import useFilters from "@/hooks/useFilters"; +import { iGenericSchema, profilesAtom, schemaState } from "@/state/atoms/schema.atom"; +import { useAtomValue } from "jotai/index"; +import React, { useState } from "react"; + +export const FilterKindSelector = ({ genericSchema }: { genericSchema: iGenericSchema }) => { + const [activeFilters] = useFilters(); + const availableNodes = useAtomValue(schemaState); + const availableProfiles = useAtomValue(profilesAtom); + const allAvailableSchemas = [...availableNodes, ...availableProfiles]; + + const selectedKindFilter = activeFilters.find((filter) => filter.name == "kind__value"); + const compatibleSchemas = (genericSchema.used_by ?? []) + .map((kindValue) => { + if (!allAvailableSchemas) return null; + + return allAvailableSchemas.find((schema) => schema.kind === kindValue); + }) + .filter((schema) => !!schema); + + return ( + { + const [isDropdownOpen, setIsDropdownOpen] = useState(false); + const currentFieldValue = field.value; + const selectedSchema = allAvailableSchemas.find( + (schema) => schema.kind === currentFieldValue?.value + ); + + return ( +
+ + + + + + {selectedSchema && ( +
+ {selectedSchema.label} {selectedSchema.namespace} +
+ )} +
+
+ + + + {compatibleSchemas.map((schemaOption) => ( + { + const newSelectedValue = + schemaOption.kind === selectedSchema?.kind ? null : schemaOption.kind; + field.onChange( + updateFormFieldValue(newSelectedValue ?? null, DEFAULT_FORM_FIELD_VALUE) + ); + setIsDropdownOpen(false); + }} + > + {schemaOption.label}{" "} + {schemaOption?.namespace} + + ))} + + +
+ + +
+ ); + }} + /> + ); +}; diff --git a/frontend/app/src/components/filters/filters.tsx b/frontend/app/src/components/filters/filters.tsx index c4d8097fc8..03468a4fdd 100644 --- a/frontend/app/src/components/filters/filters.tsx +++ b/frontend/app/src/components/filters/filters.tsx @@ -1,16 +1,14 @@ -import { BUTTON_TYPES, Button } from "@/components/buttons/button"; -import { ButtonWithTooltip } from "@/components/buttons/button-with-tooltip"; +import { Button, ButtonWithTooltip } from "@/components/buttons/button-primitive"; import SlideOver, { SlideOverTitle } from "@/components/display/slide-over"; +import { FilterForm } from "@/components/filters/filter-form"; +import { getFiltersFromFormData } from "@/components/filters/utils/getFiltersFromFormData"; +import { FormFieldValue } from "@/components/form/type"; import { SEARCH_FILTERS } from "@/config/constants"; import useFilters from "@/hooks/useFilters"; -import { Icon } from "@iconify-icon/react"; -import { useState } from "react"; -import ObjectForm from "@/components/form/object-form"; import usePagination from "@/hooks/usePagination"; import { IModelSchema } from "@/state/atoms/schema.atom"; -import { getFiltersFromFormData } from "@/components/filters/utils/getFiltersFromFormData"; -import { FormFieldValue } from "@/components/form/type"; -import { getObjectFromFilters } from "@/components/filters/utils/getObjectFromFilters"; +import { Icon } from "@iconify-icon/react"; +import React, { useState } from "react"; type FiltersProps = { schema: IModelSchema; @@ -32,8 +30,6 @@ export const Filters = ({ schema }: FiltersProps) => { setFilters(newFilters); }; - const handleShowFilters = () => setShowFilters(true); - const handleSubmit = (formData: Record) => { const newFilters = getFiltersFromFormData(formData); @@ -50,26 +46,23 @@ export const Filters = ({ schema }: FiltersProps) => { const currentFilters = filters.filter((filter) => !SEARCH_FILTERS.includes(filter.name)); return ( -
-
+ <> +
+ onClick={() => setShowFilters(true)} + > Filters: {currentFilters.length} {!!currentFilters.length && ( - )} @@ -78,15 +71,15 @@ export const Filters = ({ schema }: FiltersProps) => { } open={showFilters} - setOpen={setShowFilters}> - handleSubmit(formData)} - kind={schema?.kind} - isFilterForm - submitLabel="Apply filters" - currentObject={getObjectFromFilters(schema, currentFilters)} + setOpen={setShowFilters} + > + setShowFilters(false)} /> -
+ ); }; diff --git a/frontend/app/src/components/filters/utils/getObjectFromFilters.ts b/frontend/app/src/components/filters/utils/getObjectFromFilters.ts index 1f4516afb8..88d6cb7219 100644 --- a/frontend/app/src/components/filters/utils/getObjectFromFilters.ts +++ b/frontend/app/src/components/filters/utils/getObjectFromFilters.ts @@ -1,60 +1,63 @@ import { Filter } from "@/hooks/useFilters"; +import { IModelSchema } from "@/state/atoms/schema.atom"; import { AttributeType, RelationshipManyType, RelationshipOneType, RelationshipType, } from "@/utils/getObjectItemDisplayValue"; -import { IModelSchema } from "@/state/atoms/schema.atom"; export const getObjectFromFilters = ( schema: IModelSchema, filters: Array ): Record => { - return filters.reduce((acc, filter) => { - const [fieldName, fieldKey] = filter.name.split("__"); - - if (fieldKey === "value") { - return { - ...acc, - [fieldName]: { value: filter.value } satisfies AttributeType, - }; - } + return filters.reduce( + (acc, filter) => { + const [fieldName, fieldKey] = filter.name.split("__"); - if (fieldKey === "ids") { - const relationshipSchema = schema.relationships?.find(({ name }) => name === fieldName); - if (!relationshipSchema) return acc; - - if (relationshipSchema.cardinality === "many") { + if (fieldKey === "value") { return { ...acc, - [fieldName]: { - edges: filter.value.map( - (v: string) => - ({ - node: { - id: v, - display_label: "", - __typename: relationshipSchema.peer, - }, - } satisfies RelationshipOneType) - ), - } satisfies RelationshipManyType, + [fieldName]: { value: filter.value } satisfies AttributeType, }; } - if (relationshipSchema.cardinality === "one") { - return { - ...acc, - [fieldName]: { - node: { id: filter.value[0], display_label: "", __typename: relationshipSchema.peer }, - } satisfies RelationshipOneType, - }; + if (fieldKey === "ids") { + const relationshipSchema = schema.relationships?.find(({ name }) => name === fieldName); + if (!relationshipSchema) return acc; + + if (relationshipSchema.cardinality === "many") { + return { + ...acc, + [fieldName]: { + edges: filter.value.map( + (v: string) => + ({ + node: { + id: v, + display_label: "", + __typename: relationshipSchema.peer, + }, + }) satisfies RelationshipOneType + ), + } satisfies RelationshipManyType, + }; + } + + if (relationshipSchema.cardinality === "one") { + return { + ...acc, + [fieldName]: { + node: { id: filter.value[0], display_label: "", __typename: relationshipSchema.peer }, + } satisfies RelationshipOneType, + }; + } + + return acc; } return acc; - } - - return acc; - }, {} as Record); + }, + {} as Record + ); }; diff --git a/frontend/app/src/components/form/branch-create-form.tsx b/frontend/app/src/components/form/branch-create-form.tsx index 39dee72e4c..ad0a0036e6 100644 --- a/frontend/app/src/components/form/branch-create-form.tsx +++ b/frontend/app/src/components/form/branch-create-form.tsx @@ -1,16 +1,16 @@ +import { Button } from "@/components/buttons/button-primitive"; +import CheckboxField from "@/components/form/fields/checkbox.field"; +import InputField from "@/components/form/fields/input.field"; +import { isMinLength, isRequired } from "@/components/form/utils/validation"; +import { Form, FormSubmit } from "@/components/ui/form"; import { QSP } from "@/config/qsp"; import { Branch } from "@/generated/graphql"; import { BRANCH_CREATE } from "@/graphql/mutations/branches/createBranch"; -import { branchesState } from "@/state/atoms/branches.atom"; import { useMutation } from "@/hooks/useQuery"; +import { branchesState } from "@/state/atoms/branches.atom"; import { useAtom } from "jotai"; -import { StringParam, useQueryParam } from "use-query-params"; -import { Form, FormSubmit } from "@/components/ui/form"; -import { Button } from "@/components/buttons/button-primitive"; import React from "react"; -import InputField from "@/components/form/fields/input.field"; -import { isMinLength, isRequired } from "@/components/form/utils/validation"; -import CheckboxField from "@/components/form/fields/checkbox.field"; +import { StringParam, useQueryParam } from "use-query-params"; type BranchFormData = { name: string; @@ -56,7 +56,8 @@ const BranchCreateForm = ({ onCancel, onSuccess }: BranchCreateFormProps) => { sync_with_git: !!data.sync_with_git.value, }; await handleSubmit(branchData); - }}> + }} + > { diff --git a/frontend/app/src/components/form/fields/checkbox.field.tsx b/frontend/app/src/components/form/fields/checkbox.field.tsx index 5d33244d79..dae988a300 100644 --- a/frontend/app/src/components/form/fields/checkbox.field.tsx +++ b/frontend/app/src/components/form/fields/checkbox.field.tsx @@ -1,8 +1,8 @@ -import { Checkbox } from "@/components/inputs/checkbox"; -import { FormField, FormInput, FormMessage } from "@/components/ui/form"; -import { FormFieldProps, FormAttributeValue } from "@/components/form/type"; import { LabelFormField } from "@/components/form/fields/common"; +import { FormAttributeValue, FormFieldProps } from "@/components/form/type"; import { updateFormFieldValue } from "@/components/form/utils/updateFormFieldValue"; +import { Checkbox } from "@/components/inputs/checkbox"; +import { FormField, FormInput, FormMessage } from "@/components/ui/form"; export interface CheckboxFieldProps extends FormFieldProps {} diff --git a/frontend/app/src/components/form/fields/color.field.tsx b/frontend/app/src/components/form/fields/color.field.tsx index 187f8b6ddc..34c87f74a3 100644 --- a/frontend/app/src/components/form/fields/color.field.tsx +++ b/frontend/app/src/components/form/fields/color.field.tsx @@ -1,9 +1,9 @@ -import { FormField, FormInput, FormMessage } from "@/components/ui/form"; -import { FormFieldProps, FormAttributeValue } from "@/components/form/type"; -import { InputProps } from "@/components/ui/input"; -import { ColorPicker } from "@/components/inputs/color-picker"; import { LabelFormField } from "@/components/form/fields/common"; +import { FormAttributeValue, FormFieldProps } from "@/components/form/type"; import { updateFormFieldValue } from "@/components/form/utils/updateFormFieldValue"; +import { ColorPicker } from "@/components/inputs/color-picker"; +import { FormField, FormInput, FormMessage } from "@/components/ui/form"; +import { InputProps } from "@/components/ui/input"; export interface InputFieldProps extends FormFieldProps, diff --git a/frontend/app/src/components/form/fields/common.tsx b/frontend/app/src/components/form/fields/common.tsx index 8a6aa1508f..de6352c60b 100644 --- a/frontend/app/src/components/form/fields/common.tsx +++ b/frontend/app/src/components/form/fields/common.tsx @@ -1,18 +1,18 @@ -import { FormLabel } from "@/components/ui/form"; import { QuestionMark } from "@/components/display/question-mark"; -import { classNames } from "@/utils/common"; -import { LabelProps } from "@/components/ui/label"; -import { Icon } from "@iconify-icon/react"; -import { Badge } from "@/components/ui/badge"; -import React from "react"; import { AttributeValueFromProfile, FormFieldValue, RelationshipValueFormPool, } from "@/components/form/type"; +import { Badge } from "@/components/ui/badge"; +import { FormLabel } from "@/components/ui/form"; +import { LabelProps } from "@/components/ui/label"; import { Tooltip } from "@/components/ui/tooltip"; -import { Link } from "react-router-dom"; +import { classNames } from "@/utils/common"; import { getObjectDetailsUrl2 } from "@/utils/objects"; +import { Icon } from "@iconify-icon/react"; +import React from "react"; +import { Link } from "react-router-dom"; export const InputUniqueTips = ({ className }: { className: string }) => ( @@ -66,12 +66,14 @@ const ProfileSourceBadge = ({ fieldData }: { fieldData: AttributeValueFromProfil

This value is set by a profile:

+ className="underline inline-flex items-center gap-1" + > {fieldData?.source?.label}

You can override it by typing another value in the input.

- }> + } + >
- }> + } + > @@ -60,7 +61,8 @@ export const ObjectCreateFormTrigger = ({ /> } open={showCreateDrawer} - setOpen={setShowCreateDrawer}> + setOpen={setShowCreateDrawer} + > { setShowCreateDrawer(false); diff --git a/frontend/app/src/components/form/object-edit-slide-over-trigger.tsx b/frontend/app/src/components/form/object-edit-slide-over-trigger.tsx index 313a1fd4b0..22ac8276c4 100644 --- a/frontend/app/src/components/form/object-edit-slide-over-trigger.tsx +++ b/frontend/app/src/components/form/object-edit-slide-over-trigger.tsx @@ -32,7 +32,8 @@ const ObjectEditSlideOverTrigger = ({ tooltipEnabled={!permission.write.allow} tooltipContent={permission.write.message ?? undefined} data-testid="edit-button" - {...props}> + {...props} + > @@ -46,7 +47,8 @@ const ObjectEditSlideOverTrigger = ({ /> } open={isEditDrawerOpen} - setOpen={setIsEditDrawerOpen}> + setOpen={setIsEditDrawerOpen} + > setIsEditDrawerOpen(false)} onUpdateComplete={onUpdateComplete} diff --git a/frontend/app/src/components/form/object-form.tsx b/frontend/app/src/components/form/object-form.tsx index 72ded9f3d3..991ec514be 100644 --- a/frontend/app/src/components/form/object-form.tsx +++ b/frontend/app/src/components/form/object-form.tsx @@ -1,14 +1,24 @@ -import NoDataFound from "@/screens/errors/no-data-found"; import { DynamicFormProps } from "@/components/form/dynamic-form"; -import { AttributeType, RelationshipType } from "@/utils/getObjectItemDisplayValue"; -import { useSchema } from "@/hooks/useSchema"; import { GenericObjectForm } from "@/components/form/generic-object-form"; -import { NodeWithProfileForm } from "@/components/form/node-with-profile-form"; import { NodeForm, NodeFormSubmitParams } from "@/components/form/node-form"; -import { NUMBER_POOL_OBJECT, READONLY_REPOSITORY_KIND, REPOSITORY_KIND } from "@/config/constants"; -import { NumberPoolForm } from "@/screens/resource-manager/number-pool-form"; -import { lazy, Suspense } from "react"; +import { NodeWithProfileForm } from "@/components/form/node-with-profile-form"; +import { + ACCOUNT_GROUP_OBJECT, + ACCOUNT_OBJECT, + NUMBER_POOL_OBJECT, + OBJECT_PERMISSION_OBJECT, + READONLY_REPOSITORY_KIND, + REPOSITORY_KIND, +} from "@/config/constants"; +import { useSchema } from "@/hooks/useSchema"; +import NoDataFound from "@/screens/errors/no-data-found"; import LoadingScreen from "@/screens/loading-screen/loading-screen"; +import { NumberPoolForm } from "@/screens/resource-manager/number-pool-form"; +import { AccountForm } from "@/screens/role-management/account-form"; +import { AccountGroupForm } from "@/screens/role-management/account-group-form"; +import { ObjectPermissionForm } from "@/screens/role-management/object-permissions-form"; +import { AttributeType, RelationshipType } from "@/utils/getObjectItemDisplayValue"; +import { Suspense, lazy } from "react"; export type ProfileData = { [key: string]: string | Pick; @@ -24,13 +34,12 @@ export interface ObjectFormProps extends Omit void; currentObject?: Record; currentProfiles?: ProfileData[]; - isFilterForm?: boolean; isUpdate?: boolean; onSubmit?: (data: NodeFormSubmitParams) => void; onUpdateComplete?: () => void; } -const ObjectForm = ({ kind, isFilterForm, currentProfiles, ...props }: ObjectFormProps) => { +const ObjectForm = ({ kind, currentProfiles, ...props }: ObjectFormProps) => { const { schema, isNode, isGeneric } = useSchema(kind); if (!schema) { @@ -41,10 +50,6 @@ const ObjectForm = ({ kind, isFilterForm, currentProfiles, ...props }: ObjectFor ); } - if (isFilterForm) { - return ; - } - if ([REPOSITORY_KIND, READONLY_REPOSITORY_KIND].includes(kind)) { return ( }> @@ -57,24 +62,27 @@ const ObjectForm = ({ kind, isFilterForm, currentProfiles, ...props }: ObjectFor return ; } + if (kind === OBJECT_PERMISSION_OBJECT) { + return ; + } + + if (kind === ACCOUNT_GROUP_OBJECT) { + return ; + } + + if (kind === ACCOUNT_OBJECT) { + return ; + } + if (isGeneric) { return ; } if (isNode && schema.generate_profile) { - return ( - - ); + return ; } - return ( - - ); + return ; }; export default ObjectForm; diff --git a/frontend/app/src/components/form/pool-selector.tsx b/frontend/app/src/components/form/pool-selector.tsx index 401396c7c2..d0ebb8b0b6 100644 --- a/frontend/app/src/components/form/pool-selector.tsx +++ b/frontend/app/src/components/form/pool-selector.tsx @@ -1,12 +1,12 @@ -import React, { forwardRef } from "react"; +import { Button } from "@/components/buttons/button-primitive"; import { FormFieldValue, NumberPoolData } from "@/components/form/type"; -import { ComboboxList, tComboboxItem } from "@/components/ui/combobox"; +import { ComboboxList, tComboboxItem } from "@/components/ui/combobox-legacy"; import { Popover, PopoverAnchor, PopoverTrigger } from "@/components/ui/popover"; -import { Slot } from "@radix-ui/react-slot"; -import { Button } from "@/components/buttons/button-primitive"; -import { Icon } from "@iconify-icon/react"; import { Tooltip } from "@/components/ui/tooltip"; import { Combobox as ComboboxPrimitive } from "@headlessui/react"; +import { Icon } from "@iconify-icon/react"; +import { Slot } from "@radix-ui/react-slot"; +import React, { forwardRef } from "react"; export type PoolValue = { from_pool: { @@ -54,7 +54,8 @@ export const PoolSelector = forwardRef( @@ -66,7 +67,8 @@ export const PoolSelector = forwardRef( diff --git a/frontend/app/src/components/form/profiles-selector.tsx b/frontend/app/src/components/form/profiles-selector.tsx index 5129a33e8f..538f1179af 100644 --- a/frontend/app/src/components/form/profiles-selector.tsx +++ b/frontend/app/src/components/form/profiles-selector.tsx @@ -1,14 +1,14 @@ -import { genericsState, iNodeSchema, profilesAtom } from "@/state/atoms/schema.atom"; -import { useId } from "react"; -import { useAtomValue } from "jotai/index"; -import { getObjectAttributes } from "@/utils/getSchemaObjectColumns"; -import ErrorScreen from "@/screens/errors/error-screen"; +import { MultiCombobox } from "@/components/ui/combobox-legacy"; +import Label from "@/components/ui/label"; import { getProfiles } from "@/graphql/queries/objects/getProfiles"; -import { gql } from "@apollo/client"; import useQuery from "@/hooks/useQuery"; +import ErrorScreen from "@/screens/errors/error-screen"; import LoadingScreen from "@/screens/loading-screen/loading-screen"; -import Label from "@/components/ui/label"; -import { MultiCombobox } from "@/components/ui/combobox"; +import { genericsState, iNodeSchema, profilesAtom } from "@/state/atoms/schema.atom"; +import { getObjectAttributes } from "@/utils/getSchemaObjectColumns"; +import { gql } from "@apollo/client"; +import { useAtomValue } from "jotai/index"; +import { useId } from "react"; type ProfilesSelectorProps = { schema: iNodeSchema; diff --git a/frontend/app/src/components/form/type.ts b/frontend/app/src/components/form/type.ts index 0ba6a62230..98f619454b 100644 --- a/frontend/app/src/components/form/type.ts +++ b/frontend/app/src/components/form/type.ts @@ -1,9 +1,10 @@ +import { DropdownOption } from "@/components/inputs/dropdown"; +import { SelectOption } from "@/components/inputs/select"; import { FormField } from "@/components/ui/form"; import { SchemaAttributeType } from "@/screens/edit-form-hook/dynamic-control-types"; -import { ComponentProps } from "react"; -import { SelectOption } from "@/components/inputs/select"; -import { components } from "@/infraops"; +import { AttributeSchema, RelationshipSchema } from "@/screens/schema/types"; import { IModelSchema } from "@/state/atoms/schema.atom"; +import { ComponentProps } from "react"; type SourceType = "schema" | "user"; @@ -96,19 +97,15 @@ export type DynamicNumberFieldProps = FormFieldProps & { export type DynamicDropdownFieldProps = FormFieldProps & { type: "Dropdown"; - items: Array; - field?: - | components["schemas"]["AttributeSchema-Output"] - | components["schemas"]["RelationshipSchema-Output"]; + items: Array; + field?: AttributeSchema; schema?: IModelSchema; }; export type DynamicEnumFieldProps = FormFieldProps & { type: "enum"; - items: Array; - field?: - | components["schemas"]["AttributeSchema-Output"] - | components["schemas"]["RelationshipSchema-Output"]; + items: Array; + field?: AttributeSchema; schema?: IModelSchema; }; @@ -118,7 +115,7 @@ export type DynamicRelationshipFieldProps = Omit peer?: string; parent?: string; options?: SelectOption[]; - relationship: components["schemas"]["RelationshipSchema-Output"]; + relationship: RelationshipSchema; schema: IModelSchema; }; diff --git a/frontend/app/src/components/form/utils/getFieldDefaultValue.ts b/frontend/app/src/components/form/utils/getFieldDefaultValue.ts index 87399efc5b..6a3378668d 100644 --- a/frontend/app/src/components/form/utils/getFieldDefaultValue.ts +++ b/frontend/app/src/components/form/utils/getFieldDefaultValue.ts @@ -1,4 +1,3 @@ -import { FieldSchema, AttributeType } from "@/utils/getObjectItemDisplayValue"; import { ProfileData } from "@/components/form/object-form"; import { AttributeValueFormPool, @@ -6,8 +5,9 @@ import { AttributeValueFromUser, FormAttributeValue, } from "@/components/form/type"; -import * as R from "ramda"; import { LineageSource } from "@/generated/graphql"; +import { AttributeType, FieldSchema } from "@/utils/getObjectItemDisplayValue"; +import * as R from "ramda"; export type GetFieldDefaultValue = { fieldSchema: FieldSchema; diff --git a/frontend/app/src/components/form/utils/getFormFieldsFromSchema.ts b/frontend/app/src/components/form/utils/getFormFieldsFromSchema.ts index c2b7836ad7..6b002bcefb 100644 --- a/frontend/app/src/components/form/utils/getFormFieldsFromSchema.ts +++ b/frontend/app/src/components/form/utils/getFormFieldsFromSchema.ts @@ -1,12 +1,4 @@ -import { - genericsState, - iGenericSchema, - iNodeSchema, - profilesAtom, - schemaState, -} from "@/state/atoms/schema.atom"; -import { AttributeType, RelationshipType } from "@/utils/getObjectItemDisplayValue"; -import { AuthContextType } from "@/hooks/useAuth"; +import { ProfileData } from "@/components/form/object-form"; import { DynamicDropdownFieldProps, DynamicEnumFieldProps, @@ -17,19 +9,20 @@ import { FormFieldValue, NumberPoolData, } from "@/components/form/type"; -import { getOptionsFromAttribute, getRelationshipOptions } from "@/utils/getSchemaObjectColumns"; -import { isGeneric, sortByOrderWeight } from "@/utils/common"; import { getFieldDefaultValue } from "@/components/form/utils/getFieldDefaultValue"; -import { SchemaAttributeType } from "@/screens/edit-form-hook/dynamic-control-types"; -import { store } from "@/state"; -import { SCHEMA_ATTRIBUTE_KIND } from "@/config/constants"; -import { ProfileData } from "@/components/form/object-form"; -import { isFieldDisabled } from "@/components/form/utils/isFieldDisabled"; import { getRelationshipDefaultValue } from "@/components/form/utils/getRelationshipDefaultValue"; -import { Filter } from "@/hooks/useFilters"; import { getRelationshipParent } from "@/components/form/utils/getRelationshipParent"; -import { isRequired } from "@/components/form/utils/validation"; import { getRelationshipsForForm } from "@/components/form/utils/getRelationshipsForForm"; +import { isFieldDisabled } from "@/components/form/utils/isFieldDisabled"; +import { isRequired } from "@/components/form/utils/validation"; +import { SCHEMA_ATTRIBUTE_KIND } from "@/config/constants"; +import { AuthContextType } from "@/hooks/useAuth"; +import { SchemaAttributeType } from "@/screens/edit-form-hook/dynamic-control-types"; +import { store } from "@/state"; +import { genericsState, iGenericSchema, iNodeSchema, schemaState } from "@/state/atoms/schema.atom"; +import { sortByOrderWeight } from "@/utils/common"; +import { AttributeType, RelationshipType } from "@/utils/getObjectItemDisplayValue"; +import { getRelationshipOptions } from "@/utils/getSchemaObjectColumns"; type GetFormFieldsFromSchema = { schema: iNodeSchema | iGenericSchema; @@ -37,7 +30,6 @@ type GetFormFieldsFromSchema = { initialObject?: Record; auth?: AuthContextType; isFilterForm?: boolean; - filters?: Array; pools?: Array; isUpdate?: boolean; }; @@ -48,7 +40,6 @@ export const getFormFieldsFromSchema = ({ initialObject, auth, isFilterForm, - filters, pools = [], isUpdate, }: GetFormFieldsFromSchema): Array => { @@ -124,8 +115,8 @@ export const getFormFieldsFromSchema = ({ field: attribute, schema: schema, items: (attribute.choices ?? []).map((choice) => ({ - id: choice.id ?? choice.name, - name: choice.label ?? choice.name, + value: choice.name, + label: choice.label ?? choice.name, color: choice.color ?? undefined, description: choice.description ?? undefined, })), @@ -141,7 +132,7 @@ export const getFormFieldsFromSchema = ({ field: attribute, schema: schema, unique: attribute.unique, - items: getOptionsFromAttribute(attribute, basicFomFieldProps.defaultValue), + items: attribute.enum, }; return enumField; @@ -168,40 +159,5 @@ export const getFormFieldsFromSchema = ({ return field; }); - // Allow kind filter for generic - if (isFilterForm && isGeneric(schema) && schema.used_by?.length) { - const kindFilter = filters?.find((filter) => filter.name == "kind__value"); - const nodes = store.get(schemaState); - const profiles = store.get(profilesAtom); - const schemas = [...nodes, ...profiles]; - - const items = schema.used_by - .map((kind) => { - if (!schemas) return null; - - const relatedSchema = schemas.find((schema) => schema.kind === kind); - - if (!relatedSchema) return null; - - return { - id: relatedSchema.kind as string, - name: relatedSchema.label ?? relatedSchema.name, - badge: relatedSchema.namespace, - }; - }) - .filter((n) => n !== null); - - const genericKindField: DynamicDropdownFieldProps = { - name: "kind", - label: "Kind", - description: "Select a kind to filter nodes", - type: "Dropdown", - defaultValue: kindFilter ? { source: { type: "user" }, value: kindFilter.value } : undefined, - items, - }; - - return [genericKindField, ...formFields]; - } - return formFields; }; diff --git a/frontend/app/src/components/form/utils/getRelationshipDefaultValue.ts b/frontend/app/src/components/form/utils/getRelationshipDefaultValue.ts index 1b1b217ec9..e0fe0e98f7 100644 --- a/frontend/app/src/components/form/utils/getRelationshipDefaultValue.ts +++ b/frontend/app/src/components/form/utils/getRelationshipDefaultValue.ts @@ -1,8 +1,8 @@ import { FormRelationshipValue } from "@/components/form/type"; -import { RelationshipType } from "@/utils/getObjectItemDisplayValue"; +import { RESOURCE_GENERIC_KIND } from "@/screens/resource-manager/constants"; import { store } from "@/state"; import { schemaState } from "@/state/atoms/schema.atom"; -import { RESOURCE_GENERIC_KIND } from "@/screens/resource-manager/constants"; +import { RelationshipType } from "@/utils/getObjectItemDisplayValue"; type GetRelationshipDefaultValueParams = { relationshipData: RelationshipType | undefined; diff --git a/frontend/app/src/components/form/utils/getRelationshipsForForm.ts b/frontend/app/src/components/form/utils/getRelationshipsForForm.ts index abacd4cf49..d25750897d 100644 --- a/frontend/app/src/components/form/utils/getRelationshipsForForm.ts +++ b/frontend/app/src/components/form/utils/getRelationshipsForForm.ts @@ -1,5 +1,5 @@ -import { components } from "@/infraops"; import { relationshipKindForForm } from "@/config/constants"; +import { components } from "@/infraops"; import { RelationshipKind } from "@/screens/objects/types"; export const getRelationshipsForForm = ( diff --git a/frontend/app/src/components/form/utils/updateFormFieldValue.ts b/frontend/app/src/components/form/utils/updateFormFieldValue.ts index 59f73c5156..a334618cda 100644 --- a/frontend/app/src/components/form/utils/updateFormFieldValue.ts +++ b/frontend/app/src/components/form/utils/updateFormFieldValue.ts @@ -1,3 +1,4 @@ +import { PoolValue } from "@/components/form/pool-selector"; import { AttributeValueFormPool, FormAttributeValue, @@ -6,7 +7,6 @@ import { RelationshipValueFormPool, } from "@/components/form/type"; import { isDeepEqual } from "remeda"; -import { PoolValue } from "@/components/form/pool-selector"; export const updateFormFieldValue = ( newValue: Exclude["value"], diff --git a/frontend/app/src/components/inputs/checkbox.tsx b/frontend/app/src/components/inputs/checkbox.tsx index 36cd37faab..5883299440 100644 --- a/frontend/app/src/components/inputs/checkbox.tsx +++ b/frontend/app/src/components/inputs/checkbox.tsx @@ -1,6 +1,6 @@ import { focusStyle } from "@/components/ui/style"; import { classNames } from "@/utils/common"; -import { forwardRef, InputHTMLAttributes } from "react"; +import { InputHTMLAttributes, forwardRef } from "react"; interface CheckboxProps extends InputHTMLAttributes {} diff --git a/frontend/app/src/components/inputs/color-picker.tsx b/frontend/app/src/components/inputs/color-picker.tsx index ce9881ce1b..810994add0 100644 --- a/frontend/app/src/components/inputs/color-picker.tsx +++ b/frontend/app/src/components/inputs/color-picker.tsx @@ -39,7 +39,8 @@ export const ColorPicker = forwardRef((props, ref) => { className={classNames( "flex items-center relative", disabled && "pointer-events-none opacity-50" - )}> + )} + > ((props, ref) => { title={"Select a color"} height={POPOVER_SIZE.NONE} width={POPOVER_SIZE.NONE} - className="right- left-0"> + className="right- left-0" + > {() => (
diff --git a/frontend/app/src/components/inputs/date-picker.tsx b/frontend/app/src/components/inputs/date-picker.tsx index 5e00c0532f..7f3b0bd973 100644 --- a/frontend/app/src/components/inputs/date-picker.tsx +++ b/frontend/app/src/components/inputs/date-picker.tsx @@ -2,9 +2,9 @@ import DateTimePicker from "react-datepicker"; import "react-datepicker/dist/react-datepicker.css"; import { Button } from "@/components/buttons/button"; +import { Input } from "@/components/inputs/input"; import { format, isValid } from "date-fns"; import { forwardRef, useEffect, useRef, useState } from "react"; -import { Input } from "@/components/inputs/input"; export const DatePicker = forwardRef((props, ref) => { const { id, date, onChange, disabled, isProtected } = props; @@ -67,7 +67,8 @@ export const DatePicker = forwardRef((props, ref) => {
diff --git a/frontend/app/src/components/inputs/dropdown.tsx b/frontend/app/src/components/inputs/dropdown.tsx new file mode 100644 index 0000000000..36245e107c --- /dev/null +++ b/frontend/app/src/components/inputs/dropdown.tsx @@ -0,0 +1,274 @@ +import { Button } from "@/components/buttons/button-primitive"; +import SlideOver, { SlideOverTitle } from "@/components/display/slide-over"; +import DynamicForm from "@/components/form/dynamic-form"; +import { isRequired } from "@/components/form/utils/validation"; +import ModalDelete from "@/components/modals/modal-delete"; +import { Badge } from "@/components/ui/badge"; +import { + Combobox, + ComboboxContent, + ComboboxEmpty, + ComboboxItem, + ComboboxList, + ComboboxTrigger, +} from "@/components/ui/combobox"; +import { CommandItem } from "@/components/ui/command"; +import { + DROPDOWN_ADD_MUTATION, + DROPDOWN_REMOVE_MUTATION, +} from "@/graphql/mutations/schema/dropdown"; +import { useMutation } from "@/hooks/useQuery"; +import { AttributeSchema } from "@/screens/schema/types"; +import { IModelSchema } from "@/state/atoms/schema.atom"; +import { classNames, getTextColor } from "@/utils/common"; +import { Icon } from "@iconify-icon/react"; +import React, { forwardRef, HTMLAttributes, useState } from "react"; + +export type DropdownOption = { + value: string; + label: string; + color?: string; + description?: string; +}; + +export interface DropdownProps extends Omit, "onChange"> { + value?: DropdownOption["value"] | null; + items: Array; + className?: string; + onChange: (value: DropdownOption["value"] | null) => void; + schema?: IModelSchema; + field?: AttributeSchema; +} + +export interface DropdownItemProps extends React.ComponentPropsWithoutRef { + fieldSchema?: { + name: string; + }; + schema?: IModelSchema; + onDelete: (item: DropdownOption) => void; + item: DropdownOption; +} + +export const DropdownItem = React.forwardRef< + React.ElementRef, + DropdownItemProps +>(({ fieldSchema, schema, onDelete, className, item, ...props }, ref) => { + const [showDeleteModal, setShowDeleteModal] = useState(false); + const [removeDropdownOption, { loading }] = useMutation(DROPDOWN_REMOVE_MUTATION); + + return ( + +
+ + {item.label} + +

{item.description}

+
+ + {schema && fieldSchema && ( + <> + + + + Are you sure you want to delete the option{" "} + + {item.label} + {" "} + ? + + } + setOpen={setShowDeleteModal} + onCancel={() => setShowDeleteModal(false)} + onDelete={async () => { + try { + await removeDropdownOption({ + variables: { + kind: schema.kind, + attribute: fieldSchema.name, + dropdown: item.value, + }, + }); + onDelete(item); + } catch (error) { + console.error("Error deleting dropdown item:", error); + } + }} + open={showDeleteModal} + isLoading={loading} + /> + + )} +
+ ); +}); + +interface DropdownAddActionProps { + schema: IModelSchema; + field: AttributeSchema; + addOption: (item: DropdownOption) => void; +} + +export const DropdownAddAction: React.FC = ({ + schema, + field, + addOption, +}) => { + const [open, setOpen] = useState(false); + const [addDropdownItem] = useMutation(DROPDOWN_ADD_MUTATION); + + return ( +
+ + + + } + open={open} + setOpen={setOpen} + offset={1} + > + { + const { data } = await addDropdownItem({ + variables: { + kind: schema.kind, + attribute: field.name, + dropdown: formData.value.value, + label: formData.label?.value, + color: formData.color?.value, + description: formData.description?.value, + }, + }); + if (data?.SchemaDropdownAdd?.ok) { + addOption(data?.SchemaDropdownAdd?.object); + setOpen(false); + } + }} + onCancel={() => setOpen(false)} + className="p-4" + /> + +
+ ); +}; + +export const Dropdown = forwardRef( + ({ items, onChange, value, schema, field, ...props }, ref) => { + const [localItems, setLocalItems] = useState(items); + const [open, setOpen] = useState(false); + + const handleAddOption = (newOption: DropdownOption) => { + setLocalItems([...localItems, newOption]); + onChange(newOption.value); + }; + + const handleDeleteOption = (deletedItem: DropdownOption) => { + setLocalItems(localItems.filter((item) => item.value !== deletedItem.value)); + if (value === deletedItem.value) { + onChange(null); + } + }; + + const selectItem = localItems.find((item) => item.value === value); + + return ( + + + {selectItem?.label} + + + + + No dropdown found. + {localItems.map((item) => ( + { + onChange(item.value === value ? null : item.value); + setOpen(false); + }} + item={item} + onDelete={handleDeleteOption} + /> + ))} + + + {schema && field && ( + + )} + + + ); + } +); + +export function getDropdownStyle(color?: string | null) { + if (!color) return undefined; + + return { + backgroundColor: color, + color: getTextColor(color), + }; +} diff --git a/frontend/app/src/components/inputs/enum.tsx b/frontend/app/src/components/inputs/enum.tsx new file mode 100644 index 0000000000..88dd284243 --- /dev/null +++ b/frontend/app/src/components/inputs/enum.tsx @@ -0,0 +1,214 @@ +import { Button, ButtonProps } from "@/components/buttons/button-primitive"; +import SlideOver, { SlideOverTitle } from "@/components/display/slide-over"; +import DynamicForm from "@/components/form/dynamic-form"; +import { isRequired } from "@/components/form/utils/validation"; +import ModalDelete from "@/components/modals/modal-delete"; +import { + Combobox, + ComboboxContent, + ComboboxEmpty, + ComboboxItem, + ComboboxList, + ComboboxTrigger, +} from "@/components/ui/combobox"; +import { ENUM_ADD_MUTATION, ENUM_REMOVE_MUTATION } from "@/graphql/mutations/schema/enum"; +import { useMutation } from "@/hooks/useQuery"; +import { AttributeSchema } from "@/screens/schema/types"; +import { IModelSchema } from "@/state/atoms/schema.atom"; +import { Icon } from "@iconify-icon/react"; +import React, { forwardRef, useState } from "react"; + +export interface EnumDeleteButtonProps extends ButtonProps { + fieldSchema: AttributeSchema; + schema: IModelSchema; + value: string | number; + onDelete: (id: string | number) => void; +} + +export const EnumDeleteButton = React.forwardRef( + ({ fieldSchema, schema, onDelete, className, value, children, ...props }, ref) => { + const [showDeleteModal, setShowDeleteModal] = useState(false); + const [removeEnum, { loading }] = useMutation(ENUM_REMOVE_MUTATION, { + variables: { kind: schema?.kind, attribute: fieldSchema?.name, enum: value }, + }); + + const handleDelete = async () => { + try { + await removeEnum(); + onDelete(value); + } catch (error) { + console.error("Error deleting enum:", error); + } + }; + + return ( + <> + + + + Are you sure you want to delete the enum{" "} + {value}? + + } + setOpen={setShowDeleteModal} + onCancel={() => setShowDeleteModal(false)} + onDelete={handleDelete} + open={showDeleteModal} + isLoading={loading} + /> + + ); + } +); + +interface EnumAddActionProps { + schema?: IModelSchema; + field?: AttributeSchema; + addOption: (item: string | number) => void; +} + +export const EnumAddAction: React.FC = ({ schema, field, addOption }) => { + const [open, setOpen] = useState(false); + const [addEnum] = useMutation(ENUM_ADD_MUTATION); + + if (!schema || !field) return null; + + return ( +
+ + + + } + open={open} + setOpen={setOpen} + offset={1} + > + { + const newEnumValue = formData.enum.value; + const { data } = await addEnum({ + variables: { + kind: schema.kind, + attribute: field.name, + enum: newEnumValue, + }, + }); + if (data?.SchemaEnumAdd?.ok) { + addOption(newEnumValue as string | number); + setOpen(false); + } + }} + onCancel={() => setOpen(false)} + className="p-4" + /> + +
+ ); +}; + +export interface EnumProps { + items: Array; + value: string | number | null; + fieldSchema?: AttributeSchema; + schema?: IModelSchema; + className?: string; + onChange: (value: string | number | null) => void; +} + +export const Enum = forwardRef( + ({ items, value, fieldSchema, schema, onChange, ...props }, ref) => { + const [localItems, setLocalItems] = useState(items); + const [open, setOpen] = useState(false); + + const handleAddOption = (newOption: string | number) => { + setLocalItems([...localItems, newOption]); + onChange(newOption); + }; + + const handleDeleteOption = (deletedItem: string | number) => { + setLocalItems(localItems.filter((item) => item !== deletedItem)); + if (value === deletedItem) { + onChange(null); + } + }; + + return ( + + + {value} + + + + + No enum found. + {localItems.map((item) => ( + { + onChange(item === value ? null : item); + setOpen(false); + }} + {...props} + > + {item} + {schema && fieldSchema && ( + + )} + + ))} + + + + + + ); + } +); diff --git a/frontend/app/src/components/inputs/input.tsx b/frontend/app/src/components/inputs/input.tsx index faa553ba74..c9b7134d37 100644 --- a/frontend/app/src/components/inputs/input.tsx +++ b/frontend/app/src/components/inputs/input.tsx @@ -36,7 +36,8 @@ export const Input = forwardRef((props: any, ref: any) => { ); @@ -64,7 +65,8 @@ export const Input = forwardRef((props: any, ref: any) => { className={classNames( "absolute top-0 bottom-0 flex items-center", type === "number" ? "right-4" : "right-1" - )}> + )} + > {removeButton}
)} @@ -72,7 +74,8 @@ export const Input = forwardRef((props: any, ref: any) => { {error?.message && (
+ data-cy="field-error-message" + > {error?.message}
)} diff --git a/frontend/app/src/components/inputs/multiple-input.tsx b/frontend/app/src/components/inputs/multiple-input.tsx index 698ee14f6d..71cc6b220e 100644 --- a/frontend/app/src/components/inputs/multiple-input.tsx +++ b/frontend/app/src/components/inputs/multiple-input.tsx @@ -46,14 +46,16 @@ export const MultipleInput = React.forwardRef((props: MultipleInputProps, ref: a disabled ? "cursor-not-allowed bg-gray-100" : "", error && error?.message ? "ring-red-500 focus:ring-red-600" : "" )} - data-testid="multi-select-input"> + data-testid="multi-select-input" + > Empty list
{error?.message && (
+ data-cy="field-error-message" + > {error?.message}
)} @@ -72,7 +74,8 @@ export const MultipleInput = React.forwardRef((props: MultipleInputProps, ref: a className ?? "", disabled ? "cursor-not-allowed bg-gray-100" : "" )} - data-testid="multi-select-input"> + data-testid="multi-select-input" + > {value?.map((item: string | SelectOption, index: number) => ( + disabled={disabled} + > {typeof item === "object" ? item.name : item} ))} diff --git a/frontend/app/src/components/inputs/select.tsx b/frontend/app/src/components/inputs/select.tsx index 0b44152b42..272a0813ed 100644 --- a/frontend/app/src/components/inputs/select.tsx +++ b/frontend/app/src/components/inputs/select.tsx @@ -1,5 +1,6 @@ import { BUTTON_TYPES, Button } from "@/components/buttons/button"; import SlideOver, { SlideOverTitle } from "@/components/display/slide-over"; +import ObjectForm from "@/components/form/object-form"; import ModalDelete from "@/components/modals/modal-delete"; import { SCHEMA_DROPDOWN_ADD, @@ -13,7 +14,6 @@ import { basicMutation } from "@/graphql/mutations/objects/basicMutation"; import { getDropdownOptions } from "@/graphql/queries/objects/dropdownOptions"; import { useLazyQuery } from "@/hooks/useQuery"; import { FormFieldError } from "@/screens/edit-form-hook/form"; -import ObjectForm from "@/components/form/object-form"; import { currentBranchAtom } from "@/state/atoms/branches.atom"; import { namespacesState, profilesAtom, schemaState } from "@/state/atoms/schema.atom"; import { schemaKindNameState } from "@/state/atoms/schemaKindName.atom"; @@ -28,15 +28,15 @@ import { forwardRef, useContext, useEffect, useState } from "react"; import { Input } from "./input"; import { MultipleInput } from "./multiple-input"; +import DynamicForm from "@/components/form/dynamic-form"; import { Tooltip } from "@/components/ui/tooltip"; import { getObjectDisplayLabel } from "@/graphql/queries/objects/getObjectDisplayLabel"; +import usePrevious from "@/hooks/usePrevious"; import { POOLS_DICTIONNARY, POOLS_PEER } from "@/screens/ipam/constants"; import LoadingScreen from "@/screens/loading-screen/loading-screen"; import { comparedOptions } from "@/utils/array"; import { getOptionsFromRelationship } from "@/utils/getSchemaObjectColumns"; -import DynamicForm from "@/components/form/dynamic-form"; import { Badge } from "../ui/badge"; -import usePrevious from "@/hooks/usePrevious"; export type Parent = { name?: string; @@ -613,7 +613,8 @@ export const Select = forwardRef((props, ref) => { "relative cursor-pointer select-none py-2 pl-3 pr-9 m-2 rounded-md", active ? "bg-custom-blue-600 text-custom-white" : "bg-gray-100 text-gray-900" ) - }> + } + > Add {schemaKindName[peer]} @@ -629,7 +630,8 @@ export const Select = forwardRef((props, ref) => { value={addOption} className={ "flex relative select-none py-2 pl-3 pr-9 m-2 rounded-md bg-gray-100 text-gray-900 cursor-not-allowed" - }> + } + > Add option @@ -647,7 +649,8 @@ export const Select = forwardRef((props, ref) => { "flex relative cursor-pointer select-none py-2 pl-3 pr-9 m-2 rounded-md", active ? "bg-custom-blue-600 text-custom-white" : "bg-gray-100 text-gray-900" ) - }> + } + > Add option @@ -674,7 +677,8 @@ export const Select = forwardRef((props, ref) => { } open={open} setOpen={setOpen} - offset={1}> + offset={1} + > ((props, ref) => { } open={open} setOpen={setOpen} - offset={1}> + offset={1} + > {renderContentForDropdown()} @@ -734,7 +739,8 @@ export const Select = forwardRef((props, ref) => { } open={open} setOpen={setOpen} - offset={1}> + offset={1} + > {renderContentForEnum()} @@ -867,15 +873,17 @@ export const Select = forwardRef((props, ref) => { return (
+ data-testid="select-container" + > + {...otherProps} + >
((props, ref) => { canRequestPools ? "right-10" : "right-0" )} data-testid="select-open-option-button" - onClick={handleFocus}> + onClick={handleFocus} + > {loading && } {!loading && ( @@ -911,7 +920,8 @@ export const Select = forwardRef((props, ref) => { + onClick={handleFocusPools} + > @@ -923,18 +933,21 @@ export const Select = forwardRef((props, ref) => { className={classNames( "absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-custom-white text-base shadow-lg ring-1 ring-custom-black ring-opacity-5 focus:outline-none sm:text-sm", direction === SelectDirection.OVER ? "bottom-0" : "" - )}> + )} + > {finalOptions.map((option, index) => ( + style={getOptionStyle(option)} + > {({ selected }) => (
+ className={classNames("block truncate", selected ? "font-semibold" : "")} + > {option.name} @@ -943,7 +956,8 @@ export const Select = forwardRef((props, ref) => { className={classNames( "block truncate italic text-xs", selected ? "font-semibold" : "" - )}> + )} + > {option.description} )} @@ -962,12 +976,14 @@ export const Select = forwardRef((props, ref) => { {canRemoveOption(option.id) && ( + enabled={field.inherited} + > {!hideCancel && ( diff --git a/frontend/app/src/components/modals/modal-delete-object.tsx b/frontend/app/src/components/modals/modal-delete-object.tsx index 67b14c8610..27302e1a1e 100644 --- a/frontend/app/src/components/modals/modal-delete-object.tsx +++ b/frontend/app/src/components/modals/modal-delete-object.tsx @@ -1,16 +1,16 @@ -import React, { Fragment, useState } from "react"; -import ModalDelete from "./modal-delete"; -import { deleteObject } from "@/graphql/mutations/objects/deleteObject"; -import { toast } from "react-toastify"; -import { Alert, ALERT_TYPES } from "../ui/alert"; -import graphqlClient from "@/graphql/graphqlClientApollo"; import { ACCOUNT_TOKEN_OBJECT } from "@/config/constants"; +import graphqlClient from "@/graphql/graphqlClientApollo"; +import { deleteObject } from "@/graphql/mutations/objects/deleteObject"; +import { currentBranchAtom } from "@/state/atoms/branches.atom"; +import { datetimeAtom } from "@/state/atoms/time.atom"; import { stringifyWithoutQuotes } from "@/utils/string"; import { gql } from "@apollo/client"; import { useAtomValue } from "jotai"; -import { currentBranchAtom } from "@/state/atoms/branches.atom"; -import { datetimeAtom } from "@/state/atoms/time.atom"; +import React, { Fragment, useState } from "react"; import { useParams } from "react-router-dom"; +import { toast } from "react-toastify"; +import { ALERT_TYPES, Alert } from "../ui/alert"; +import ModalDelete from "./modal-delete"; interface iProps { label?: string | null; diff --git a/frontend/app/src/components/modals/modal-delete.tsx b/frontend/app/src/components/modals/modal-delete.tsx index 3baee92424..39b579900d 100644 --- a/frontend/app/src/components/modals/modal-delete.tsx +++ b/frontend/app/src/components/modals/modal-delete.tsx @@ -38,7 +38,8 @@ export default function ModalDelete({ enterTo="opacity-100" leave="ease-in duration-200" leaveFrom="opacity-100" - leaveTo="opacity-0"> + leaveTo="opacity-0" + >
@@ -51,17 +52,20 @@ export default function ModalDelete({ enterTo="opacity-100 translate-y-0 scale-100" leave="ease-in duration-200" leaveFrom="opacity-100 translate-y-0 scale-100" - leaveTo="opacity-0 translate-y-4 translate-y-0 scale-95"> + leaveTo="opacity-0 translate-y-4 translate-y-0 scale-95" + > + data-testid="modal-delete" + >
+ className="flex items-center font-semibold leading-6 text-gray-900" + >
+ data-testid="modal-delete-confirm" + > {confirmLabel ?? "Delete"}
diff --git a/frontend/app/src/components/modals/modal.tsx b/frontend/app/src/components/modals/modal.tsx index 045853fe03..e86c6fd715 100644 --- a/frontend/app/src/components/modals/modal.tsx +++ b/frontend/app/src/components/modals/modal.tsx @@ -23,7 +23,8 @@ export default function Modal(props: Props) { enterTo="opacity-100" leave="ease-in duration-200" leaveFrom="opacity-100" - leaveTo="opacity-0"> + leaveTo="opacity-0" + >
@@ -36,11 +37,13 @@ export default function Modal(props: Props) { enterTo="opacity-100 translate-y-0 sm:scale-100" leave="ease-in duration-200" leaveFrom="opacity-100 translate-y-0 sm:scale-100" - leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"> + leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95" + > + className="text-base font-semibold leading-6 text-gray-900 flex items-center px-6" + > Account Label @@ -51,14 +54,16 @@ export default function Modal(props: Props) {
diff --git a/frontend/app/src/components/search/search-actions.tsx b/frontend/app/src/components/search/search-actions.tsx index 702318dbea..1af7c34b46 100644 --- a/frontend/app/src/components/search/search-actions.tsx +++ b/frontend/app/src/components/search/search-actions.tsx @@ -1,6 +1,6 @@ import { Badge } from "@/components/ui/badge"; import { MenuItem } from "@/screens/layout/sidebar/desktop-menu"; -import { genericsState, IModelSchema, menuFlatAtom, schemaState } from "@/state/atoms/schema.atom"; +import { IModelSchema, genericsState, menuFlatAtom, schemaState } from "@/state/atoms/schema.atom"; import { constructPath } from "@/utils/fetch"; import { Icon } from "@iconify-icon/react"; import { useAtomValue } from "jotai"; diff --git a/frontend/app/src/components/search/search-anywhere.tsx b/frontend/app/src/components/search/search-anywhere.tsx index 9743a1ecb6..56e45f8820 100644 --- a/frontend/app/src/components/search/search-anywhere.tsx +++ b/frontend/app/src/components/search/search-anywhere.tsx @@ -1,50 +1,34 @@ +import { Button, ButtonProps } from "@/components/buttons/button-primitive"; +import { Card } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import Kbd from "@/components/ui/kbd"; import { classNames } from "@/utils/common"; import { Combobox, Dialog, Transition } from "@headlessui/react"; import { Icon } from "@iconify-icon/react"; -import { - ChangeEventHandler, - Fragment, - MouseEventHandler, - ReactNode, - forwardRef, - useEffect, - useState, -} from "react"; +import { Fragment, ReactNode, forwardRef, useEffect, useState } from "react"; import { Link, LinkProps, useNavigate } from "react-router-dom"; import { SearchActions } from "./search-actions"; import { SearchDocs } from "./search-docs"; import { SearchNodes } from "./search-nodes"; -import { Card } from "@/components/ui/card"; - -type SearchInputProps = { - className?: string; - value?: string; - onChange?: ChangeEventHandler; - onClick?: MouseEventHandler; -}; -const SearchTrigger = ({ value, onChange, onClick, className = "" }: SearchInputProps) => { +const SearchAnywhereTriggerButton = ({ className, ...props }: ButtonProps) => { return ( -
-