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/.devcontainer/welcome b/.devcontainer/welcome index 219f04c576..de8a3f1afe 100644 --- a/.devcontainer/welcome +++ b/.devcontainer/welcome @@ -1,9 +1,9 @@ -------------------------------------------------------------------- ******************************************************************** -👋👋👋 Welcome to the Infrahub Demo Codespace environment 👋👋👋 +👋👋👋 Welcome to the Infrahub Demo Codespaces environment 👋👋👋 -The Infrahub API server, Git agents and dependent services have been +The Infrahub API server, Task workers and dependent services have been started. Infrahub can be accessed by opening the ports tab, clicking on the globe icon in the Forwarded Address column of port 8000. diff --git a/.dockerignore b/.dockerignore index 0963440f8c..ab869eba21 100644 --- a/.dockerignore +++ b/.dockerignore @@ -15,10 +15,14 @@ node_modules coverage.xml .git* .devcontainer -.husky frontend/**/node_modules frontend/**/playwright-report +# Documentation +docs/build +docs/node_modules +docs/.docusaurus + # Direnv files (https://direnv.net/) .direnv/ .envrc diff --git a/.github/file-filters.yml b/.github/file-filters.yml index 1a9eacdeee..69af088525 100644 --- a/.github/file-filters.yml +++ b/.github/file-filters.yml @@ -13,6 +13,9 @@ development_files: &development_files backend_files: &backend_files - "backend/**" +sdk_files: &sdk_files + - "python_sdk" # Catch updates to the submodule commit + infrahub_poetry_files: &infrahub_poetry_files - "pyproject.toml" - "poetry.lock" @@ -52,6 +55,7 @@ backend_all: - *ci_config - *development_files - *infrahub_poetry_files + - *sdk_files documentation_all: - *development_files 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..71727c034a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,6 +7,7 @@ on: branches: - develop - stable + - release-* concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -15,15 +16,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 +91,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 +98,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 +117,11 @@ jobs: with: submodules: true - name: "Setup environment" - run: "pip install ruff==0.5.0" + run: "pip install ruff==0.7.1" - 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 +134,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,26 +235,30 @@ 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 true --local + poetry env use 3.12 + - name: "Install dependencies" + run: "poetry install --no-interaction --no-ansi" - name: "Unit Tests" - run: "invoke backend.test-unit" + run: "poetry run invoke backend.test-unit" - name: "Coveralls : Unit Tests" uses: coverallsapp/github-action@v2 continue-on-error: true @@ -270,7 +268,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,24 +294,28 @@ 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 true --local + poetry env use 3.12 + - name: "Install dependencies" + run: "poetry install --no-interaction --no-ansi" - name: "Mypy Tests" - run: "invoke backend.mypy --docker" + run: "poetry run invoke backend.mypy" - name: "Pylint Tests" - run: "invoke backend.pylint --docker" + run: "poetry run invoke backend.pylint" - name: "Integration Tests" - run: "invoke backend.test-integration" + run: "poetry run invoke backend.test-integration" - name: "Coveralls : Integration Tests" uses: coverallsapp/github-action@v2 continue-on-error: true @@ -323,7 +325,7 @@ jobs: flag-name: backend-integration parallel: true - backend-tests-neo4j: + backend-tests-memgraph: if: | always() && !cancelled() && !contains(needs.*.result, 'failure') && @@ -337,18 +339,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,20 +355,24 @@ 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 true --local + poetry env use 3.12 + - name: "Install dependencies" + run: "poetry install --no-interaction --no-ansi" - name: "Unit Tests" - run: "invoke backend.test-unit" + run: "poetry run invoke backend.test-unit" backend-validate-generated: if: | @@ -472,7 +475,7 @@ jobs: with: node-version: 20 cache: 'npm' - cache-dependency-path: package-lock.json + cache-dependency-path: docs/package-lock.json - name: "Install dependencies" run: npm install - name: "Setup Python environment" @@ -554,15 +557,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: @@ -575,22 +578,16 @@ jobs: with: node-version: 20 cache: 'npm' - cache-dependency-path: package-lock.json + cache-dependency-path: frontend/app/package-lock.json - 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 +628,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 +656,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 +688,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() @@ -716,8 +725,8 @@ jobs: if: always() run: 'echo "https://grafana-prod.tailc018d.ts.net/d/a4461039-bb27-4f57-9b2a-2c7f4e0a3459/e2e-tests?orgId=1&var-pr=$GITHUB_PR_NUMBER&var-job=$JOB_NAME&var-runner=$INFRAHUB_BUILD_NAME&from=$TEST_START_TIME&to=$(date +%s)000"' - # ------------------------------------------ Benchmarks ------------------------------------------------ - backend-benchmark: + # ------------------------------------------ E2E invoke demo ------------------------------------------------ + E2E-testing-invoke-demo-start: needs: - javascript-lint - files-changed @@ -727,46 +736,105 @@ jobs: always() && !cancelled() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') && - needs.files-changed.outputs.backend == 'true' + github.base_ref == 'develop' runs-on: group: huge-runners - env: - INFRAHUB_DB_TYPE: memgraph 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 environment" + python-version: "3.12" + + - name: Install Invoke 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" + pip install toml invoke - - name: Select infrahub db port - run: echo "INFRAHUB_DB_PORT=$(shuf -n 1 -i 10000-30000)" >> $GITHUB_ENV + - name: Set job name + run: echo JOB_NAME="$GITHUB_JOB" >> $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: "Store start time" + run: echo TEST_START_TIME=$(date +%s)000 >> $GITHUB_ENV - - name: Start dependencies - run: invoke dev.deps + - name: Run 'invoke demo' + run: | + unset INFRAHUB_IMAGE_VER + invoke demo.start demo.load-infra-schema demo.load-infra-data + - name: Display server logs + if: always() + run: docker logs "${INFRAHUB_BUILD_NAME}-server-1" + + - name: Display task worker 1 logs + if: always() + run: docker logs "${INFRAHUB_BUILD_NAME}-infrahub-git-1" + + - name: Display task worker 2 logs + if: always() + run: docker logs "${INFRAHUB_BUILD_NAME}-infrahub-git-2" + + - name: Display task manager logs + if: always() + run: docker logs "${INFRAHUB_BUILD_NAME}-task-manager-1" + + - name: Display database logs + if: always() + run: docker logs "${INFRAHUB_BUILD_NAME}-database-1" + + - name: Display message-queue logs + if: always() + run: docker logs "${INFRAHUB_BUILD_NAME}-message-queue-1" + + - name: "Clear docker environment and force vmagent to stop" + if: always() + run: docker compose -p $INFRAHUB_BUILD_NAME down -v --remove-orphans --rmi local + + # ------------------------------------------ Benchmarks ------------------------------------------------ + backend-benchmark: + needs: + - javascript-lint + - files-changed + - yaml-lint + - python-lint + if: | + always() && !cancelled() && + !contains(needs.*.result, 'failure') && + !contains(needs.*.result, 'cancelled') && + needs.files-changed.outputs.backend == 'true' + runs-on: + group: huge-runners + env: + 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: | + poetry config virtualenvs.create true --local + poetry env use 3.12 + - name: "Install dependencies" + run: "poetry install --no-interaction --no-ansi" - name: Update PATH run: "echo ~/.cargo/bin >> $GITHUB_PATH" - name: Run benchmarks @@ -774,8 +842,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..3660391a67 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 @@ -32,4 +33,6 @@ python_sdk/dist/* # Generated files generated/ query_performance_results/ -sync/dist/ \ No newline at end of file +sync/dist/ +helm/charts/ +helm/Chart.lock \ No newline at end of file diff --git a/.husky/.gitignore b/.husky/.gitignore deleted file mode 100644 index 31354ec138..0000000000 --- a/.husky/.gitignore +++ /dev/null @@ -1 +0,0 @@ -_ diff --git a/.husky/pre-commit b/.husky/pre-commit deleted file mode 100755 index 36af219892..0000000000 --- a/.husky/pre-commit +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh -. "$(dirname "$0")/_/husky.sh" - -npx lint-staged diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 96359c6623..f3a8f0b84a 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.7.1 hooks: # Run the linter. - id: ruff diff --git a/.vale/styles/Infrahub/sentence-case.yml b/.vale/styles/Infrahub/sentence-case.yml index 30f2e81512..585f6d3a45 100644 --- a/.vale/styles/Infrahub/sentence-case.yml +++ b/.vale/styles/Infrahub/sentence-case.yml @@ -49,9 +49,13 @@ exceptions: - JetStream - Jinja - Jinja2 + - JWT - Namespace - NATS - Node + - OAuth2 + - OIDC + - Open ID Connect - OpsMill - Pydantic - Python @@ -59,9 +63,12 @@ exceptions: - REST - RFile - SDK + - Single sign-on + - SSO - TLS - Tony Stark - TransformPython + - UI - Vale - VS Code - VS Code extensions diff --git a/.vale/styles/spelling-exceptions.txt b/.vale/styles/spelling-exceptions.txt index c0fb18b4a6..0cebfec559 100644 --- a/.vale/styles/spelling-exceptions.txt +++ b/.vale/styles/spelling-exceptions.txt @@ -4,6 +4,7 @@ APIs artifact_definitions artifact_name async +Authentik boolean check_definitions class_name @@ -64,6 +65,7 @@ Jotai json JSONSchema kbps +Keycloak Loopbacks markdownlint max_count @@ -80,6 +82,7 @@ Newsfragment Nornir npm o'brian +order_weight openconfig opentelemetry order_by @@ -94,12 +97,14 @@ REST ressources schema_mapping sdk +subcommand subnet template_path 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/CHANGELOG.md b/CHANGELOG.md index 597e6655ad..dd63e7c12f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,58 @@ This project uses [*towncrier*](https://towncrier.readthedocs.io/) and the chang +## [1.0.0](https://github.com/opsmill/infrahub/tree/v1.0.0) - 2024-10-30 + +### Removed + +- Remove previously deprecated GET API endpoint "/api/schema/" ([#3884](https://github.com/opsmill/infrahub/issues/3884)) + +### Deprecated + +- Marked CoreAccount.role as deprecated + Due to the new permissions framework the account roles "admin" / "read-only" / "read-write" are deprecated and will be removed in Infrahub 1.1 + +### Added + +- Reworked branch selector: + - Redesigned the UI + - Added filter for branch + - Improved accessibility & keyboard navigation + - Improved UX on new branch form + - Added quick link to view all branches +- Add support to sign in with OAuth2 and Open ID Connect (OIDC) ([#1568](https://github.com/opsmill/infrahub/issues/1568)) +- Add internal HTTP adapter to allow for generic access from Infrahub ([#3302](https://github.com/opsmill/infrahub/issues/3302)) +- Add support to search a node by human friendly ID within a GraphQL query ([#3908](https://github.com/opsmill/infrahub/issues/3908)) +- Added link to our Discord server in the account menu +- Added permissions framework for global and object kind level permissions + + In this first iteration the object permissions are applied to nodes as a whole, in upcoming versions it will be possible to define attribute level permissions as well. +- New permissions system in UI: + - Implemented CRUD views for managing accounts, groups, roles, and permissions + - Updated all components to support new permission system + - Added dynamic message display according to user access levels + +### Fixed + +- 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 ([#1075](https://github.com/opsmill/infrahub/issues/1075)) +- Add ability to import repositories with default branch other than 'main' ([#3435](https://github.com/opsmill/infrahub/issues/3435)) +- Disable approve/merge/close buttons for merged Proposed Changes ([#3495](https://github.com/opsmill/infrahub/issues/3495)) +- Fixed regex validation for List type attributes ([#3929](https://github.com/opsmill/infrahub/issues/3929)) +- Allow users to run artifacts and generators on nodes without name attribute ([#4062](https://github.com/opsmill/infrahub/issues/4062)) +- In the schema, properly delete inherited attribute and relationship on Node when the original attribute or relationship are being deleted on the Generic ([#4301](https://github.com/opsmill/infrahub/issues/4301)) +- "Retry All" button for checks is bigger ([#4315](https://github.com/opsmill/infrahub/issues/4315)) +- Add a size restriction on common attribute kinds. Only TextArea and JSON support large values ([#4432](https://github.com/opsmill/infrahub/issues/4432)) +- The HFID of a related node is properly returned via GraphQL in all scenarios ([#4482](https://github.com/opsmill/infrahub/issues/4482)) +- Add full validation to BranchMerge and BranchRebase mutations ([#4595](https://github.com/opsmill/infrahub/issues/4595)) +- Report user-friendly error for invalid uniqueness_constraints when loading schemas ([#4677](https://github.com/opsmill/infrahub/issues/4677)) +- Fixed pagination query for nodes with order_by clause using non unique attributes ([#4700](https://github.com/opsmill/infrahub/issues/4700)) +- Fixed schema migration when an attribute previously present on a node is added back ([#4727](https://github.com/opsmill/infrahub/issues/4727)) +- Add order_weight property to multiple attributes and relationships in the demo schema to improve how some models are displayed in the list views +- Changed the Python SDK connection timeout to 60s +- Fix metric missing the query name in Prometheus data +- Fixes an issue where docker compose would output ANSI control characters that don't support it +- Prevent temporary directories generated by Docusaurus to be imported by Docker + ## [0.16.4](https://github.com/opsmill/infrahub/tree/v0.16.4) - 2024-10-17 ### Fixed diff --git a/README.md b/README.md index 7388295bc6..c214ecff77 100644 --- a/README.md +++ b/README.md @@ -10,9 +10,6 @@ Infrahub from [OpsMill](https://opsmill.com) is taking a new approach to Infrast ## Quick Start -> [!NOTE] -> Infrahub is currently in beta, and the team is actively working towards reaching version 1.0 by the end of the year. The project is committed to ensuring data safety and providing a migration path for future releases. See our [FAQ](https://docs.infrahub.app/faq/) for more information. - Leveraging [GitHub Codespaces](https://docs.github.com/en/codespaces/overview), it's possible to start a new instance of Infrahub in the Cloud in minutes: | No Data | Demo Data | diff --git a/backend/infrahub/__init__.py b/backend/infrahub/__init__.py index 7cf736611d..659590272a 100644 --- a/backend/infrahub/__init__.py +++ b/backend/infrahub/__init__.py @@ -1,3 +1,3 @@ import importlib.metadata -__version__ = importlib.metadata.version("infrahub") +__version__ = importlib.metadata.version("infrahub-server") 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/artifact.py b/backend/infrahub/api/artifact.py index 8d3042e264..123b60b0c0 100644 --- a/backend/infrahub/api/artifact.py +++ b/backend/infrahub/api/artifact.py @@ -1,7 +1,5 @@ from __future__ import annotations -from typing import TYPE_CHECKING - from fastapi import APIRouter, Body, Depends, Request, Response from pydantic import BaseModel, Field @@ -11,11 +9,9 @@ from infrahub.core.protocols import CoreArtifactDefinition from infrahub.database import InfrahubDatabase # noqa: TCH001 from infrahub.exceptions import NodeNotFoundError +from infrahub.git.models import RequestArtifactDefinitionGenerate from infrahub.log import get_logger -from infrahub.message_bus import messages - -if TYPE_CHECKING: - from infrahub.services import InfrahubServices +from infrahub.workflows.catalogue import REQUEST_ARTIFACT_DEFINITION_GENERATE log = get_logger() router = APIRouter(prefix="/artifact") @@ -68,9 +64,9 @@ async def generate_artifact( branch=branch_params.branch, ) - service: InfrahubServices = request.app.state.service - await service.send( - message=messages.RequestArtifactDefinitionGenerate( - artifact_definition=artifact_definition.id, branch=branch_params.branch.name, limit=payload.nodes - ) + service = request.app.state.service + model = RequestArtifactDefinitionGenerate( + artifact_definition=artifact_definition.id, branch=branch_params.branch.name, limit=payload.nodes ) + + await service.workflow.submit_workflow(workflow=REQUEST_ARTIFACT_DEFINITION_GENERATE, parameters={"model": model}) 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..20762ad638 100644 --- a/backend/infrahub/api/menu.py +++ b/backend/infrahub/api/menu.py @@ -3,231 +3,32 @@ from typing import TYPE_CHECKING 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.schema import NodeSchema +from infrahub.core.protocols import CoreMenuItem from infrahub.log import get_logger +from infrahub.menu.generator import generate_restricted_menu +from infrahub.menu.models import Menu # noqa: TCH001 if TYPE_CHECKING: - from infrahub.core.schema import MainSchemaTypes + from infrahub.auth import AccountSession + from infrahub.database import InfrahubDatabase + log = get_logger() router = APIRouter(prefix="/menu") -class InterfaceMenu(BaseModel): - 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") - children: list[InterfaceMenu] = Field(default_factory=list, description="Child objects") - kind: str = Field(default="") - - def __lt__(self, other: object) -> bool: - if not isinstance(other, InterfaceMenu): - raise NotImplementedError - return self.title < other.title - - def list_title(self) -> str: - return f"All {self.title}(s)" - - -def add_to_menu(structure: dict[str, list[InterfaceMenu]], menu_item: InterfaceMenu) -> None: - all_items = InterfaceMenu(title=menu_item.list_title(), path=menu_item.path, icon=menu_item.icon) - menu_item.path = "" - menu_item.icon = "" - for child in structure[menu_item.kind]: - menu_item.children.append(child) - if child.kind in structure: - add_to_menu(structure, child) - menu_item.children.sort() - menu_item.children.insert(0, all_items) - - -def _extract_node_icon(model: MainSchemaTypes) -> str: - if not model.icon: - return "" - return model.icon - - @router.get("") -async def get_menu(branch: Branch = Depends(get_branch_dep)) -> list[InterfaceMenu]: +async def get_menu( + db: InfrahubDatabase = Depends(get_db), + branch: Branch = Depends(get_branch_dep), + account_session: AccountSession = Depends(get_current_user), +) -> Menu: log.info("menu_request", branch=branch.name) - full_schema = registry.schema.get_full(branch=branch, duplicate=False) - objects = InterfaceMenu(title="Objects", children=[]) - - structure: dict[str, list[InterfaceMenu]] = {} - - ipam = InterfaceMenu( - title="IPAM", - children=[ - InterfaceMenu( - title="Namespaces", - path=f"/objects/{InfrahubKind.IPNAMESPACE}", - icon=_extract_node_icon(full_schema[InfrahubKind.IPNAMESPACE]), - ), - InterfaceMenu( - title="IP Prefixes", path="/ipam/prefixes", icon=_extract_node_icon(full_schema[InfrahubKind.IPPREFIX]) - ), - InterfaceMenu( - title="IP Addresses", - path="/ipam/addresses?ipam-tab=ip-details", - icon=_extract_node_icon(full_schema[InfrahubKind.IPADDRESS]), - ), - ], - ) - - has_ipam = False - - for key in full_schema.keys(): - model = full_schema[key] - - if isinstance(model, NodeSchema) and ( - InfrahubKind.IPADDRESS in model.inherit_from or InfrahubKind.IPPREFIX in model.inherit_from - ): - has_ipam = True - - if not model.include_in_menu: - continue - - menu_name = model.menu_placement or "base" - if menu_name not in structure: - structure[menu_name] = [] - - structure[menu_name].append( - InterfaceMenu(title=model.menu_title, path=f"/objects/{model.kind}", icon=model.icon or "", kind=model.kind) - ) - - for menu_item in structure["base"]: - if menu_item.kind in structure: - add_to_menu(structure, menu_item) - - objects.children.append(menu_item) - - objects.children.sort() - groups = InterfaceMenu( - title="Object Management", - children=[ - InterfaceMenu( - title="All Groups", - path=f"/objects/{InfrahubKind.GENERICGROUP}", - icon=_extract_node_icon(full_schema[InfrahubKind.GENERICGROUP]), - ), - InterfaceMenu( - title="All Profiles", - path=f"/objects/{InfrahubKind.PROFILE}", - icon=_extract_node_icon(full_schema[InfrahubKind.PROFILE]), - ), - InterfaceMenu( - title="Resource Manager", - path="/resource-manager", - icon=_extract_node_icon(full_schema[InfrahubKind.RESOURCEPOOL]), - ), - ], - ) - - unified_storage = InterfaceMenu( - title="Unified Storage", - children=[ - InterfaceMenu(title="Schema", path="/schema", icon="mdi:file-code"), - InterfaceMenu( - title="Repository", - path=f"/objects/{InfrahubKind.GENERICREPOSITORY}", - icon=_extract_node_icon(full_schema[InfrahubKind.GENERICREPOSITORY]), - ), - InterfaceMenu( - title="GraphQL Query", - path=f"/objects/{InfrahubKind.GRAPHQLQUERY}", - icon=_extract_node_icon(full_schema[InfrahubKind.GRAPHQLQUERY]), - ), - ], - ) - change_control = InterfaceMenu( - title="Change Control", - children=[ - InterfaceMenu(title="Branches", path="/branches", icon="mdi:layers-triple"), - InterfaceMenu( - title="Proposed Changes", - path="/proposed-changes", - icon=_extract_node_icon(full_schema[InfrahubKind.PROPOSEDCHANGE]), - ), - InterfaceMenu( - title="Check Definition", - path=f"/objects/{InfrahubKind.CHECKDEFINITION}", - icon=_extract_node_icon(full_schema[InfrahubKind.CHECKDEFINITION]), - ), - InterfaceMenu(title="Tasks", path="/tasks", icon="mdi:shield-check"), - ], - ) - deployment = InterfaceMenu( - title="Deployment", - children=[ - InterfaceMenu( - title="Artifact", - path=f"/objects/{InfrahubKind.ARTIFACT}", - icon=_extract_node_icon(full_schema[InfrahubKind.ARTIFACT]), - ), - InterfaceMenu( - title="Artifact Definition", - path=f"/objects/{InfrahubKind.ARTIFACTDEFINITION}", - icon=_extract_node_icon(full_schema[InfrahubKind.ARTIFACTDEFINITION]), - ), - InterfaceMenu( - title="Generator Definition", - path=f"/objects/{InfrahubKind.GENERATORDEFINITION}", - icon=_extract_node_icon(full_schema[InfrahubKind.GENERATORDEFINITION]), - ), - InterfaceMenu( - title="Generator Instance", - path=f"/objects/{InfrahubKind.GENERATORINSTANCE}", - icon=_extract_node_icon(full_schema[InfrahubKind.GENERATORINSTANCE]), - ), - InterfaceMenu( - title="Transformation", - path=f"/objects/{InfrahubKind.TRANSFORM}", - icon=_extract_node_icon(full_schema[InfrahubKind.TRANSFORM]), - ), - ], - ) - - admin = InterfaceMenu( - title="Admin", - children=[ - InterfaceMenu( - title="Accounts", - path=f"/objects/{InfrahubKind.GENERICACCOUNT}", - icon=_extract_node_icon(full_schema[InfrahubKind.GENERICACCOUNT]), - ), - InterfaceMenu( - title="Credentials", - path=f"/objects/{InfrahubKind.CREDENTIAL}", - icon=_extract_node_icon(full_schema[InfrahubKind.CREDENTIAL]), - ), - InterfaceMenu( - title="Webhooks", - children=[ - InterfaceMenu( - title="Webhook", - path=f"/objects/{InfrahubKind.STANDARDWEBHOOK}", - icon=_extract_node_icon(full_schema[InfrahubKind.STANDARDWEBHOOK]), - ), - InterfaceMenu( - title="Custom Webhook", - path=f"/objects/{InfrahubKind.CUSTOMWEBHOOK}", - icon=_extract_node_icon(full_schema[InfrahubKind.CUSTOMWEBHOOK]), - ), - ], - ), - ], - ) - - menu_items = [objects] - if has_ipam: - menu_items.append(ipam) - menu_items.extend([groups, unified_storage, change_control, deployment, admin]) - - return menu_items + menu_items = await registry.manager.query(db=db, schema=CoreMenuItem, branch=branch) + menu = await generate_restricted_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..32fbdaa83f 100644 --- a/backend/infrahub/api/schema.py +++ b/backend/infrahub/api/schema.py @@ -16,23 +16,32 @@ from infrahub.api.dependencies import get_branch_dep, get_current_user, get_db from infrahub.api.exceptions import SchemaNotValidError from infrahub.core import registry +from infrahub.core.account import GlobalPermission 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.constants import GLOBAL_BRANCH_NAME, GlobalPermissions, PermissionDecision +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.exceptions import MigrationError, PermissionDeniedError from infrahub.log import get_logger from infrahub.message_bus import Meta, messages 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.auth import AccountSession + from infrahub.core.schema.schema_branch import SchemaBranch from infrahub.services import InfrahubServices @@ -135,7 +144,6 @@ def evaluate_candidate_schemas( @router.get("") -@router.get("/", include_in_schema=False, deprecated=True) async def get_schema( branch: Branch = Depends(get_branch_dep), namespaces: Union[list[str], None] = Query(default=None) ) -> SchemaReadAPI: @@ -234,8 +242,24 @@ async def load_schema( background_tasks: BackgroundTasks, db: InfrahubDatabase = Depends(get_db), branch: Branch = Depends(get_branch_dep), - _: Any = Depends(get_current_user), + account_session: AccountSession = Depends(get_current_user), ) -> SchemaUpdate: + for permission_backend in registry.permission_backends: + if not await permission_backend.has_permission( + db=db, + account_session=account_session, + permission=GlobalPermission( + action=GlobalPermissions.MANAGE_SCHEMA.value, + decision=( + PermissionDecision.ALLOW_DEFAULT + if branch.name in (GLOBAL_BRANCH_NAME, registry.default_branch) + else PermissionDecision.ALLOW_OTHER + ).value, + ), + branch=branch, + ): + raise PermissionDeniedError("You are not allowed to manage the schema") + service: InfrahubServices = request.app.state.service log.info("schema_load_request", branch=branch.name) @@ -258,8 +282,15 @@ 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_workflow( + workflow=SCHEMA_VALIDATE_MIGRATION, + expected_return=list[str], + parameters={"message": validate_migration_data}, ) if error_messages: raise SchemaNotValidError(message=",\n".join(error_messages)) @@ -293,15 +324,20 @@ 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_workflow( + workflow=SCHEMA_APPLY_MIGRATION, + expected_return=list[str], + parameters={"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 +375,15 @@ 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_workflow( + workflow=SCHEMA_VALIDATE_MIGRATION, + expected_return=list[str], + parameters={"message": validate_migration_data}, ) if error_messages: raise SchemaNotValidError(message=",\n".join(error_messages)) diff --git a/backend/infrahub/api/storage.py b/backend/infrahub/api/storage.py index 8d7a935fec..77b903abc7 100644 --- a/backend/infrahub/api/storage.py +++ b/backend/infrahub/api/storage.py @@ -1,7 +1,7 @@ import hashlib from fastapi import APIRouter, Depends, File, Response, UploadFile -from infrahub_sdk import UUIDT +from infrahub_sdk.uuidt import UUIDT from pydantic import BaseModel from infrahub.api.dependencies import get_current_user diff --git a/backend/infrahub/api/transformation.py b/backend/infrahub/api/transformation.py index 153386df32..8b9a64bf0a 100644 --- a/backend/infrahub/api/transformation.py +++ b/backend/infrahub/api/transformation.py @@ -21,14 +21,10 @@ ) 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.transformations.models import TransformJinjaTemplateData, TransformPythonData +from infrahub.workflows.catalogue import TRANSFORM_JINJA2_RENDER, TRANSFORM_PYTHON_RENDER if TYPE_CHECKING: from infrahub.services import InfrahubServices @@ -89,8 +85,10 @@ async def transform_python( data=data, ) - response = await service.message_bus.rpc(message=message, response_class=TransformPythonDataResponse) - return JSONResponse(content=response.data.transformed_data) + response = await service.workflow.execute_workflow( + workflow=TRANSFORM_PYTHON_RENDER, parameters={"message": message} + ) + return JSONResponse(content=response) @router.get("/transform/jinja2/{transform_id}", response_class=PlainTextResponse) @@ -134,9 +132,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 +142,9 @@ 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 = await service.workflow.execute_workflow( + workflow=TRANSFORM_JINJA2_RENDER, expected_return=str, parameters={"message": message} + ) + return PlainTextResponse(content=response) diff --git a/backend/infrahub/auth.py b/backend/infrahub/auth.py index 861f98767d..a7c0e6217f 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 @@ -84,8 +85,11 @@ async def authenticate_with_password( now = datetime.now(tz=timezone.utc) refresh_expires = now + timedelta(seconds=config.SETTINGS.security.refresh_token_lifetime) + + # The read-only account role is deprecated and will only be used for anonymous access + role = "read-write" if account.role.value.value == "read-only" else account.role.value.value 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) + access_token = generate_access_token(account_id=account.id, role=role, 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) @@ -123,6 +127,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) @@ -210,6 +243,9 @@ async def validate_api_key(db: InfrahubDatabase, token: str) -> AccountSession: await validate_active_account(db=db, account_id=str(account_id)) + # The read-only account role is deprecated and will only be used for anonymous access + role = "read-write" if role == "read-only" else role + return AccountSession(account_id=account_id, role=role, auth_type=AuthType.API) 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..4ea5ff46e3 100644 --- a/backend/infrahub/cli/db.py +++ b/backend/infrahub/cli/db.py @@ -1,10 +1,14 @@ +from __future__ import annotations + 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 @@ -12,25 +16,40 @@ from infrahub import config from infrahub.core import registry +from infrahub.core.constants import InfrahubKind from infrahub.core.graph import GRAPH_VERSION from infrahub.core.graph.constraints import ConstraintManagerBase, ConstraintManagerMemgraph, ConstraintManagerNeo4j from infrahub.core.graph.index import node_indexes, rel_indexes 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.initialization import ( + create_anonymous_role, + create_default_menu, + create_default_roles, + create_super_administrator_role, + create_super_administrators_group, + first_time_initialization, + get_root_node, + initialization, + initialize_registry, +) +from infrahub.core.manager import NodeManager 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 + from infrahub.database import InfrahubDatabase app = AsyncTyper() @@ -177,6 +196,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 +207,116 @@ 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: + await create_defaults(db=db) + 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_workflow( + workflow=SCHEMA_VALIDATE_MIGRATION, + expected_return=list[str], + parameters={"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_workflow( + workflow=SCHEMA_APPLY_MIGRATION, + expected_return=list[str], + parameters={"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) + + await create_defaults(db=db) @app.command() @@ -361,3 +401,37 @@ async def index( console.print(table) await dbdriver.close() + + +async def create_defaults(db: InfrahubDatabase) -> None: + """Create and assign default objects.""" + existing_permissions = await NodeManager.query( + schema=InfrahubKind.OBJECTPERMISSION, + db=db, + limit=1, + ) + if not existing_permissions: + await setup_permissions(db=db) + + existing_menu_items = await NodeManager.query( + schema=InfrahubKind.MENUITEM, + db=db, + limit=1, + ) + if not existing_menu_items: + await create_default_menu(db=db) + + +async def setup_permissions(db: InfrahubDatabase) -> None: + existing_accounts = await NodeManager.query( + schema=InfrahubKind.ACCOUNT, + db=db, + limit=1, + ) + administrator_role = await create_super_administrator_role(db=db) + await create_super_administrators_group(db=db, role=administrator_role, admin_accounts=existing_accounts) + + await create_default_roles(db=db) + + if config.SETTINGS.main.allow_anonymous_access: + await create_anonymous_role(db=db) 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..f366596ed5 --- /dev/null +++ b/backend/infrahub/cli/tasks.py @@ -0,0 +1,52 @@ +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( + workflow=DUMMY_FLOW, parameters={"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..163e7364d0 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 @@ -9,17 +10,18 @@ from typing import TYPE_CHECKING, Any, Optional import toml -from infrahub_sdk import generate_uuid -from pydantic import AliasChoices, Field, ValidationError, model_validator +from infrahub_sdk.utils import generate_uuid +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( @@ -72,10 +119,14 @@ class MainSettings(BaseSettings): allow_anonymous_access: bool = Field( default=True, description="Indicates if the system allows anonymous read access" ) + anonymous_access_role: str = Field( + default="Anonymous User", description="Name of the role defining which permissions anonymous users have" + ) telemetry_optout: bool = Field(default=False, description="Disable anonymous usage reporting") telemetry_endpoint: str = "https://telemetry.opsmill.cloud/infrahub" - 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", ) @@ -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( @@ -238,10 +326,65 @@ class GitSettings(BaseSettings): model_config = SettingsConfigDict(env_prefix="INFRAHUB_GIT_") repositories_directory: str = "repositories" sync_interval: int = Field( - default=10, ge=0, description="Time (in seconds) between git repositories synchronizations" + default=10, + ge=0, + description="Time (in seconds) between git repositories synchronizations", + deprecated="This setting is deprecated and not currently in use.", ) +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 +411,101 @@ 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 SecurityOIDCProviderSettings(BaseModel): + """This class is meant to facilitate configuration of OIDC providers when loading configuration from a infrahub.toml file.""" + + google: Optional[SecurityOIDCGoogle] = Field(default=None) + provider1: Optional[SecurityOIDCProvider1] = Field(default=None) + provider2: Optional[SecurityOIDCProvider2] = Field(default=None) + + +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 SecurityOAuth2ProviderSettings(BaseModel): + """This class is meant to facilitate configuration of OAuth2 providers when loading configuration from a infrahub.toml file.""" + + google: Optional[SecurityOAuth2Google] = Field(default=None) + provider1: Optional[SecurityOAuth2Provider1] = Field(default=None) + provider2: Optional[SecurityOAuth2Provider2] = Field(default=None) + + class MiscellaneousSettings(BaseSettings): model_config = SettingsConfigDict(env_prefix="INFRAHUB_MISC_") print_query_details: bool = False @@ -312,6 +550,98 @@ 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") + oauth2_provider_settings: SecurityOAuth2ProviderSettings = Field(default_factory=SecurityOAuth2ProviderSettings) + oidc_providers: list[OIDCProvider] = Field(default_factory=list, description="The selected OIDC providers") + oidc_provider_settings: SecurityOIDCProviderSettings = Field(default_factory=SecurityOIDCProviderSettings) + _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: + match oauth2_provider: + case Oauth2Provider.GOOGLE: + if self.oauth2_provider_settings.google: + self._oauth2_settings[oauth2_provider.value] = self.oauth2_provider_settings.google + case Oauth2Provider.PROVIDER1: + if self.oauth2_provider_settings.provider1: + self._oauth2_settings[oauth2_provider.value] = self.oauth2_provider_settings.provider1 + case Oauth2Provider.PROVIDER2: + if self.oauth2_provider_settings.provider2: + self._oauth2_settings[oauth2_provider.value] = self.oauth2_provider_settings.provider2 + + if oauth2_provider.value not in self._oauth2_settings: + 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: + match oidc_provider: + case OIDCProvider.GOOGLE: + if self.oidc_provider_settings.google: + self._oidc_settings[oidc_provider.value] = self.oidc_provider_settings.google + case OIDCProvider.PROVIDER1: + if self.oidc_provider_settings.provider1: + self._oidc_settings[oidc_provider.value] = self.oidc_provider_settings.provider1 + case OIDCProvider.PROVIDER2: + if self.oidc_provider_settings.provider2: + self._oidc_settings[oidc_provider.value] = self.oidc_provider_settings.provider2 + + if oidc_provider.value not in self._oidc_settings: + 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 +663,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: @@ -346,7 +677,7 @@ def initialize(self, config_file: Optional[str] = None) -> None: if not config_file: config_file_name = os.environ.get("INFRAHUB_CONFIG", "infrahub.toml") config_file = os.path.abspath(config_file_name) - load(config_file) + self.settings = load(config_file) def initialize_and_exit(self, config_file: Optional[str] = None) -> None: """Initialize the settings if they have not been initialized, exit on failures.""" @@ -379,6 +710,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 +726,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 +773,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() @@ -443,7 +789,7 @@ class Settings(BaseSettings): experimental_features: ExperimentalFeaturesSettings = ExperimentalFeaturesSettings() -def load(config_file_name: str = "infrahub.toml", config_data: Optional[dict[str, Any]] = None) -> None: +def load(config_file_name: str = "infrahub.toml", config_data: Optional[dict[str, Any]] = None) -> Settings: """Load configuration. Configuration is loaded from a config file in toml format that contains the settings, @@ -451,17 +797,15 @@ def load(config_file_name: str = "infrahub.toml", config_data: Optional[dict[str """ if config_data: - SETTINGS.settings = Settings(**config_data) - return + return Settings(**config_data) if os.path.exists(config_file_name): config_string = Path(config_file_name).read_text(encoding="utf-8") config_tmp = toml.loads(config_string) SETTINGS.settings = Settings(**config_tmp) - return - SETTINGS.settings = Settings() + return Settings() def load_and_exit(config_file_name: str = "infrahub.toml", config_data: Optional[dict[str, Any]] = None) -> None: @@ -475,7 +819,7 @@ def load_and_exit(config_file_name: str = "infrahub.toml", config_data: Optional config_data (dict, optional): [description]. Defaults to None. """ try: - load(config_file_name=config_file_name, config_data=config_data) + SETTINGS.settings = load(config_file_name=config_file_name, config_data=config_data) except ValidationError as err: print(f"Configuration not valid, found {len(err.errors())} error(s)") for error in err.errors(): diff --git a/backend/infrahub/core/account.py b/backend/infrahub/core/account.py index 2402c4be83..04226bf6bf 100644 --- a/backend/infrahub/core/account.py +++ b/backend/infrahub/core/account.py @@ -1,17 +1,545 @@ from __future__ import annotations +from dataclasses import dataclass from typing import TYPE_CHECKING, Any, Optional, Union +from typing_extensions import Self + +from infrahub.core.constants import InfrahubKind, PermissionDecision 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 GlobalPermission: + action: str + decision: int + description: str = "" + id: str = "" + + def __str__(self) -> str: + decision = PermissionDecision(self.decision) + return f"global:{self.action}:{decision.name.lower()}" + + @classmethod + def from_string(cls, input: str) -> Self: + parts = input.split(":") + if len(parts) != 3 and parts[0] != "global": + raise ValueError(f"{input} is not a valid format for a Global permission") + + # FIXME there is probably a better way to convert the decision + decision = PermissionDecision.DENY + if parts[2] == "allow_default": + decision = PermissionDecision.ALLOW_DEFAULT + elif parts[2] == "allow_all": + decision = PermissionDecision.ALLOW_ALL + elif parts[2] == "allow_other": + decision = PermissionDecision.ALLOW_OTHER + return cls(action=str(parts[1]), decision=decision) + + +@dataclass +class ObjectPermission: + namespace: str + name: str + action: str + decision: int + description: str = "" + id: str = "" + + def __str__(self) -> str: + decision = PermissionDecision(self.decision) + return f"object:{self.namespace}:{self.name}:{self.action}:{decision.name.lower()}" + + +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 + CALL { + WITH account + MATCH (account)-[r1:IS_RELATED]->(:Relationship {name: "group_member"})<-[r2:IS_RELATED]-(account_group:%(account_group_node)s) + WHERE all(r IN [r1, r2] WHERE (%(branch_filter)s)) + WITH account_group, r1, r2, (r1.status = "active" AND r2.status = "active") AS is_active + ORDER BY account_group.uuid, r2.branch_level DESC, r2.from DESC, r1.branch_level DESC, r1.from DESC + WITH account_group, head(collect(is_active)) as latest_is_active + WHERE latest_is_active = TRUE + RETURN account_group + } + WITH account_group + + CALL { + WITH account_group + MATCH (account_group)-[r1:IS_RELATED]->(:Relationship {name: "role__accountgroups"})<-[r2:IS_RELATED]-(account_role:%(account_role_node)s) + WHERE all(r IN [r1, r2] WHERE (%(branch_filter)s)) + WITH account_role, r1, r2, (r1.status = "active" AND r2.status = "active") AS is_active + ORDER BY account_role.uuid, r2.branch_level DESC, r2.from DESC, r1.branch_level DESC, r1.from DESC + WITH account_role, head(collect(is_active)) as latest_is_active + WHERE latest_is_active = TRUE + RETURN account_role + } + WITH account_role + + CALL { + WITH account_role + MATCH (account_role)-[r1:IS_RELATED]->(:Relationship {name: "role__permissions"})<-[r2:IS_RELATED]-(global_permission:%(global_permission_node)s) + WHERE all(r IN [r1, r2] WHERE (%(branch_filter)s)) + WITH global_permission, r1, r2, (r1.status = "active" AND r2.status = "active") AS is_active + ORDER BY global_permission.uuid, r2.branch_level DESC, r2.from DESC, r1.branch_level DESC, r1.from DESC + WITH global_permission, head(collect(is_active)) as latest_is_active + WHERE latest_is_active = TRUE + RETURN global_permission + } + WITH global_permission + + CALL { + WITH global_permission + MATCH (global_permission)-[r1:HAS_ATTRIBUTE]->(:Attribute {name: "action"})-[r2:HAS_VALUE]->(global_permission_action:AttributeValue) + WHERE all(r IN [r1, r2] WHERE (%(branch_filter)s)) + RETURN global_permission_action, (r1.status = "active" AND r2.status = "active") AS is_active + ORDER BY r2.branch_level DESC, r2.from DESC, r1.branch_level DESC, r1.from DESC + LIMIT 1 + } + WITH global_permission, global_permission_action, is_active AS gpa_is_active + WHERE gpa_is_active = TRUE + + CALL { + WITH global_permission + MATCH (global_permission)-[r1:HAS_ATTRIBUTE]->(:Attribute {name: "decision"})-[r2:HAS_VALUE]->(global_permission_decision:AttributeValue) + WHERE all(r IN [r1, r2] WHERE (%(branch_filter)s)) + RETURN global_permission_decision, (r1.status = "active" AND r2.status = "active") AS is_active + ORDER BY r2.branch_level DESC, r2.from DESC, r1.branch_level DESC, r1.from DESC + LIMIT 1 + } + WITH global_permission, global_permission_action, global_permission_decision, is_active AS gpd_is_active + WHERE gpd_is_active = TRUE + """ % { + "branch_filter": branch_filter, + "generic_account_node": InfrahubKind.GENERICACCOUNT, + "account_group_node": InfrahubKind.ACCOUNTGROUP, + "account_role_node": InfrahubKind.ACCOUNTROLE, + "global_permission_node": InfrahubKind.GLOBALPERMISSION, + } + + self.add_to_query(query) + + self.return_labels = ["global_permission", "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"), + 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 + CALL { + WITH account + MATCH (account)-[r1:IS_RELATED]->(:Relationship {name: "group_member"})<-[r2:IS_RELATED]-(account_group:%(account_group_node)s) + WHERE all(r IN [r1, r2] WHERE (%(branch_filter)s)) + WITH account_group, r1, r2, (r1.status = "active" AND r2.status = "active") AS is_active + ORDER BY account_group.uuid, r2.branch_level DESC, r2.from DESC, r1.branch_level DESC, r1.from DESC + WITH account_group, head(collect(is_active)) as latest_is_active + WHERE latest_is_active = TRUE + RETURN account_group + } + WITH account_group + + CALL { + WITH account_group + MATCH (account_group)-[r1:IS_RELATED]->(:Relationship {name: "role__accountgroups"})<-[r2:IS_RELATED]-(account_role:%(account_role_node)s) + WHERE all(r IN [r1, r2] WHERE (%(branch_filter)s)) + WITH account_role, r1, r2, (r1.status = "active" AND r2.status = "active") AS is_active + ORDER BY account_role.uuid, r2.branch_level DESC, r2.from DESC, r1.branch_level DESC, r1.from DESC + WITH account_role, head(collect(is_active)) as latest_is_active + WHERE latest_is_active = TRUE + RETURN account_role + } + WITH account_role + + CALL { + WITH account_role + MATCH (account_role)-[r1:IS_RELATED]->(:Relationship {name: "role__permissions"})<-[r2:IS_RELATED]-(object_permission:%(object_permission_node)s) + WHERE all(r IN [r1, r2] WHERE (%(branch_filter)s)) + WITH object_permission, r1, r2, (r1.status = "active" AND r2.status = "active") AS is_active + ORDER BY object_permission.uuid, r2.branch_level DESC, r2.from DESC, r1.branch_level DESC, r1.from DESC + WITH object_permission, head(collect(is_active)) as latest_is_active + WHERE latest_is_active = TRUE + RETURN object_permission + } + WITH object_permission + + CALL { + WITH object_permission + MATCH (object_permission)-[r1:HAS_ATTRIBUTE]->(:Attribute {name: "namespace"})-[r2:HAS_VALUE]->(object_permission_namespace:AttributeValue) + WHERE all(r IN [r1, r2] WHERE (%(branch_filter)s)) + RETURN object_permission_namespace, (r1.status = "active" AND r2.status = "active") AS is_active + ORDER BY r2.branch_level DESC, r2.from DESC, r1.branch_level DESC, r1.from DESC + LIMIT 1 + } + WITH object_permission, object_permission_namespace, is_active AS opn_is_active + WHERE opn_is_active = TRUE + CALL { + WITH object_permission + MATCH (object_permission)-[r1:HAS_ATTRIBUTE]->(:Attribute {name: "name"})-[r2:HAS_VALUE]->(object_permission_name:AttributeValue) + WHERE all(r IN [r1, r2] WHERE (%(branch_filter)s)) + RETURN object_permission_name, (r1.status = "active" AND r2.status = "active") AS is_active + ORDER BY r2.branch_level DESC, r2.from DESC, r1.branch_level DESC, r1.from DESC + LIMIT 1 + } + WITH object_permission, object_permission_namespace, object_permission_name, is_active AS opn_is_active + WHERE opn_is_active = TRUE + CALL { + WITH object_permission + MATCH (object_permission)-[r1:HAS_ATTRIBUTE]->(:Attribute {name: "action"})-[r2:HAS_VALUE]->(object_permission_action:AttributeValue) + WHERE all(r IN [r1, r2] WHERE (%(branch_filter)s)) + RETURN object_permission_action, (r1.status = "active" AND r2.status = "active") AS is_active + ORDER BY r2.branch_level DESC, r2.from DESC, r1.branch_level DESC, r1.from DESC + LIMIT 1 + } + WITH object_permission, object_permission_namespace, object_permission_name, object_permission_action, is_active AS opa_is_active + WHERE opa_is_active = TRUE + CALL { + WITH object_permission + MATCH (object_permission)-[r1:HAS_ATTRIBUTE]->(:Attribute {name: "decision"})-[r2:HAS_VALUE]->(object_permission_decision:AttributeValue) + WHERE all(r IN [r1, r2] WHERE (%(branch_filter)s)) + RETURN object_permission_decision, (r1.status = "active" AND r2.status = "active") AS is_active + ORDER BY r2.branch_level DESC, r2.from DESC, r1.branch_level DESC, r1.from DESC + LIMIT 1 + } + WITH object_permission, object_permission_namespace, object_permission_name, object_permission_action, object_permission_decision, is_active AS opd_is_active + WHERE opd_is_active = TRUE + """ % { + "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_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"), + 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 AccountRoleGlobalPermissionQuery(Query): + name: str = "account_role_global_permissions" + + def __init__(self, role_id: str, **kwargs: Any): + self.role_id = role_id + super().__init__(**kwargs) + + async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: + self.params["role_id"] = self.role_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_role:%(account_role_node)s) + WHERE account_role.uuid = $role_id + CALL { + WITH account_role + MATCH (account_role)-[r:IS_PART_OF]-(root:Root) + WHERE %(branch_filter)s + RETURN account_role as account_role1, r as r1 + ORDER BY r.branch_level DESC, r.from DESC + LIMIT 1 + } + WITH account_role, r1 as r + WHERE r.status = "active" + WITH account_role + + CALL { + WITH account_role + MATCH (account_role)-[r1:IS_RELATED]->(:Relationship {name: "role__permissions"})<-[r2:IS_RELATED]-(global_permission:%(global_permission_node)s) + WHERE all(r IN [r1, r2] WHERE (%(branch_filter)s)) + WITH global_permission, r1, r2, (r1.status = "active" AND r2.status = "active") AS is_active + ORDER BY global_permission.uuid, r2.branch_level DESC, r2.from DESC, r1.branch_level DESC, r1.from DESC + WITH global_permission, head(collect(is_active)) as latest_is_active + WHERE latest_is_active = TRUE + RETURN global_permission + } + WITH global_permission + + CALL { + WITH global_permission + MATCH (global_permission)-[r1:HAS_ATTRIBUTE]->(:Attribute {name: "action"})-[r2:HAS_VALUE]->(global_permission_action:AttributeValue) + WHERE all(r IN [r1, r2] WHERE (%(branch_filter)s)) + RETURN global_permission_action, (r1.status = "active" AND r2.status = "active") AS is_active + ORDER BY r2.branch_level DESC, r2.from DESC, r1.branch_level DESC, r1.from DESC + LIMIT 1 + } + WITH global_permission, global_permission_action, is_active AS gpa_is_active + WHERE gpa_is_active = TRUE + + CALL { + WITH global_permission + MATCH (global_permission)-[r1:HAS_ATTRIBUTE]->(:Attribute {name: "decision"})-[r2:HAS_VALUE]->(global_permission_decision:AttributeValue) + WHERE all(r IN [r1, r2] WHERE (%(branch_filter)s)) + RETURN global_permission_decision, (r1.status = "active" AND r2.status = "active") AS is_active + ORDER BY r2.branch_level DESC, r2.from DESC, r1.branch_level DESC, r1.from DESC + LIMIT 1 + } + WITH global_permission, global_permission_action, global_permission_decision, is_active AS gpd_is_active + WHERE gpd_is_active = TRUE + """ % { + "branch_filter": branch_filter, + "account_role_node": InfrahubKind.ACCOUNTROLE, + "global_permission_node": InfrahubKind.GLOBALPERMISSION, + } + + self.add_to_query(query) + + self.return_labels = ["global_permission", "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"), + action=result.get("global_permission_action").get("value"), + decision=result.get("global_permission_decision").get("value"), + ) + ) + + return permissions + + +class AccountRoleObjectPermissionQuery(Query): + name: str = "account_role_object_permissions" + + def __init__(self, role_id: str, **kwargs: Any): + self.role_id = role_id + super().__init__(**kwargs) + + async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: + self.params["role_id"] = self.role_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_role:%(account_role_node)s) + WHERE account_role.uuid = $role_id + CALL { + WITH account_role + MATCH (account_role)-[r:IS_PART_OF]-(root:Root) + WHERE %(branch_filter)s + RETURN account_role as account_role1, r as r1 + ORDER BY r.branch_level DESC, r.from DESC + LIMIT 1 + } + WITH account_role, r1 as r + WHERE r.status = "active" + WITH account_role + + CALL { + WITH account_role + MATCH (account_role)-[r1:IS_RELATED]->(:Relationship {name: "role__permissions"})<-[r2:IS_RELATED]-(object_permission:%(object_permission_node)s) + WHERE all(r IN [r1, r2] WHERE (%(branch_filter)s)) + WITH object_permission, r1, r2, (r1.status = "active" AND r2.status = "active") AS is_active + ORDER BY object_permission.uuid, r2.branch_level DESC, r2.from DESC, r1.branch_level DESC, r1.from DESC + WITH object_permission, head(collect(is_active)) as latest_is_active + WHERE latest_is_active = TRUE + RETURN object_permission + } + WITH object_permission + + CALL { + WITH object_permission + MATCH (object_permission)-[r1:HAS_ATTRIBUTE]->(:Attribute {name: "namespace"})-[r2:HAS_VALUE]->(object_permission_namespace:AttributeValue) + WHERE all(r IN [r1, r2] WHERE (%(branch_filter)s)) + RETURN object_permission_namespace, (r1.status = "active" AND r2.status = "active") AS is_active + ORDER BY r2.branch_level DESC, r2.from DESC, r1.branch_level DESC, r1.from DESC + LIMIT 1 + } + WITH object_permission, object_permission_namespace, is_active AS opn_is_active + WHERE opn_is_active = TRUE + CALL { + WITH object_permission + MATCH (object_permission)-[r1:HAS_ATTRIBUTE]->(:Attribute {name: "name"})-[r2:HAS_VALUE]->(object_permission_name:AttributeValue) + WHERE all(r IN [r1, r2] WHERE (%(branch_filter)s)) + RETURN object_permission_name, (r1.status = "active" AND r2.status = "active") AS is_active + ORDER BY r2.branch_level DESC, r2.from DESC, r1.branch_level DESC, r1.from DESC + LIMIT 1 + } + WITH object_permission, object_permission_namespace, object_permission_name, is_active AS opn_is_active + WHERE opn_is_active = TRUE + CALL { + WITH object_permission + MATCH (object_permission)-[r1:HAS_ATTRIBUTE]->(:Attribute {name: "action"})-[r2:HAS_VALUE]->(object_permission_action:AttributeValue) + WHERE all(r IN [r1, r2] WHERE (%(branch_filter)s)) + RETURN object_permission_action, (r1.status = "active" AND r2.status = "active") AS is_active + ORDER BY r2.branch_level DESC, r2.from DESC, r1.branch_level DESC, r1.from DESC + LIMIT 1 + } + WITH object_permission, object_permission_namespace, object_permission_name, object_permission_action, is_active AS opa_is_active + WHERE opa_is_active = TRUE + CALL { + WITH object_permission + MATCH (object_permission)-[r1:HAS_ATTRIBUTE]->(:Attribute {name: "decision"})-[r2:HAS_VALUE]->(object_permission_decision:AttributeValue) + WHERE all(r IN [r1, r2] WHERE (%(branch_filter)s)) + RETURN object_permission_decision, (r1.status = "active" AND r2.status = "active") AS is_active + ORDER BY r2.branch_level DESC, r2.from DESC, r1.branch_level DESC, r1.from DESC + LIMIT 1 + } + WITH object_permission, object_permission_namespace, object_permission_name, object_permission_action, object_permission_decision, is_active AS opd_is_active + WHERE opd_is_active = TRUE + """ % { + "branch_filter": branch_filter, + "account_role_node": InfrahubKind.ACCOUNTROLE, + "object_permission_node": InfrahubKind.OBJECTPERMISSION, + } + + self.add_to_query(query) + + self.return_labels = [ + "object_permission", + "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"), + 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_role_permissions(role_id: str, db: InfrahubDatabase, branch: Branch) -> AssignedPermissions: + query1 = await AccountRoleGlobalPermissionQuery.init(db=db, branch=branch, role_id=role_id, branch_agnostic=True) + await query1.execute(db=db) + global_permissions = query1.get_permissions() + + query2 = await AccountRoleObjectPermissionQuery.init(db=db, branch=branch, role_id=role_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..004de8dadc 100644 --- a/backend/infrahub/core/attribute.py +++ b/backend/infrahub/core/attribute.py @@ -7,9 +7,9 @@ import netaddr import ujson -from infrahub_sdk import UUIDT from infrahub_sdk.timestamp import TimestampFormatError from infrahub_sdk.utils import is_valid_url +from infrahub_sdk.uuidt import UUIDT from pydantic import BaseModel, Field from infrahub import config @@ -28,6 +28,7 @@ from infrahub.exceptions import ValidationError from infrahub.helpers import hash_password +from ..types import ATTRIBUTE_TYPES, LARGE_ATTRIBUTE_TYPES from .constants.relationship_label import RELATIONSHIP_TO_NODE_LABEL, RELATIONSHIP_TO_VALUE_LABEL if TYPE_CHECKING: @@ -39,6 +40,24 @@ # pylint: disable=redefined-builtin,c-extension-no-member,too-many-lines,too-many-public-methods +# Use a more user-friendly threshold than Neo4j one (8167 bytes). +MAX_STRING_LENGTH = 4096 + + +def validate_string_length(value: Optional[str]) -> None: + """ + Validates input string length does not exceed a given threshold, as Neo4J cannot index string values larger than 8167 bytes, + see https://neo4j.com/developer/kb/index-limitations-and-workaround/. + Note `value` parameter is optional as this function could be called from an attribute class + with optional value such as StringOptional. + """ + if value is None: + return + + if 3 + len(value.encode("utf-8")) >= MAX_STRING_LENGTH: + raise ValidationError(f"Text attribute length should be less than {MAX_STRING_LENGTH} characters.") + + class AttributeCreateData(BaseModel): uuid: str name: str @@ -151,7 +170,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 @@ -215,15 +234,21 @@ def validate_content(cls, value: Any, name: str, schema: AttributeSchema) -> Non ValidationError: Content of the attribute value is not valid """ if schema.regex: - try: - is_valid = re.match(pattern=schema.regex, string=str(value)) - except re.error as exc: - raise ValidationError( - {name: f"The regex defined in the schema is not valid ({schema.regex!r})"} - ) from exc + if schema.kind == "List": + validation_values = [str(entry) for entry in value] + else: + validation_values = [str(value)] + + for validation_value in validation_values: + try: + is_valid = re.match(pattern=schema.regex, string=str(validation_value)) + except re.error as exc: + raise ValidationError( + {name: f"The regex defined in the schema is not valid ({schema.regex!r})"} + ) from exc - if not is_valid: - raise ValidationError({name: f"{value} must be conform with the regex: {schema.regex!r}"}) + if not is_valid: + raise ValidationError({name: f"{validation_value} must conform with the regex: {schema.regex!r}"}) if schema.min_length: if len(value) < schema.min_length: @@ -250,7 +275,12 @@ def to_db(self) -> dict[str, Any]: if self.value is None: data["value"] = NULL_VALUE else: - data["value"] = self.serialize_value() + serialized_value = self.serialize_value() + if isinstance(serialized_value, str) and ATTRIBUTE_TYPES[self.schema.kind] not in LARGE_ATTRIBUTE_TYPES: + # Perform validation here to avoid an extra serialization during validation step. + # Standard non-str attributes (integer, boolean) do not exceed limit size related to neo4j indexing. + validate_string_length(serialized_value) + data["value"] = serialized_value return data @@ -436,13 +466,12 @@ async def to_graphql( fields: Optional[dict] = None, related_node_ids: Optional[set] = None, filter_sensitive: bool = False, + permissions: Optional[dict] = None, ) -> dict: """Generate GraphQL Payload for this attribute.""" # pylint: disable=too-many-branches - response: dict[str, Any] = { - "id": self.id, - } + response: dict[str, Any] = {"id": self.id} if fields and isinstance(fields, dict): field_names = fields.keys() @@ -462,6 +491,10 @@ async def to_graphql( response[field_name] = self.get_kind() continue + if field_name == "permissions": + response[field_name] = {"update_value": permissions["update"]} if permissions else None + continue + if field_name in ["source", "owner"]: node_attr_getter = getattr(self, f"get_{field_name}") node_attr = await node_attr_getter(db=db) @@ -549,7 +582,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: @@ -618,7 +651,7 @@ class HashedPassword(BaseAttribute): def serialize_value(self) -> str: """Serialize the value before storing it in the database.""" - return hash_password(str(self.value)) + return hash_password(self.value) class HashedPasswordOptional(HashedPassword): @@ -838,9 +871,9 @@ def validate_format(cls, value: Any, name: str, schema: AttributeSchema) -> None def serialize_value(self) -> str: """Serialize the value before storing it in the database.""" - return ipaddress.ip_network(str(self.value)).with_prefixlen + return ipaddress.ip_network(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 @@ -964,9 +997,9 @@ def validate_format(cls, value: Any, name: str, schema: AttributeSchema) -> None def serialize_value(self) -> str: """Serialize the value before storing it in the database.""" - return ipaddress.ip_interface(str(self.value)).with_prefixlen + return ipaddress.ip_interface(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 @@ -1085,7 +1118,7 @@ def validate_format(cls, value: Any, name: str, schema: AttributeSchema) -> None def serialize_value(self) -> str: """Serialize the value as standard EUI-48 or EUI-64 before storing it in the database.""" - return str(netaddr.EUI(addr=str(self.value))) + return str(netaddr.EUI(addr=self.value)) class MacAddressOptional(MacAddress): diff --git a/backend/infrahub/core/branch/__init__.py b/backend/infrahub/core/branch/__init__.py new file mode 100644 index 0000000000..558eee49fb --- /dev/null +++ b/backend/infrahub/core/branch/__init__.py @@ -0,0 +1,3 @@ +from .models import Branch + +__all__ = ["Branch"] diff --git a/backend/infrahub/core/branch.py b/backend/infrahub/core/branch/models.py similarity index 99% rename from backend/infrahub/core/branch.py rename to backend/infrahub/core/branch/models.py index 47eaf43f9c..7eb55320ec 100644 --- a/backend/infrahub/core/branch.py +++ b/backend/infrahub/core/branch/models.py @@ -119,13 +119,13 @@ def has_schema_changes(self) -> bool: return False - def update_schema_hash(self, at: Optional[Union[Timestamp, str]] = None) -> bool: + def update_schema_hash(self, at: Timestamp | str | None = None) -> bool: latest_schema = registry.schema.get_schema_branch(name=self.name) - self.schema_changed_at = Timestamp(at).to_string() new_hash = latest_schema.get_hash_full() if self.schema_hash and new_hash.main == self.schema_hash.main: return False + self.schema_changed_at = Timestamp(at).to_string() self.schema_hash = new_hash return True diff --git a/backend/infrahub/core/branch/tasks.py b/backend/infrahub/core/branch/tasks.py new file mode 100644 index 0000000000..cc89305f96 --- /dev/null +++ b/backend/infrahub/core/branch/tasks.py @@ -0,0 +1,201 @@ +from __future__ import annotations + +from prefect import flow, get_run_logger + +from infrahub import lock +from infrahub.core import registry +from infrahub.core.branch import Branch +from infrahub.core.diff.coordinator import DiffCoordinator +from infrahub.core.diff.ipam_diff_parser import IpamDiffParser +from infrahub.core.diff.merger.merger import DiffMerger +from infrahub.core.diff.repository.repository import DiffRepository +from infrahub.core.merge import BranchMerger +from infrahub.core.migrations.schema.models import SchemaApplyMigrationData +from infrahub.core.migrations.schema.tasks import schema_apply_migrations +from infrahub.core.validators.determiner import ConstraintValidatorDeterminer +from infrahub.core.validators.models.validate_migration import SchemaValidateMigrationData +from infrahub.core.validators.tasks import schema_validate_migrations +from infrahub.dependencies.registry import get_component_registry +from infrahub.exceptions import ValidationError +from infrahub.log import get_log_data +from infrahub.message_bus import Meta, messages +from infrahub.services import services +from infrahub.worker import WORKER_IDENTITY +from infrahub.workflows.catalogue import IPAM_RECONCILIATION +from infrahub.workflows.utils import add_branch_tag + + +@flow(name="branch-rebase") +async def rebase_branch(branch: str) -> None: + service = services.service + log = get_run_logger() + await add_branch_tag(branch_name=branch) + + obj = await Branch.get_by_name(db=service.database, name=branch) + base_branch = await Branch.get_by_name(db=service.database, name=registry.default_branch) + component_registry = get_component_registry() + diff_coordinator = await component_registry.get_component(DiffCoordinator, db=service.database, branch=obj) + diff_merger = await component_registry.get_component(DiffMerger, db=service.database, branch=obj) + merger = BranchMerger( + db=service.database, + diff_coordinator=diff_coordinator, + diff_merger=diff_merger, + source_branch=obj, + service=service, + ) + diff_repository = await component_registry.get_component(DiffRepository, db=service.database, branch=obj) + enriched_diff = await diff_coordinator.update_branch_diff(base_branch=base_branch, diff_branch=obj) + if enriched_diff.get_all_conflicts(): + raise ValidationError( + f"Branch {obj.name} contains conflicts with the default branch that must be addressed." + " Please review the diff for details and manually update the conflicts before rebasing." + ) + node_diff_field_summaries = await diff_repository.get_node_field_summaries( + diff_branch_name=enriched_diff.diff_branch_name, diff_id=enriched_diff.uuid + ) + + candidate_schema = merger.get_candidate_schema() + determiner = ConstraintValidatorDeterminer(schema_branch=candidate_schema) + constraints = await determiner.get_constraints(node_diffs=node_diff_field_summaries) + + # If there are some changes related to the schema between this branch and main, we need to + # - Run all the validations to ensure everything is correct before rebasing the branch + # - Run all the migrations after the rebase + if obj.has_schema_changes: + constraints += await merger.calculate_validations(target_schema=candidate_schema) + if constraints: + error_messages = await schema_validate_migrations( + message=SchemaValidateMigrationData(branch=obj, schema_branch=candidate_schema, constraints=constraints) + ) + if error_messages: + raise ValidationError(",\n".join(error_messages)) + + schema_in_main_before = merger.destination_schema.duplicate() + + async with lock.registry.global_graph_lock(): + async with service.database.start_transaction() as dbt: + await obj.rebase(db=dbt) + log.info("Branch successfully rebased") + + if obj.has_schema_changes: + # NOTE there is a bit additional work in order to calculate a proper diff that will + # allow us to pull only the part of the schema that has changed, for now the safest option is to pull + # Everything + # schema_diff = await merger.has_schema_changes() + # TODO Would be good to convert this part to a Prefect Task in order to track it properly + updated_schema = await registry.schema.load_schema_from_db( + db=service.database, + branch=obj, + # schema=merger.source_schema.duplicate(), + # schema_diff=schema_diff, + ) + registry.schema.set_schema_branch(name=obj.name, schema=updated_schema) + obj.update_schema_hash() + await obj.save(db=service.database) + + # Execute the migrations + migrations = await merger.calculate_migrations(target_schema=updated_schema) + + errors = await schema_apply_migrations( + message=SchemaApplyMigrationData( + branch=merger.source_branch, + new_schema=candidate_schema, + previous_schema=schema_in_main_before, + migrations=migrations, + ) + ) + for error in errors: + log.error(error) + + # ------------------------------------------------------------- + # Trigger the reconciliation of IPAM data after the rebase + # ------------------------------------------------------------- + differ = await merger.get_graph_diff() + diff_parser = IpamDiffParser( + db=service.database, + differ=differ, + source_branch_name=obj.name, + target_branch_name=registry.default_branch, + ) + ipam_node_details = await diff_parser.get_changed_ipam_node_details() + await service.workflow.submit_workflow( + workflow=IPAM_RECONCILIATION, parameters={"branch": obj.name, "ipam_node_details": ipam_node_details} + ) + + # ------------------------------------------------------------- + # Generate an event to indicate that a branch has been rebased + # NOTE: we still need to convert this event and potentially pull + # some tasks currently executed based on the event into this workflow + # ------------------------------------------------------------- + log_data = get_log_data() + request_id = log_data.get("request_id", "") + message = messages.EventBranchRebased( + branch=obj.name, + meta=Meta(initiator_id=WORKER_IDENTITY, request_id=request_id), + ) + await service.send(message=message) + + +@flow(name="branch-merge") +async def merge_branch(branch: str, conflict_resolution: dict[str, bool] | None = None) -> None: + service = services.service + log = get_run_logger() + + await add_branch_tag(branch_name=branch) + await add_branch_tag(branch_name=registry.default_branch) + + obj = await Branch.get_by_name(db=service.database, name=branch) + component_registry = get_component_registry() + + merger: BranchMerger | None = None + async with lock.registry.global_graph_lock(): + async with service.database.start_transaction() as db: + diff_coordinator = await component_registry.get_component(DiffCoordinator, db=db, branch=obj) + diff_merger = await component_registry.get_component(DiffMerger, db=db, branch=obj) + merger = BranchMerger( + db=db, diff_coordinator=diff_coordinator, diff_merger=diff_merger, source_branch=obj, service=service + ) + await merger.merge(conflict_resolution=conflict_resolution) + await merger.update_schema() + + if merger and merger.migrations: + errors = await schema_apply_migrations( + message=SchemaApplyMigrationData( + branch=merger.destination_branch, + new_schema=merger.destination_schema, + previous_schema=merger.initial_source_schema, + migrations=merger.migrations, + ) + ) + for error in errors: + log.error(error) + + # ------------------------------------------------------------- + # Trigger the reconciliation of IPAM data after the merge + # ------------------------------------------------------------- + differ = await merger.get_graph_diff() + diff_parser = IpamDiffParser( + db=service.database, + differ=differ, + source_branch_name=obj.name, + target_branch_name=registry.default_branch, + ) + ipam_node_details = await diff_parser.get_changed_ipam_node_details() + await service.workflow.submit_workflow( + workflow=IPAM_RECONCILIATION, + parameters={"branch": registry.default_branch, "ipam_node_details": ipam_node_details}, + ) + + # ------------------------------------------------------------- + # Generate an event to indicate that a branch has been merged + # NOTE: we still need to convert this event and potentially pull + # some tasks currently executed based on the event into this workflow + # ------------------------------------------------------------- + log_data = get_log_data() + request_id = log_data.get("request_id", "") + message = messages.EventBranchMerge( + source_branch=obj.name, + target_branch=registry.default_branch, + meta=Meta(initiator_id=WORKER_IDENTITY, request_id=request_id), + ) + await service.send(message=message) diff --git a/backend/infrahub/core/constants/__init__.py b/backend/infrahub/core/constants/__init__.py index 6bb1f4195c..b258cfb8a5 100644 --- a/backend/infrahub/core/constants/__init__.py +++ b/backend/infrahub/core/constants/__init__.py @@ -4,17 +4,17 @@ from infrahub.core.constants import infrahubkind as InfrahubKind from infrahub.exceptions import ValidationError -from infrahub.utils import InfrahubStringEnum +from infrahub.utils import InfrahubNumberEnum, InfrahubStringEnum from .schema import FlagProperty, NodeProperty, SchemaElementPathType, UpdateSupport, UpdateValidationErrorType __all__ = [ - "InfrahubKind", "FlagProperty", + "InfrahubKind", "NodeProperty", + "SchemaElementPathType", "UpdateSupport", "UpdateValidationErrorType", - "SchemaElementPathType", ] @@ -50,6 +50,32 @@ 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_SCHEMA = "manage_schema" + MANAGE_ACCOUNTS = "manage_accounts" + MANAGE_PERMISSIONS = "manage_permissions" + MANAGE_REPOSITORIES = "manage_repositories" + + +class PermissionAction(InfrahubStringEnum): + ANY = "any" + CREATE = "create" + UPDATE = "update" + DELETE = "delete" + VIEW = "view" + + +class PermissionDecision(InfrahubNumberEnum): + DENY = 1 + ALLOW_DEFAULT = 2 + ALLOW_OTHER = 4 + ALLOW_ALL = 6 + + class AccountRole(InfrahubStringEnum): ADMIN = "admin" READ_ONLY = "read-only" @@ -175,16 +201,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..ad660b3fb2 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,14 @@ IPADDRESSPOOL = "CoreIPAddressPool" IPPREFIX = "BuiltinIPPrefix" IPPREFIXPOOL = "CoreIPPrefixPool" +MENU = "CoreMenu" +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..670423ed00 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, @@ -747,7 +747,8 @@ async def _calculated_diff_rels(self) -> None: dst_node_id = result.get("dn").get("uuid") from_time = Timestamp(result.get("r1").get("from")) - # to_time = result.get("r1").get("to", None) + to_time_raw = result.get("r1").get("to", None) + to_time = Timestamp(to_time_raw) if to_time_raw else None item = { "branch": branch_name, @@ -779,14 +780,21 @@ async def _calculated_diff_rels(self) -> None: item["paths"] = relationship_paths.paths item["conflict_paths"] = relationship_paths.conflict_paths - # FIXME Need to revisit changed_at, mostlikely not accurate. More of a placeholder at this point - if branch_status == RelationshipStatus.ACTIVE.value: - item["action"] = DiffAction.ADDED - item["changed_at"] = from_time - elif branch_status == RelationshipStatus.DELETED.value: + if branch_status == RelationshipStatus.DELETED.value: item["action"] = DiffAction.REMOVED item["changed_at"] = from_time rel_ids_to_query.append(rel_id) + elif ( + branch_status == RelationshipStatus.ACTIVE.value + and to_time + and from_time < self.diff_from <= to_time <= self.diff_to + ): + item["action"] = DiffAction.REMOVED + item["changed_at"] = to_time + rel_ids_to_query.append(rel_id) + elif branch_status == RelationshipStatus.ACTIVE.value: + item["action"] = DiffAction.ADDED + item["changed_at"] = from_time else: raise ValueError(f"Unexpected value for branch_status: {branch_status}") @@ -804,7 +812,6 @@ async def _calculated_diff_rels(self) -> None: for result in query_props.get_results(): branch_name = result.get("r3").get("branch") - branch_status = result.get("r3").get("status") rel_name = result.get("rel").get("name") rel_id = result.get("rel").get("uuid") @@ -871,6 +878,8 @@ async def _calculated_diff_rels(self) -> None: prop_type = result.get_rel("r3").type prop_from = Timestamp(result.get("r3").get("from")) + prop_to_raw = result.get("r3").get("to", None) + prop_to = Timestamp(prop_to_raw) if prop_to_raw else None origin_prop = origin_rel_properties_query.get_results_by_id_and_prop_type( rel_id=rel_id, prop_type=prop_type @@ -892,6 +901,9 @@ async def _calculated_diff_rels(self) -> None: if not origin_prop and prop_from >= self.diff_from and branch_status == RelationshipStatus.ACTIVE.value: prop["action"] = DiffAction.ADDED prop["changed_at"] = prop_from + elif prop_to and prop_to >= self.diff_from and branch_status == RelationshipStatus.ACTIVE.value: + prop["action"] = DiffAction.REMOVED + prop["changed_at"] = prop_to elif prop_from >= self.diff_from and branch_status == RelationshipStatus.DELETED.value: prop["action"] = DiffAction.REMOVED prop["changed_at"] = prop_from 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..276c777401 100644 --- a/backend/infrahub/core/diff/coordinator.py +++ b/backend/infrahub/core/diff/coordinator.py @@ -86,7 +86,7 @@ async def run_update( if from_time: from_timestamp = Timestamp(from_time) else: - from_timestamp = Timestamp(diff_branch.get_created_at()) + from_timestamp = Timestamp(diff_branch.get_branched_from()) if to_time: to_timestamp = Timestamp(to_time) else: @@ -123,7 +123,7 @@ async def update_branch_diff(self, base_branch: Branch, diff_branch: Branch) -> general_lock_name = self._get_lock_name( base_branch_name=base_branch.name, diff_branch_name=diff_branch.name, is_incremental=False ) - from_time = Timestamp(diff_branch.get_created_at()) + from_time = Timestamp(diff_branch.get_branched_from()) to_time = Timestamp() tracking_id = BranchTrackingId(name=diff_branch.name) async with ( @@ -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/backend/infrahub/core/diff/data_check_synchronizer.py b/backend/infrahub/core/diff/data_check_synchronizer.py index 492919db75..60975dd6a7 100644 --- a/backend/infrahub/core/diff/data_check_synchronizer.py +++ b/backend/infrahub/core/diff/data_check_synchronizer.py @@ -5,6 +5,7 @@ from infrahub.core.manager import NodeManager from infrahub.core.node import Node from infrahub.database import InfrahubDatabase +from infrahub.exceptions import SchemaNotFoundError from .conflicts_extractor import DiffConflictsExtractor from .model.path import ConflictSelection, EnrichedDiffConflict, EnrichedDiffRoot @@ -22,14 +23,18 @@ def __init__( self.conflict_recorder = conflict_recorder async def synchronize(self, enriched_diff: EnrichedDiffRoot) -> list[Node]: - proposed_changes = await NodeManager.query( - db=self.db, - schema=InfrahubKind.PROPOSEDCHANGE, - filters={"source_branch": enriched_diff.diff_branch_name, "state": ProposedChangeState.OPEN}, - ) + try: + proposed_changes = await NodeManager.query( + db=self.db, + schema=InfrahubKind.PROPOSEDCHANGE, + filters={"source_branch": enriched_diff.diff_branch_name, "state": ProposedChangeState.OPEN}, + ) + except SchemaNotFoundError: + # if the CoreProposedChange schema does not exist, then there's nothing to do + proposed_changes = [] if not proposed_changes: return [] - enriched_conflicts = enriched_diff.get_all_conflicts() + enriched_conflicts_map = enriched_diff.get_all_conflicts() data_conflicts = await self.conflicts_extractor.get_data_conflicts(enriched_diff_root=enriched_diff) all_data_checks = [] for pc in proposed_changes: @@ -38,7 +43,7 @@ async def synchronize(self, enriched_diff: EnrichedDiffRoot) -> list[Node]: ) all_data_checks.extend(core_data_checks) core_data_checks_by_id = {cdc.enriched_conflict_id.value: cdc for cdc in core_data_checks} # type: ignore[attr-defined] - enriched_conflicts_by_id = {ec.uuid: ec for ec in enriched_conflicts} + enriched_conflicts_by_id = {ec.uuid: ec for ec in enriched_conflicts_map.values()} for conflict_id, core_data_check in core_data_checks_by_id.items(): enriched_conflict = enriched_conflicts_by_id.get(conflict_id) if not enriched_conflict: diff --git a/backend/infrahub/test_data/__init__.py b/backend/infrahub/core/diff/merger/__init__.py similarity index 100% rename from backend/infrahub/test_data/__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..26afc14ed6 --- /dev/null +++ b/backend/infrahub/core/diff/merger/merger.py @@ -0,0 +1,67 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from infrahub.core import registry +from infrahub.core.diff.model.path import BranchTrackingId +from infrahub.core.diff.query.merge import DiffMergePropertiesQuery, 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_diffs = await self.diff_repository.get_empty_roots( + diff_branch_names=[self.source_branch.name], base_branch_names=[self.destination_branch.name] + ) + latest_diff = None + tracking_id = BranchTrackingId(name=self.source_branch.name) + for diff in enriched_diffs: + if latest_diff is None or (diff.tracking_id == tracking_id and diff.to_time > latest_diff.to_time): + latest_diff = diff + if latest_diff is None: + raise RuntimeError(f"Missing diff for branch {self.source_branch.name}") + enriched_diff = await self.diff_repository.get_one( + diff_branch_name=self.source_branch.name, diff_id=latest_diff.uuid + ) + async for node_diff_dicts, property_diff_dicts in self.serializer.serialize_diff(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) + merge_properties_query = await DiffMergePropertiesQuery.init( + db=self.db, + branch=self.source_branch, + at=at, + target_branch=self.destination_branch, + property_diff_dicts=property_diff_dicts, + ) + await merge_properties_query.execute(db=self.db) + + self.source_branch.branched_from = at.to_string() + await self.source_branch.save(db=self.db) + registry.branch[self.source_branch.name] = self.source_branch diff --git a/backend/infrahub/core/diff/merger/model.py b/backend/infrahub/core/diff/merger/model.py new file mode 100644 index 0000000000..8ebda88e95 --- /dev/null +++ b/backend/infrahub/core/diff/merger/model.py @@ -0,0 +1,38 @@ +from typing import TypedDict + + +class RelationshipMergeDict(TypedDict): + peer_id: str + name: str + action: str + + +class AttributeMergeDict(TypedDict): + name: str + action: str + + +class NodeMergeDict(TypedDict): + uuid: str + action: str + attributes: list[AttributeMergeDict] + relationships: list[RelationshipMergeDict] + + +class PropertyMergeDict(TypedDict): + property_type: str + action: str + value: str | bool | int | float | None + + +class AttributePropertyMergeDict(TypedDict): + node_uuid: str + attribute_name: str + properties: list[PropertyMergeDict] + + +class RelationshipPropertyMergeDict(TypedDict): + node_uuid: str + relationship_id: str + peer_uuid: str + properties: list[PropertyMergeDict] diff --git a/backend/infrahub/core/diff/merger/serializer.py b/backend/infrahub/core/diff/merger/serializer.py new file mode 100644 index 0000000000..e199010d7f --- /dev/null +++ b/backend/infrahub/core/diff/merger/serializer.py @@ -0,0 +1,356 @@ +from typing import AsyncGenerator + +from infrahub.core.constants import DiffAction, RelationshipCardinality +from infrahub.core.constants.database import DatabaseEdgeType +from infrahub.core.schema import MainSchemaTypes +from infrahub.database import InfrahubDatabase +from infrahub.exceptions import SchemaNotFoundError +from infrahub.types import ATTRIBUTE_PYTHON_TYPES + +from ..model.path import ( + ConflictSelection, + EnrichedDiffAttribute, + EnrichedDiffConflict, + EnrichedDiffProperty, + EnrichedDiffRoot, + EnrichedDiffSingleRelationship, +) +from .model import ( + AttributeMergeDict, + AttributePropertyMergeDict, + NodeMergeDict, + PropertyMergeDict, + RelationshipMergeDict, + RelationshipPropertyMergeDict, +) + +Primitives = str | bool | int | float + + +class DiffMergeSerializer: + def __init__(self, db: InfrahubDatabase, max_batch_size: int) -> None: + self.db = db + self.max_batch_size = max_batch_size + self._relationship_id_cache: dict[tuple[str, str], str] = {} + self._attribute_type_cache: dict[tuple[str, str], type] = {} + self._source_branch_name: str | None = None + self._target_branch_name: str | None = None + # {(node_id, relationship_id, peer_id)} + self._conflicted_cardinality_one_relationships: set[tuple[str, str, str]] = set() + + def _reset_caches(self) -> None: + self._relationship_id_cache = {} + self._attribute_type_cache = {} + + @property + def source_branch_name(self) -> str: + if self._source_branch_name is None: + raise RuntimeError("source_branch_name not set") + return self._source_branch_name + + @property + def target_branch_name(self) -> str: + if self._target_branch_name is None: + raise RuntimeError("target_branch_name not set") + return self._target_branch_name + + def _get_schema(self, kind: str, branch_name: str) -> MainSchemaTypes: + schema_branch = self.db.schema.get_schema_branch(name=branch_name) + return schema_branch.get(name=kind, duplicate=False) + + 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") + + def _to_action_str(self, action: DiffAction) -> str: + return str(action.value).upper() + + def _get_relationship_identifier(self, schema_kind: str, relationship_name: str) -> str: + cache_key = (schema_kind, relationship_name) + if cache_key in self._relationship_id_cache: + return self._relationship_id_cache[cache_key] + try: + node_schema = self._get_schema(kind=schema_kind, branch_name=self.source_branch_name) + relationship_schema = node_schema.get_relationship(name=relationship_name) + except (SchemaNotFoundError, ValueError): + node_schema = self._get_schema(kind=schema_kind, branch_name=self.target_branch_name) + relationship_schema = node_schema.get_relationship(name=relationship_name) + relationship_identifier = relationship_schema.get_identifier() + self._relationship_id_cache[cache_key] = relationship_identifier + return relationship_identifier + + def _get_property_type_for_attribute_value(self, schema_kind: str, attribute_name: str) -> type: + cache_key = (schema_kind, attribute_name) + if cache_key in self._attribute_type_cache: + return self._attribute_type_cache[cache_key] + try: + node_schema = self._get_schema(kind=schema_kind, branch_name=self.source_branch_name) + attribute_schema = node_schema.get_attribute(name=attribute_name) + except (SchemaNotFoundError, ValueError): + node_schema = self._get_schema(kind=schema_kind, branch_name=self.target_branch_name) + attribute_schema = node_schema.get_attribute(name=attribute_name) + python_type = ATTRIBUTE_PYTHON_TYPES[attribute_schema.kind] + final_python_type: type = str + if python_type in (str, int, float, bool): + final_python_type = python_type + self._attribute_type_cache[cache_key] = final_python_type + return final_python_type + + def _convert_property_value( + self, property_type: DatabaseEdgeType, raw_value: str | None, value_type: type | None = None + ) -> Primitives | None: + # peer IDs are strings + if property_type in (DatabaseEdgeType.HAS_OWNER, DatabaseEdgeType.HAS_SOURCE, DatabaseEdgeType.IS_RELATED): + return raw_value + # these are boolean + if (property_type in (DatabaseEdgeType.IS_VISIBLE, DatabaseEdgeType.IS_PROTECTED)) and isinstance( + raw_value, str + ): + return raw_value.lower() == "true" + # this must be HAS_VALUE + if raw_value in (None, "NULL"): + return "NULL" + if value_type: + if value_type is bool and isinstance(raw_value, str): + return raw_value.lower() == "true" + return value_type(raw_value) + return raw_value + + def _cache_conflicted_cardinality_one_relationships(self, diff: EnrichedDiffRoot) -> None: + for node in diff.nodes: + for rel in node.relationships: + if rel.cardinality is not RelationshipCardinality.ONE: + continue + for element in rel.relationships: + if element.conflict is None: + continue + for prop in element.properties: + if prop.property_type is not DatabaseEdgeType.IS_RELATED: + continue + relationship_identifier = self._get_relationship_identifier( + schema_kind=node.kind, relationship_name=rel.name + ) + if prop.previous_value: + self._conflicted_cardinality_one_relationships.add( + (node.uuid, relationship_identifier, prop.previous_value) + ) + if prop.new_value: + self._conflicted_cardinality_one_relationships.add( + (node.uuid, relationship_identifier, prop.new_value) + ) + + async def serialize_diff( + self, diff: EnrichedDiffRoot + ) -> AsyncGenerator[ + tuple[list[NodeMergeDict], list[AttributePropertyMergeDict | RelationshipPropertyMergeDict]], None + ]: + self._reset_caches() + self._source_branch_name = diff.diff_branch_name + self._target_branch_name = diff.base_branch_name + self._cache_conflicted_cardinality_one_relationships(diff=diff) + serialized_node_diffs = [] + serialized_property_diffs: list[AttributePropertyMergeDict | RelationshipPropertyMergeDict] = [] + for node in diff.nodes: + node_action = self._get_action(action=node.action, conflict=node.conflict) + serial_attr_diffs = [] + for attr_diff in node.attributes: + serial_attr_diff, attribute_property_diff = self._serialize_attribute( + attribute_diff=attr_diff, node_uuid=node.uuid, node_kind=node.kind + ) + if serial_attr_diff: + serial_attr_diffs.append(serial_attr_diff) + serialized_property_diffs.append(attribute_property_diff) + relationship_diffs = [] + for rel_diff in node.relationships: + relationship_identifier = self._get_relationship_identifier( + schema_kind=node.kind, relationship_name=rel_diff.name + ) + for relationship_element_diff in rel_diff.relationships: + element_diffs, relationship_property_diffs = self._serialize_relationship_element( + relationship_diff=relationship_element_diff, + relationship_identifier=relationship_identifier, + node_uuid=node.uuid, + ) + relationship_diffs.extend(element_diffs) + serialized_property_diffs.extend(relationship_property_diffs) + if node_action in (DiffAction.ADDED, DiffAction.REMOVED) or serial_attr_diffs or relationship_diffs: + serialized_node_diffs.append( + NodeMergeDict( + uuid=node.uuid, + action=self._to_action_str(action=node_action), + attributes=serial_attr_diffs, + relationships=relationship_diffs, + ) + ) + if len(serialized_node_diffs) == self.max_batch_size: + yield (serialized_node_diffs, serialized_property_diffs) + serialized_node_diffs, serialized_property_diffs = [], [] + yield (serialized_node_diffs, serialized_property_diffs) + + def _get_property_actions_and_values( + self, property_diff: EnrichedDiffProperty, python_value_type: type + ) -> list[tuple[DiffAction, Primitives]]: + action = property_diff.action + new_value = property_diff.new_value + if property_diff.conflict and property_diff.conflict.selected_branch is ConflictSelection.BASE_BRANCH: + action = property_diff.conflict.base_branch_action + if property_diff.conflict.base_branch_value: + new_value = property_diff.conflict.base_branch_value + actions = [action] + if property_diff.action is DiffAction.UPDATED: + actions = [DiffAction.ADDED, DiffAction.REMOVED] + actions_and_values: list[tuple[DiffAction, Primitives]] = [] + for action in actions: + if action not in (DiffAction.ADDED, DiffAction.REMOVED): + continue + if action is DiffAction.ADDED: + raw_value = new_value + else: + raw_value = property_diff.previous_value + final_value = self._convert_property_value( + property_type=property_diff.property_type, raw_value=raw_value, value_type=python_value_type + ) + if final_value is not None: + actions_and_values.append((action, final_value)) + return actions_and_values + + def _serialize_attribute( + self, attribute_diff: EnrichedDiffAttribute, node_uuid: str, node_kind: str + ) -> tuple[AttributeMergeDict | None, AttributePropertyMergeDict]: + prop_dicts: list[PropertyMergeDict] = [] + python_type = self._get_property_type_for_attribute_value( + schema_kind=node_kind, attribute_name=attribute_diff.name + ) + for property_diff in attribute_diff.properties: + actions_and_values = self._get_property_actions_and_values( + property_diff=property_diff, python_value_type=python_type + ) + for action, value in actions_and_values: + # we only delete attributes when the whole attribute is deleted + if action is DiffAction.REMOVED and attribute_diff.action is not DiffAction.REMOVED: + continue + prop_dicts.append( + PropertyMergeDict( + property_type=property_diff.property_type.value, + action=self._to_action_str(action=action), + value=value, + ) + ) + attr_dict = None + if attribute_diff.action in (DiffAction.ADDED, DiffAction.REMOVED): + attr_dict = AttributeMergeDict( + name=attribute_diff.name, + action=self._to_action_str(action=attribute_diff.action), + ) + attr_prop_dict = AttributePropertyMergeDict( + node_uuid=node_uuid, attribute_name=attribute_diff.name, properties=prop_dicts + ) + return attr_dict, attr_prop_dict + + def _get_default_property_merge_dicts(self, action: DiffAction) -> dict[DatabaseEdgeType, PropertyMergeDict]: + # start with default values for IS_VISIBLE and IS_PROTECTED b/c we always want to update them during a merge + return { + DatabaseEdgeType.IS_VISIBLE: PropertyMergeDict( + property_type=DatabaseEdgeType.IS_VISIBLE.value, + action=self._to_action_str(action), + value=None, + ), + DatabaseEdgeType.IS_PROTECTED: PropertyMergeDict( + property_type=DatabaseEdgeType.IS_PROTECTED.value, + action=self._to_action_str(action), + value=None, + ), + } + + def _get_actions_and_peers(self, relationship_diff: EnrichedDiffSingleRelationship) -> list[tuple[DiffAction, str]]: + is_related_prop = [p for p in relationship_diff.properties if p.property_type is DatabaseEdgeType.IS_RELATED][0] + actions_and_values = self._get_property_actions_and_values(property_diff=is_related_prop, python_value_type=str) + actions_and_peers: list[tuple[DiffAction, str]] = [] + for action, peer_id in actions_and_values: + if action is DiffAction.ADDED: + actions_and_peers.append((DiffAction.ADDED, str(peer_id))) + elif action is DiffAction.REMOVED: + actions_and_peers.append((DiffAction.REMOVED, str(peer_id))) + + conflict = relationship_diff.conflict + if ( + conflict + and conflict.selected_branch + and conflict.selected_branch is ConflictSelection.DIFF_BRANCH + and conflict.base_branch_value + and conflict.base_branch_action in (DiffAction.ADDED, DiffAction.UPDATED) + ): + actions_and_peers.append((DiffAction.REMOVED, conflict.base_branch_value)) + return actions_and_peers + + def _serialize_relationship_element( + self, relationship_diff: EnrichedDiffSingleRelationship, relationship_identifier: str, node_uuid: str + ) -> tuple[list[RelationshipMergeDict], list[RelationshipPropertyMergeDict]]: + # if there is a relationship-element conflict and we are keeping the base branch version + # then we do not need to do anything special + if relationship_diff.conflict and relationship_diff.conflict.selected_branch is ConflictSelection.BASE_BRANCH: + return ([], []) + relationship_dicts = [] + added_property_dicts = self._get_default_property_merge_dicts(action=DiffAction.ADDED) + removed_property_dicts = self._get_default_property_merge_dicts(action=DiffAction.REMOVED) + other_property_dicts: dict[DatabaseEdgeType, PropertyMergeDict] = {} + actions_and_peers = self._get_actions_and_peers(relationship_diff=relationship_diff) + added_peer_ids = [peer_id for action, peer_id in actions_and_peers if action is DiffAction.ADDED] + removed_peer_ids = [peer_id for action, peer_id in actions_and_peers if action is DiffAction.REMOVED] + + for action, peer_id in actions_and_peers: + if ( + peer_id + and (peer_id, relationship_identifier, node_uuid) not in self._conflicted_cardinality_one_relationships + ): + relationship_dicts.append( + RelationshipMergeDict( + peer_id=peer_id, name=relationship_identifier, action=self._to_action_str(action=action) + ) + ) + for property_diff in relationship_diff.properties: + if property_diff.property_type is DatabaseEdgeType.IS_RELATED: + # handled above + continue + python_value_type: type = str + if property_diff.property_type in (DatabaseEdgeType.IS_VISIBLE, DatabaseEdgeType.IS_PROTECTED): + python_value_type = bool + actions_and_values = self._get_property_actions_and_values( + property_diff=property_diff, python_value_type=python_value_type + ) + for action, value in actions_and_values: + property_dict = PropertyMergeDict( + property_type=property_diff.property_type.value, + action=self._to_action_str(action=action), + value=value, + ) + if added_peer_ids and action is DiffAction.ADDED: + added_property_dicts[property_diff.property_type] = property_dict + elif removed_peer_ids and action is DiffAction.REMOVED: + removed_property_dicts[property_diff.property_type] = property_dict + else: + other_property_dicts[property_diff.property_type] = property_dict + relationship_property_dicts = [] + peers_and_property_dics = [(peer_id, added_property_dicts) for peer_id in added_peer_ids] + peers_and_property_dics += [(peer_id, removed_property_dicts) for peer_id in removed_peer_ids] + peers_and_property_dics += [(relationship_diff.peer_id, other_property_dicts)] + for peer_id, property_dicts in peers_and_property_dics: + if ( + peer_id + and property_dicts + and (peer_id, relationship_identifier, node_uuid) not in self._conflicted_cardinality_one_relationships + ): + relationship_property_dicts.append( + RelationshipPropertyMergeDict( + node_uuid=node_uuid, + relationship_id=relationship_identifier, + peer_uuid=peer_id, + properties=list(property_dicts.values()), + ) + ) + return relationship_dicts, relationship_property_dicts diff --git a/backend/infrahub/core/diff/model/path.py b/backend/infrahub/core/diff/model/path.py index 5fb902d0c4..b1fc156015 100644 --- a/backend/infrahub/core/diff/model/path.py +++ b/backend/infrahub/core/diff/model/path.py @@ -4,7 +4,13 @@ from enum import Enum from typing import TYPE_CHECKING, Any, Optional -from infrahub.core.constants import DiffAction, RelationshipCardinality, RelationshipDirection, RelationshipStatus +from infrahub.core.constants import ( + BranchSupportType, + DiffAction, + RelationshipCardinality, + RelationshipDirection, + RelationshipStatus, +) from infrahub.core.constants.database import DatabaseEdgeType from infrahub.core.timestamp import Timestamp @@ -15,7 +21,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 @@ -83,6 +89,13 @@ def __hash__(self) -> int: return hash(f"{self.node_uuid}:{self.field_name}") +@dataclass +class NodeDiffFieldSummary: + kind: str + attribute_names: set[str] = field(default_factory=set) + relationship_names: set[str] = field(default_factory=set) + + @dataclass class BaseSummary: num_added: int = field(default=0, kw_only=True) @@ -157,8 +170,8 @@ class EnrichedDiffAttribute(BaseSummary): def __hash__(self) -> int: return hash(self.name) - def get_all_conflicts(self) -> list[EnrichedDiffConflict]: - return [prop.conflict for prop in self.properties if prop.conflict] + def get_all_conflicts(self) -> dict[str, EnrichedDiffConflict]: + return {prop.path_identifier: prop.conflict for prop in self.properties if prop.conflict} @classmethod def from_calculated_attribute(cls, calculated_attribute: DiffAttribute) -> EnrichedDiffAttribute: @@ -186,11 +199,11 @@ class EnrichedDiffSingleRelationship(BaseSummary): def __hash__(self) -> int: return hash(self.peer_id) - def get_all_conflicts(self) -> list[EnrichedDiffConflict]: - all_conflicts = [] + def get_all_conflicts(self) -> dict[str, EnrichedDiffConflict]: + all_conflicts: dict[str, EnrichedDiffConflict] = {} if self.conflict: - all_conflicts.append(self.conflict) - all_conflicts.extend([prop.conflict for prop in self.properties if prop.conflict]) + all_conflicts[self.path_identifier] = self.conflict + all_conflicts.update({prop.path_identifier: prop.conflict for prop in self.properties if prop.conflict}) return all_conflicts def get_property(self, property_type: DatabaseEdgeType) -> EnrichedDiffProperty: @@ -226,10 +239,10 @@ class EnrichedDiffRelationship(BaseSummary): def __hash__(self) -> int: return hash(self.name) - def get_all_conflicts(self) -> list[EnrichedDiffConflict]: - all_conflicts = [] + def get_all_conflicts(self) -> dict[str, EnrichedDiffConflict]: + all_conflicts: dict[str, EnrichedDiffConflict] = {} for element in self.relationships: - all_conflicts.extend(element.get_all_conflicts()) + all_conflicts.update(element.get_all_conflicts()) return all_conflicts @property @@ -275,14 +288,14 @@ class EnrichedDiffNode(BaseSummary): def __hash__(self) -> int: return hash(self.uuid) - def get_all_conflicts(self) -> list[EnrichedDiffConflict]: - all_conflicts = [] + def get_all_conflicts(self) -> dict[str, EnrichedDiffConflict]: + all_conflicts: dict[str, EnrichedDiffConflict] = {} if self.conflict: - all_conflicts.append(self.conflict) + all_conflicts[self.path_identifier] = self.conflict for attribute in self.attributes: - all_conflicts.extend(attribute.get_all_conflicts()) + all_conflicts.update(attribute.get_all_conflicts()) for relationship in self.relationships: - all_conflicts.extend(relationship.get_all_conflicts()) + all_conflicts.update(relationship.get_all_conflicts()) return all_conflicts def get_parent_info(self, context: GraphqlContext | None = None) -> ParentNodeInfo | None: @@ -401,10 +414,10 @@ def has_node(self, node_uuid: str) -> bool: except ValueError: return False - def get_all_conflicts(self) -> list[EnrichedDiffConflict]: - all_conflicts = [] + def get_all_conflicts(self) -> dict[str, EnrichedDiffConflict]: + all_conflicts: dict[str, EnrichedDiffConflict] = {} for node in self.nodes: - all_conflicts.extend(node.get_all_conflicts()) + all_conflicts.update(node.get_all_conflicts()) return all_conflicts @classmethod @@ -626,6 +639,10 @@ def node_changed_at(self) -> Timestamp: def node_status(self) -> RelationshipStatus: return RelationshipStatus(self.path_to_node.get("status")) + @property + def node_branch_support(self) -> BranchSupportType: + return BranchSupportType(self.node_node.get("branch_support")) + @property def attribute_name(self) -> str: return str(self.attribute_node.get("name")) @@ -655,33 +672,37 @@ def property_id(self) -> str: return self.property_node.element_id @property - def property_changed_at(self) -> Timestamp: + def property_from_time(self) -> Timestamp: return Timestamp(self.path_to_property.get("from")) @property - def property_status(self) -> RelationshipStatus: - return RelationshipStatus(self.path_to_property.get("status")) + def property_to_time(self) -> Timestamp | None: + raw_to = self.path_to_property.get("to") + if not raw_to: + return None + return Timestamp(str(raw_to)) @property - def property_end_time(self) -> Optional[Timestamp]: - end_time_str = self.path_to_property.get("to") - if not end_time_str: - return None - return Timestamp(end_time_str) + def property_status(self) -> RelationshipStatus: + return RelationshipStatus(self.path_to_property.get("status")) @property def property_value(self) -> Any: return self.property_node.get("value") + @property + def property_is_peer(self) -> bool: + return "Node" in self.property_node.labels + @property def peer_id(self) -> Optional[str]: - if "Node" not in self.property_node.labels: + if not self.property_is_peer: return None return str(self.property_node.get("uuid")) @property def peer_kind(self) -> Optional[str]: - if "Node" not in self.property_node.labels: + if not self.property_is_peer: return None return str(self.property_node.get("kind")) diff --git a/backend/infrahub/message_bus/messages/request_diff_update.py b/backend/infrahub/core/diff/models.py similarity index 53% rename from backend/infrahub/message_bus/messages/request_diff_update.py rename to backend/infrahub/core/diff/models.py index 7310473f77..fa898455b1 100644 --- a/backend/infrahub/message_bus/messages/request_diff_update.py +++ b/backend/infrahub/core/diff/models.py @@ -1,9 +1,7 @@ -from pydantic import Field +from pydantic import BaseModel, Field -from infrahub.message_bus import InfrahubMessage - -class RequestDiffUpdate(InfrahubMessage): +class RequestDiffUpdate(BaseModel): """ Request diff to be updated. @@ -15,3 +13,10 @@ class RequestDiffUpdate(InfrahubMessage): name: str | None = None from_time: str | None = None to_time: str | None = None + + +class RequestDiffRefresh(BaseModel): + """Request diff be recalculated from scratch.""" + + branch_name: str = Field(..., description="The branch associated with the diff") + diff_id: str = Field(..., description="The id for this diff") diff --git a/backend/infrahub/core/diff/query/field_summary.py b/backend/infrahub/core/diff/query/field_summary.py new file mode 100644 index 0000000000..ec50974858 --- /dev/null +++ b/backend/infrahub/core/diff/query/field_summary.py @@ -0,0 +1,78 @@ +from typing import Any + +from infrahub.core.constants import DiffAction +from infrahub.core.query import Query, QueryType +from infrahub.database import InfrahubDatabase + +from ..model.path import NodeDiffFieldSummary, TrackingId + + +class EnrichedDiffNodeFieldSummaryQuery(Query): + """ + Get node kind and names of all altered attributes and relationships for each kind + """ + + name = "enriched_diff_node_field_summary" + type = QueryType.READ + + def __init__( + self, + diff_branch_name: str, + tracking_id: TrackingId | None = None, + diff_id: str | None = None, + **kwargs: Any, + ) -> None: + super().__init__(**kwargs) + self.diff_branch_name = diff_branch_name + self.tracking_id = tracking_id + self.diff_id = diff_id + + async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: + if self.tracking_id is None and self.diff_id is None: + raise RuntimeError("Either tacking_id or diff_id is required") + self.params = { + "unchanged_str": DiffAction.UNCHANGED.value, + "diff_branch": self.diff_branch_name, + "tracking_id": self.tracking_id.serialize() if self.tracking_id else None, + "diff_id": self.diff_id, + } + query = """ + MATCH (diff_root:DiffRoot) + WHERE diff_root.diff_branch = $diff_branch + AND (diff_root.tracking_id = $tracking_id OR $tracking_id IS NULL) + AND (diff_root.uuid = $diff_id OR $diff_id IS NULL) + OPTIONAL MATCH (diff_root)-[:DIFF_HAS_NODE]->(n:DiffNode) + WHERE n.action <> $unchanged_str + WITH DISTINCT n.kind AS kind + CALL { + WITH kind + OPTIONAL MATCH (n:DiffNode {kind: kind})-[:DIFF_HAS_ATTRIBUTE]->(a:DiffAttribute) + WHERE n.action <> $unchanged_str + AND a.action <> $unchanged_str + WITH DISTINCT a.name AS attr_name + RETURN collect(attr_name) AS attr_names + } + WITH kind, attr_names + CALL { + WITH kind + OPTIONAL MATCH (n:DiffNode {kind: kind})-[:DIFF_HAS_RELATIONSHIP]->(r:DiffRelationship) + WHERE n.action <> $unchanged_str + AND r.action <> $unchanged_str + WITH DISTINCT r.name AS rel_name + RETURN collect(rel_name) AS rel_names + } + """ + self.add_to_query(query=query) + self.return_labels = ["kind", "attr_names", "rel_names"] + + async def get_field_summaries(self) -> list[NodeDiffFieldSummary]: + field_summaries = [] + for result in self.get_results(): + kind = result.get_as_type(label="kind", return_type=str) + attr_names = result.get_as_type(label="attr_names", return_type=list[str]) + rel_names = result.get_as_type(label="rel_names", return_type=list[str]) + if attr_names or rel_names: + field_summaries.append( + NodeDiffFieldSummary(kind=kind, attribute_names=set(attr_names), relationship_names=set(rel_names)) + ) + return field_summaries diff --git a/backend/infrahub/core/diff/query/merge.py b/backend/infrahub/core/diff/query/merge.py new file mode 100644 index 0000000000..465c321732 --- /dev/null +++ b/backend/infrahub/core/diff/query/merge.py @@ -0,0 +1,435 @@ +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 + MATCH (n:Node {uuid: node_diff_map.uuid}) + RETURN n +} +WITH n, node_diff_map +CALL { + WITH n, node_diff_map + WITH n, 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 n, node_diff_map, node_rel_status + WITH n, node_diff_map, node_rel_status + WHERE node_rel_status IS NOT NULL + MATCH (root:Root) + // ------------------------------ + // set IS_PART_OF.to, optionally, target branch + // ------------------------------ + WITH 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, n, node_rel_status + 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) + 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) + } + } + WITH n, node_diff_map + CALL { + WITH n, node_diff_map + WITH n, CASE + WHEN node_diff_map.attributes IS NULL OR node_diff_map.attributes = [] THEN [NULL] + ELSE node_diff_map.attributes + END AS attribute_maps + UNWIND attribute_maps AS attribute_diff_map + // ------------------------------ + // handle updates for attributes under this node + // ------------------------------ + CALL { + WITH n, attribute_diff_map + WITH n, attribute_diff_map.name AS attr_name, CASE + WHEN attribute_diff_map.action = "ADDED" THEN "active" + WHEN attribute_diff_map.action = "REMOVED" THEN "deleted" + ELSE NULL + END AS attr_rel_status + CALL { + WITH n, attr_name + OPTIONAL MATCH (n)-[has_attr:HAS_ATTRIBUTE]->(a:Attribute {name: attr_name}) + WHERE has_attr.branch IN [$source_branch, $target_branch] + RETURN a + ORDER BY has_attr.from DESC + LIMIT 1 + } + WITH n, attr_rel_status, a + // ------------------------------ + // set HAS_ATTRIBUTE.to on target branch if necessary + // ------------------------------ + CALL { + WITH n, attr_rel_status, a + OPTIONAL MATCH (n) + -[target_r_attr:HAS_ATTRIBUTE {branch: $target_branch, status: "active"}] + ->(a) + WHERE attr_rel_status = "deleted" + AND target_r_attr.from <= $at AND target_r_attr.to IS NULL + SET target_r_attr.to = $at + } + WITH n, attr_rel_status, a + // ------------------------------ + // conditionally create new HAS_ATTRIBUTE relationship on target_branch, if necessary + // ------------------------------ + CALL { + WITH n, attr_rel_status, a + WITH n, attr_rel_status, a + WHERE a IS NOT NULL + OPTIONAL MATCH (n)-[r_attr:HAS_ATTRIBUTE {branch: $target_branch}]->(a) + WHERE r_attr.status = attr_rel_status + AND r_attr.from <= $at + AND (r_attr.to >= $at OR r_attr.to IS NULL) + WITH n, r_attr, attr_rel_status, a + WHERE r_attr IS NULL + CREATE (n)-[:HAS_ATTRIBUTE { branch: $target_branch, branch_level: $branch_level, from: $at, status: attr_rel_status }]->(a) + } + RETURN 1 AS done + } + RETURN 1 AS done + } + WITH n, node_diff_map + CALL { + WITH n,node_diff_map + UNWIND node_diff_map.relationships AS relationship_diff_map + // ------------------------------ + // handle updates for relationships under this node + // ------------------------------ + CALL { + WITH n, relationship_diff_map + WITH n, relationship_diff_map.peer_id AS rel_peer_id, relationship_diff_map.name AS rel_name, CASE + WHEN relationship_diff_map.action = "ADDED" THEN "active" + WHEN relationship_diff_map.action = "REMOVED" THEN "deleted" + ELSE NULL + END AS related_rel_status + // ------------------------------ + // determine the directions of each IS_RELATED + // ------------------------------ + CALL { + WITH n, rel_name, rel_peer_id, related_rel_status + MATCH (n) + -[source_r_rel_1:IS_RELATED {branch: $source_branch}] + -(r:Relationship {name: rel_name}) + -[source_r_rel_2:IS_RELATED {branch: $source_branch}] + -(:Node {uuid: rel_peer_id}) + WHERE source_r_rel_1.from <= $at AND source_r_rel_1.to IS NULL + AND source_r_rel_2.from <= $at AND source_r_rel_2.to IS NULL + RETURN r, CASE + WHEN startNode(source_r_rel_1).uuid = n.uuid THEN "r" + ELSE "l" + END AS r1_dir, + CASE + WHEN startNode(source_r_rel_2).uuid = r.uuid THEN "r" + ELSE "l" + END AS r2_dir + } + WITH n, r, r1_dir, r2_dir, rel_name, rel_peer_id, related_rel_status + CALL { + WITH n, rel_name, rel_peer_id, related_rel_status + OPTIONAL MATCH (n) + -[target_r_rel_1:IS_RELATED {branch: $target_branch, status: "active"}] + -(:Relationship {name: rel_name}) + -[target_r_rel_2:IS_RELATED {branch: $target_branch, status: "active"}] + -(:Node {uuid: rel_peer_id}) + WHERE related_rel_status = "deleted" + AND target_r_rel_1.from <= $at AND target_r_rel_1.to IS NULL + AND target_r_rel_2.from <= $at AND target_r_rel_2.to IS NULL + SET target_r_rel_1.to = $at + SET target_r_rel_2.to = $at + } + WITH n, r, r1_dir, r2_dir, rel_name, rel_peer_id, related_rel_status + // ------------------------------ + // conditionally create new IS_RELATED relationships on target_branch, if necessary + // ------------------------------ + CALL { + WITH n, r, r1_dir, r2_dir, rel_name, rel_peer_id, related_rel_status + MATCH (p:Node {uuid: rel_peer_id}) + OPTIONAL MATCH (n) + -[r_rel_1:IS_RELATED {branch: $target_branch, status: related_rel_status}] + -(:Relationship {name: rel_name}) + -[r_rel_2:IS_RELATED {branch: $target_branch, status: related_rel_status}] + -(p) + WHERE r_rel_1.from <= $at + AND (r_rel_1.to >= $at OR r_rel_1.to IS NULL) + AND r_rel_2.from <= $at + AND (r_rel_2.to >= $at OR r_rel_2.to IS NULL) + WITH n, r, r1_dir, r2_dir, p, related_rel_status, r_rel_1, r_rel_2 + WHERE r_rel_1 IS NULL + AND r_rel_2 IS NULL + // ------------------------------ + // create IS_RELATED relationships with directions maintained from source + // ------------------------------ + CALL { + WITH n, r, r1_dir, related_rel_status + WITH n, r, r1_dir, related_rel_status + WHERE r1_dir = "r" + CREATE (n) + -[:IS_RELATED {branch: $target_branch, branch_level: $branch_level, from: $at, status: related_rel_status}] + ->(r) + } + CALL { + WITH n, r, r1_dir, related_rel_status + WITH n, r, r1_dir, related_rel_status + WHERE r1_dir = "l" + CREATE (n) + <-[:IS_RELATED {branch: $target_branch, branch_level: $branch_level, from: $at, status: related_rel_status}] + -(r) + } + CALL { + WITH r, p, r2_dir, related_rel_status + WITH r, p, r2_dir, related_rel_status + WHERE r2_dir = "r" + CREATE (r) + -[:IS_RELATED {branch: $target_branch, branch_level: $branch_level, from: $at, status: related_rel_status}] + ->(p) + } + CALL { + WITH r, p, r2_dir, related_rel_status + WITH r, p, r2_dir, related_rel_status + WHERE r2_dir = "l" + CREATE (r) + <-[:IS_RELATED {branch: $target_branch, branch_level: $branch_level, from: $at, status: related_rel_status}] + -(p) + } + } + } + } +} +RETURN 1 AS done + """ + self.add_to_query(query=query) + + +class DiffMergePropertiesQuery(Query): + name = "diff_merge_properties" + type = QueryType.WRITE + insert_return = False + + def __init__( + self, + property_diff_dicts: dict[str, Any], + at: Timestamp, + target_branch: Branch, + **kwargs: Any, + ) -> None: + super().__init__(**kwargs) + self.property_diff_dicts = property_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 = { + "property_diff_dicts": self.property_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 $property_diff_dicts AS attr_rel_prop_diff +CALL { + // ------------------------------ + // find the Attribute node + // ------------------------------ + WITH attr_rel_prop_diff + CALL { + WITH attr_rel_prop_diff + OPTIONAL MATCH (n:Node {uuid: attr_rel_prop_diff.node_uuid}) + -[has_attr:HAS_ATTRIBUTE] + ->(attr:Attribute {name: attr_rel_prop_diff.attribute_name}) + WHERE attr_rel_prop_diff.attribute_name IS NOT NULL + AND has_attr.branch IN [$source_branch, $target_branch] + RETURN attr + ORDER BY has_attr.from DESC + LIMIT 1 + } + CALL { + WITH attr_rel_prop_diff + OPTIONAL MATCH (n:Node {uuid: attr_rel_prop_diff.node_uuid}) + -[r1:IS_RELATED] + -(rel:Relationship {name: attr_rel_prop_diff.relationship_id}) + -[r2:IS_RELATED] + -(:Node {uuid: attr_rel_prop_diff.peer_uuid}) + WHERE attr_rel_prop_diff.relationship_id IS NOT NULL + AND r1.branch IN [$source_branch, $target_branch] + AND r2.branch IN [$source_branch, $target_branch] + RETURN rel + ORDER BY r1.branch_level DESC, r2.branch_level DESC, r1.from DESC, r2.from DESC + LIMIT 1 + } + WITH attr_rel_prop_diff, COALESCE(attr, rel) AS attr_rel + UNWIND attr_rel_prop_diff.properties AS property_diff + // ------------------------------ + // handle updates for properties under this attribute/relationship + // ------------------------------ + CALL { + WITH attr_rel, property_diff + // ------------------------------ + // identify the correct property node to link + // ------------------------------ + CALL { + WITH attr_rel, property_diff + OPTIONAL MATCH (peer:Node {uuid: property_diff.value}) + WHERE property_diff.property_type IN ["HAS_SOURCE", "HAS_OWNER"] + // ------------------------------ + // the serialized diff might not include the values for IS_VISIBLE and IS_PROTECTED in + // some cases, so we need to figure them out here + // ------------------------------ + CALL { + WITH attr_rel, property_diff + OPTIONAL MATCH (attr_rel)-[r_vis_pro]->(bool:Boolean) + WHERE property_diff.property_type IN ["IS_VISIBLE", "IS_PROTECTED"] + AND r_vis_pro.branch IN [$source_branch, $target_branch] + AND type(r_vis_pro) = property_diff.property_type + AND (property_diff.value IS NULL OR bool.value = property_diff.value) + RETURN bool + ORDER BY r_vis_pro.from DESC + LIMIT 1 + } + CALL { + // ------------------------------ + // get the latest linked AttributeValue on the source b/c there could be multiple + // with different is_default values + // ------------------------------ + WITH attr_rel, property_diff + OPTIONAL MATCH (attr_rel)-[r_attr_val:HAS_VALUE]->(av:AttributeValue) + WHERE property_diff.property_type = "HAS_VALUE" + AND ( + av.value = property_diff.value + OR toLower(toString(av.value)) = toLower(toString(property_diff.value)) + ) + AND r_attr_val.branch IN [$source_branch, $target_branch] + RETURN av + ORDER BY r_attr_val.from DESC + LIMIT 1 + } + RETURN COALESCE (peer, bool, av) AS prop_node + } + WITH attr_rel, property_diff.property_type AS prop_type, prop_node, CASE + WHEN property_diff.action = "ADDED" THEN "active" + WHEN property_diff.action = "REMOVED" THEN "deleted" + ELSE NULL + END as prop_rel_status + // ------------------------------ + // set property edge.to, optionally, on target branch + // ------------------------------ + CALL { + WITH attr_rel, prop_rel_status, prop_type + OPTIONAL MATCH (attr_rel) + -[target_r_prop {branch: $target_branch}] + ->() + WHERE type(target_r_prop) = prop_type + AND target_r_prop.from < $at AND target_r_prop.to IS NULL + SET target_r_prop.to = $at + } + // ------------------------------ + // check for existing edge on target_branch + // ------------------------------ + CALL { + WITH attr_rel, prop_rel_status, prop_type, prop_node + OPTIONAL MATCH (attr_rel)-[r_prop {branch: $target_branch}]->(prop_node) + WHERE type(r_prop) = prop_type + AND r_prop.status = prop_rel_status + AND r_prop.from <= $at + AND (r_prop.to > $at OR r_prop.to IS NULL) + RETURN r_prop + } + WITH attr_rel, prop_rel_status, prop_type, prop_node, r_prop + WHERE r_prop IS NULL + // ------------------------------ + // create new edge to prop_node on target_branch, if necessary + // one subquery per possible edge type b/c edge type cannot be a variable + // ------------------------------ + CALL { + WITH attr_rel, prop_rel_status, prop_type, prop_node + WITH attr_rel, prop_rel_status, prop_type, prop_node + WHERE prop_type = "HAS_VALUE" + CREATE (attr_rel)-[:HAS_VALUE { branch: $target_branch, branch_level: $branch_level, from: $at, status: prop_rel_status }]->(prop_node) + } + CALL { + WITH attr_rel, prop_rel_status, prop_type, prop_node + WITH attr_rel, prop_rel_status, prop_type, prop_node + WHERE prop_type = "HAS_SOURCE" + CREATE (attr_rel)-[:HAS_SOURCE { branch: $target_branch, branch_level: $branch_level, from: $at, status: prop_rel_status }]->(prop_node) + } + CALL { + WITH attr_rel, prop_rel_status, prop_type, prop_node + WITH attr_rel, prop_rel_status, prop_type, prop_node + WHERE prop_type = "HAS_OWNER" + CREATE (attr_rel)-[:HAS_OWNER { branch: $target_branch, branch_level: $branch_level, from: $at, status: prop_rel_status }]->(prop_node) + } + CALL { + WITH attr_rel, prop_rel_status, prop_type, prop_node + WITH attr_rel, prop_rel_status, prop_type, prop_node + WHERE prop_type = "IS_VISIBLE" + CREATE (attr_rel)-[:IS_VISIBLE { branch: $target_branch, branch_level: $branch_level, from: $at, status: prop_rel_status }]->(prop_node) + } + CALL { + WITH attr_rel, prop_rel_status, prop_type, prop_node + WITH attr_rel, prop_rel_status, prop_type, prop_node + WHERE prop_type = "IS_PROTECTED" + CREATE (attr_rel)-[:IS_PROTECTED { branch: $target_branch, branch_level: $branch_level, from: $at, status: prop_rel_status }]->(prop_node) + } + } +} + """ + 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..ed12f8152c 100644 --- a/backend/infrahub/core/diff/query_parser.py +++ b/backend/infrahub/core/diff/query_parser.py @@ -4,7 +4,7 @@ from typing import TYPE_CHECKING, Any, Optional from uuid import uuid4 -from infrahub.core.constants import DiffAction, RelationshipCardinality, RelationshipStatus +from infrahub.core.constants import BranchSupportType, DiffAction, RelationshipCardinality, RelationshipStatus from infrahub.core.constants.database import DatabaseEdgeType from infrahub.core.timestamp import Timestamp @@ -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): ... @@ -212,7 +212,10 @@ def from_properties( raise DiffNoPeerIdError(f"Cannot identify peer ID for relationship property {(properties[0]).db_id}") ordered_properties_by_type: dict[DatabaseEdgeType, list[DiffRelationshipPropertyIntermediate]] = {} - chronological_properties = sorted(properties, key=lambda p: p.changed_at) + # tiebreaker for simultaneous updates is to prefer the DELETED relationship + chronological_properties = sorted( + properties, key=lambda p: (p.changed_at, p.status is RelationshipStatus.ACTIVE) + ) last_changed_at = chronological_properties[-1].changed_at for chronological_property in chronological_properties: property_key = DatabaseEdgeType(chronological_property.property_type) @@ -307,7 +310,7 @@ class DiffRelationshipIntermediate: properties_by_db_id: dict[str, set[DiffRelationshipPropertyIntermediate]] = field(default_factory=dict) _single_relationship_list: list[DiffSingleRelationshipIntermediate] = field(default_factory=list) - def add_path(self, database_path: DatabasePath) -> None: + def add_path(self, database_path: DatabasePath, diff_from_time: Timestamp, diff_to_time: Timestamp) -> None: if database_path.property_type in [ DatabaseEdgeType.IS_RELATED, DatabaseEdgeType.HAS_OWNER, @@ -323,11 +326,26 @@ def add_path(self, database_path: DatabasePath) -> None: DiffRelationshipPropertyIntermediate( db_id=db_id, property_type=database_path.property_type, - changed_at=database_path.property_changed_at, + changed_at=database_path.property_from_time, status=database_path.property_status, value=value, ) ) + to_time = database_path.property_to_time + if ( + to_time + and database_path.property_from_time < diff_from_time <= to_time <= diff_to_time + and database_path.property_status is RelationshipStatus.ACTIVE + ): + self.properties_by_db_id[db_id].add( + DiffRelationshipPropertyIntermediate( + db_id=db_id, + property_type=database_path.property_type, + changed_at=to_time, + status=RelationshipStatus.DELETED, + value=value, + ) + ) def _index_relationships(self) -> None: self._single_relationship_list = [ @@ -362,6 +380,7 @@ def to_diff_relationship(self, from_time: Timestamp) -> DiffRelationship: @dataclass class DiffNodeIntermediate(TrackedStatusUpdates): + force_action: DiffAction | None uuid: str kind: str attributes_by_name: dict[str, DiffAttributeIntermediate] = field(default_factory=dict) @@ -381,6 +400,8 @@ def to_diff_node(self, from_time: Timestamp) -> DiffNode: action, changed_at = self.get_action_and_timestamp(from_time=from_time) if not attributes and not relationships: action = DiffAction.UNCHANGED + if self.force_action: + action = self.force_action return DiffNode( uuid=self.uuid, kind=self.kind, @@ -447,15 +468,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: @@ -486,6 +509,9 @@ def _get_diff_node(self, database_path: DatabasePath, diff_root: DiffRootInterme diff_root.nodes_by_id[node_id] = DiffNodeIntermediate( uuid=node_id, kind=database_path.node_kind, + force_action=DiffAction.UPDATED + if database_path.node_branch_support is BranchSupportType.AGNOSTIC + else None, ) diff_node = diff_root.nodes_by_id[node_id] diff_node.track_database_path(database_path=database_path) @@ -505,7 +531,9 @@ def _update_attribute_level(self, database_path: DatabasePath, diff_node: DiffNo if not relationship_schema: return diff_relationship = self._get_diff_relationship(diff_node=diff_node, relationship_schema=relationship_schema) - diff_relationship.add_path(database_path=database_path) + diff_relationship.add_path( + database_path=database_path, diff_from_time=self.from_time, diff_to_time=self.to_time + ) def _get_diff_attribute( self, database_path: DatabasePath, diff_node: DiffNodeIntermediate @@ -527,11 +555,14 @@ def _update_attribute_property( if property_type not in diff_attribute.properties_by_type: diff_attribute.properties_by_type[property_type] = DiffPropertyIntermediate(property_type=property_type) diff_property = diff_attribute.properties_by_type[property_type] + value = database_path.property_value + if database_path.property_is_peer: + value = database_path.peer_id diff_property.add_value( diff_value=DiffValueIntermediate( - changed_at=database_path.property_changed_at, + changed_at=database_path.property_from_time, status=database_path.property_status, - value=database_path.property_value, + value=value, ) ) diff --git a/backend/infrahub/core/diff/repository/repository.py b/backend/infrahub/core/diff/repository/repository.py index b4a71919ec..15854f1a9a 100644 --- a/backend/infrahub/core/diff/repository/repository.py +++ b/backend/infrahub/core/diff/repository/repository.py @@ -2,8 +2,9 @@ from infrahub import config from infrahub.core import registry +from infrahub.core.diff.query.field_summary import EnrichedDiffNodeFieldSummaryQuery from infrahub.core.timestamp import Timestamp -from infrahub.database import InfrahubDatabase +from infrahub.database import InfrahubDatabase, retry_db_transaction from infrahub.exceptions import ResourceNotFoundError from ..model.path import ( @@ -12,6 +13,7 @@ EnrichedDiffRoot, EnrichedDiffs, EnrichedNodeCreateRequest, + NodeDiffFieldSummary, TimeRange, TrackingId, ) @@ -155,6 +157,7 @@ def _get_node_create_request_batch( if node_requests: yield node_requests + @retry_db_transaction(name="enriched_diff_save") async def save(self, enriched_diffs: EnrichedDiffs) -> None: root_query = await EnrichedDiffRootsCreateQuery.init(db=self.db, enriched_diffs=enriched_diffs) await root_query.execute(db=self.db) @@ -235,3 +238,12 @@ async def update_conflict_by_id( if not conflict_node: raise ResourceNotFoundError(f"No conflict with id {conflict_id}") return self.deserializer.deserialize_conflict(diff_conflict_node=conflict_node) + + async def get_node_field_summaries( + self, diff_branch_name: str, tracking_id: TrackingId | None = None, diff_id: str | None = None + ) -> list[NodeDiffFieldSummary]: + query = await EnrichedDiffNodeFieldSummaryQuery.init( + db=self.db, diff_branch_name=diff_branch_name, tracking_id=tracking_id, diff_id=diff_id + ) + await query.execute(db=self.db) + return await query.get_field_summaries() diff --git a/backend/infrahub/message_bus/operations/requests/diff.py b/backend/infrahub/core/diff/tasks.py similarity index 65% rename from backend/infrahub/message_bus/operations/requests/diff.py rename to backend/infrahub/core/diff/tasks.py index 479af29511..eda8137918 100644 --- a/backend/infrahub/message_bus/operations/requests/diff.py +++ b/backend/infrahub/core/diff/tasks.py @@ -1,33 +1,40 @@ +from prefect import flow + from infrahub.core import registry from infrahub.core.diff.coordinator import DiffCoordinator +from infrahub.core.diff.models import RequestDiffRefresh, RequestDiffUpdate from infrahub.dependencies.registry import get_component_registry from infrahub.log import get_logger -from infrahub.message_bus import messages -from infrahub.services import InfrahubServices +from infrahub.services import services log = get_logger() -async def update(message: messages.RequestDiffUpdate, service: InfrahubServices) -> None: +@flow(name="diff-update") +async def update_diff(model: RequestDiffUpdate) -> None: + service = services.service component_registry = get_component_registry() base_branch = await registry.get_branch(db=service.database, branch=registry.default_branch) - diff_branch = await registry.get_branch(db=service.database, branch=message.branch_name) + diff_branch = await registry.get_branch(db=service.database, branch=model.branch_name) diff_coordinator = await component_registry.get_component(DiffCoordinator, db=service.database, branch=diff_branch) await diff_coordinator.run_update( base_branch=base_branch, diff_branch=diff_branch, - from_time=message.from_time, - to_time=message.to_time, - name=message.name, + from_time=model.from_time, + to_time=model.to_time, + name=model.name, ) -async def refresh(message: messages.RequestDiffRefresh, service: InfrahubServices) -> None: +@flow(name="diff-refresh") +async def refresh_diff(model: RequestDiffRefresh) -> None: + service = services.service + component_registry = get_component_registry() base_branch = await registry.get_branch(db=service.database, branch=registry.default_branch) - diff_branch = await registry.get_branch(db=service.database, branch=message.branch_name) + diff_branch = await registry.get_branch(db=service.database, branch=model.branch_name) diff_coordinator = await component_registry.get_component(DiffCoordinator, db=service.database, branch=diff_branch) - await diff_coordinator.recalculate(base_branch=base_branch, diff_branch=diff_branch, diff_id=message.diff_id) + await diff_coordinator.recalculate(base_branch=base_branch, diff_branch=diff_branch, diff_id=model.diff_id) diff --git a/backend/infrahub/core/initialization.py b/backend/infrahub/core/initialization.py index 1c3d5b3b8a..ae6989a849 100644 --- a/backend/infrahub/core/initialization.py +++ b/backend/infrahub/core/initialization.py @@ -1,22 +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, + PermissionAction, + PermissionDecision, +) from infrahub.core.graph import GRAPH_VERSION +from infrahub.core.manager import NodeManager 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 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.utils import create_menu_children +from infrahub.permissions import PermissionBackend from infrahub.storage import InfrahubObjectStorage log = get_logger() @@ -53,6 +68,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 +108,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: @@ -126,6 +160,7 @@ async def initialization(db: InfrahubDatabase) -> None: hash_new=default_branch.active_schema_hash.main, branch=default_branch.name, ) + await default_branch.save(db=db) for branch in list(registry.branch.values()): if branch.name in [default_branch.name, GLOBAL_BRANCH_NAME]: @@ -141,6 +176,7 @@ async def initialization(db: InfrahubDatabase) -> None: f" {hash_in_db!r} >> {branch.active_schema_hash.main!r}", branch=branch.name, ) + await branch.save(db=db) # --------------------------------------------------- # Load Default Namespace @@ -233,9 +269,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 +297,165 @@ async def create_ipam_namespace( return obj +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, + action=GlobalPermissions.SUPER_ADMIN.value, + decision=PermissionDecision.ALLOW_ALL.value, + description="Allow a user to do anything", + ) + 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_default_roles(db: InfrahubDatabase) -> Node: + repo_permission = await Node.init(db=db, schema=InfrahubKind.GLOBALPERMISSION) + await repo_permission.new( + db=db, + action=GlobalPermissions.MANAGE_REPOSITORIES.value, + decision=PermissionDecision.ALLOW_ALL.value, + description="Allow a user to manage repositories", + ) + await repo_permission.save(db=db) + + schema_permission = await Node.init(db=db, schema=InfrahubKind.GLOBALPERMISSION) + await schema_permission.new( + db=db, + action=GlobalPermissions.MANAGE_SCHEMA.value, + decision=PermissionDecision.ALLOW_ALL.value, + description="Allow a user to manage the schema", + ) + await schema_permission.save(db=db) + + proposed_change_permission = await Node.init(db=db, schema=InfrahubKind.GLOBALPERMISSION) + await proposed_change_permission.new( + db=db, + action=GlobalPermissions.MERGE_PROPOSED_CHANGE.value, + decision=PermissionDecision.ALLOW_ALL.value, + description="Allow a user to merge proposed changes", + ) + await proposed_change_permission.save(db=db) + + # Other permissions, created to keep references of them from the start + for permission_action, permission_description in ( + (GlobalPermissions.EDIT_DEFAULT_BRANCH, "Allow a user to change data in the default branch"), + (GlobalPermissions.MANAGE_ACCOUNTS, "Allow a user to manage accounts, account roles and account groups"), + (GlobalPermissions.MANAGE_PERMISSIONS, "Allow a user to manage permissions"), + (GlobalPermissions.MERGE_BRANCH, "Allow a user to merge branches"), + ): + permission = await Node.init(db=db, schema=InfrahubKind.GLOBALPERMISSION) + await permission.new( + db=db, + action=permission_action.value, + decision=PermissionDecision.ALLOW_ALL.value, + description=permission_description, + ) + await permission.save(db=db) + + view_permission = await Node.init(db=db, schema=InfrahubKind.OBJECTPERMISSION) + await view_permission.new( + db=db, + name="*", + namespace="*", + action=PermissionAction.VIEW.value, + decision=PermissionDecision.ALLOW_ALL.value, + description="Allow a user to view any object in any branch", + ) + await view_permission.save(db=db) + + modify_permission = await Node.init(db=db, schema=InfrahubKind.OBJECTPERMISSION) + await modify_permission.new( + db=db, + name="*", + namespace="*", + action=PermissionAction.ANY.value, + decision=PermissionDecision.ALLOW_OTHER.value, + description="Allow a user to change data in non-default branches", + ) + await modify_permission.save(db=db) + + role_name = "General Access" + role = await Node.init(db=db, schema=InfrahubKind.ACCOUNTROLE) + await role.new( + db=db, + name=role_name, + permissions=[ + repo_permission, + schema_permission, + proposed_change_permission, + view_permission, + modify_permission, + ], + ) + await role.save(db=db) + log.info(f"Created account role: {role_name}") + + group_name = "Infrahub Users" + 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}") + + return role + + +async def create_anonymous_role(db: InfrahubDatabase) -> Node: + deny_permission = await Node.init(db=db, schema=InfrahubKind.OBJECTPERMISSION) + await deny_permission.new( + db=db, name="*", namespace="*", action=PermissionAction.ANY.value, decision=PermissionDecision.DENY.value + ) + await deny_permission.save(db=db) + + view_permission = await NodeManager.get_one_by_hfid( + db=db, + kind=InfrahubKind.OBJECTPERMISSION, + hfid=["*", "*", PermissionAction.VIEW.value, str(PermissionDecision.ALLOW_ALL.value)], + ) + + role = await Node.init(db=db, schema=InfrahubKind.ACCOUNTROLE) + await role.new( + db=db, name=config.SETTINGS.main.anonymous_access_role, permissions=[deny_permission, view_permission] + ) + await role.save(db=db) + log.info(f"Created anonymous account role: {config.SETTINGS.main.anonymous_access_role}") + + return role + + +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 +478,47 @@ 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) + + await create_default_roles(db=db) + if config.SETTINGS.main.allow_anonymous_access: + await create_anonymous_role(db=db) + # -------------------------------------------------- # Create Default IPAM Namespace # -------------------------------------------------- diff --git a/backend/infrahub/core/ipam/tasks.py b/backend/infrahub/core/ipam/tasks.py new file mode 100644 index 0000000000..a593c71360 --- /dev/null +++ b/backend/infrahub/core/ipam/tasks.py @@ -0,0 +1,41 @@ +import ipaddress +from typing import TYPE_CHECKING + +from prefect import flow + +from infrahub.core import registry +from infrahub.core.ipam.reconciler import IpamReconciler +from infrahub.services import services +from infrahub.workflows.utils import add_branch_tag + +from .model import IpamNodeDetails + +if TYPE_CHECKING: + from infrahub.core.ipam.constants import AllIPTypes + + +@flow( + name="ipam-reconciliation", + flow_run_name="branch-{branch}", + description="Ensure the IPAM Tree is up to date", + persist_result=False, +) +async def ipam_reconciliation(branch: str, ipam_node_details: list[IpamNodeDetails]) -> None: + service = services.service + branch_obj = await registry.get_branch(db=service.database, branch=branch) + + await add_branch_tag(branch_name=branch_obj.name) + + ipam_reconciler = IpamReconciler(db=service.database, branch=branch_obj) + + for ipam_node_detail_item in ipam_node_details: + if ipam_node_detail_item.is_address: + ip_value: AllIPTypes = ipaddress.ip_interface(ipam_node_detail_item.ip_value) + else: + ip_value = ipaddress.ip_network(ipam_node_detail_item.ip_value) + await ipam_reconciler.reconcile( + ip_value=ip_value, + namespace=ipam_node_detail_item.namespace_id, + node_uuid=ipam_node_detail_item.node_uuid, + is_delete=ipam_node_detail_item.is_delete, + ) 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..08e550223d 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 @@ -372,6 +372,12 @@ async def query_peers( if display_label_fields: fields = deep_merge_dict(dicta=fields, dictb=display_label_fields) + if fields and "hfid" in fields: + peer_schema = schema.get_peer_schema(db=db, branch=branch) + hfid_fields = peer_schema.generate_fields_for_hfid() + if hfid_fields: + fields = deep_merge_dict(dicta=fields, dictb=hfid_fields) + if fetch_peers: peer_ids = [peer.peer_id for peer in peers_info] peer_nodes = await cls.get_many( @@ -1043,21 +1049,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..1cb00c19f7 100644 --- a/backend/infrahub/core/merge.py +++ b/backend/infrahub/core/merge.py @@ -2,19 +2,13 @@ from typing import TYPE_CHECKING, Optional, Union -from infrahub.core.constants import DiffAction, RelationshipStatus, RepositoryInternalStatus +from infrahub.core.constants import DiffAction, 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, -) -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 from infrahub.message_bus import messages @@ -22,8 +16,11 @@ if TYPE_CHECKING: from infrahub.core.branch import Branch + from infrahub.core.diff.coordinator import DiffCoordinator + from infrahub.core.diff.merger.merger import DiffMerger 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 @@ -35,12 +32,16 @@ def __init__( self, db: InfrahubDatabase, source_branch: Branch, + diff_coordinator: DiffCoordinator, + diff_merger: DiffMerger, destination_branch: Optional[Branch] = None, service: Optional[InfrahubServices] = None, ): self.source_branch = source_branch - self.destination_branch = destination_branch or registry.get_branch_from_registry() + self.destination_branch: Branch = destination_branch or registry.get_branch_from_registry() self.db = db + self.diff_coordinator = diff_coordinator + self.diff_merger = diff_merger self.migrations: list[SchemaUpdateMigrationInfo] = [] self._graph_diff: Optional[BranchDiffer] = None @@ -228,209 +229,31 @@ async def validate_graph(self) -> list[DataConflict]: async def merge( self, at: Optional[Union[str, Timestamp]] = None, - conflict_resolution: Optional[dict[str, bool]] = None, + conflict_resolution: Optional[dict[str, bool]] = None, # pylint: disable=unused-argument ) -> None: """Merge the current branch into main.""" - conflict_resolution = conflict_resolution or {} - conflicts = await self.validate_branch() - - if conflict_resolution: - errors: list[str] = [] - for conflict in conflicts: - if conflict.conflict_path not in conflict_resolution: - errors.append(str(conflict)) - - if errors: - raise ValidationError( - f"Unable to merge the branch '{self.source_branch.name}', conflict resolution missing: {', '.join(errors)}" - ) + if self.source_branch.name == registry.default_branch: + raise ValidationError(f"Unable to merge the branch '{self.source_branch.name}' into itself") - elif conflicts: - errors = [str(conflict) for conflict in conflicts] + enriched_diff = await self.diff_coordinator.update_branch_diff( + base_branch=self.destination_branch, diff_branch=self.source_branch + ) + conflict_map = enriched_diff.get_all_conflicts() + errors: list[str] = [] + for conflict_path, conflict in conflict_map.items(): + if conflict.selected_branch is None: + errors.append(conflict_path) + + if errors: raise ValidationError( - f"Unable to merge the branch '{self.source_branch.name}', validation failed: {', '.join(errors)}" + f"Unable to merge the branch '{self.source_branch.name}', conflict resolution missing: {', '.join(errors)}" ) - if self.source_branch.name == registry.default_branch: - raise ValidationError(f"Unable to merge the branch '{self.source_branch.name}' into itself") - # TODO need to find a way to properly communicate back to the user any issue that could come up during the merge # From the Graph or From the repositories - await self.merge_graph(at=at, conflict_resolution=conflict_resolution) - await self.merge_repositories() - - async def merge_graph( # pylint: disable=too-many-branches,too-many-statements - self, - at: Optional[Union[str, Timestamp]] = None, - conflict_resolution: Optional[dict[str, bool]] = None, - ) -> None: - rel_ids_to_update: list[str] = [] - conflict_resolution = conflict_resolution or {} - - default_branch: Branch = registry.branch[registry.default_branch] - at = Timestamp(at) - - diff = await self.get_graph_diff() - nodes = await diff.get_nodes() - - if self.source_branch.name in nodes: - origin_nodes_query = await NodeListGetInfoQuery.init( - db=self.db, ids=list(nodes[self.source_branch.name].keys()), branch=default_branch - ) - await origin_nodes_query.execute(db=self.db) - origin_nodes = { - node.get("n").get("uuid"): node for node in origin_nodes_query.get_results_group_by(("n", "uuid")) - } - - # --------------------------------------------- - # NODES - # --------------------------------------------- - for node_id, node in nodes[self.source_branch.name].items(): - if node.action == DiffAction.ADDED: - query1 = await AddNodeToBranch.init(db=self.db, node_id=node.db_id, branch=default_branch) - await query1.execute(db=self.db) - if node.rel_id: - rel_ids_to_update.append(node.rel_id) - - elif node.action == DiffAction.REMOVED: - if node_id in origin_nodes: - query2 = await NodeDeleteQuery.init(db=self.db, branch=default_branch, node_id=node_id, at=at) - await query2.execute(db=self.db) - if node.rel_id: - rel_ids_to_update.extend([node.rel_id, origin_nodes[node_id].get("rb").element_id]) - - for attr in node.attributes.values(): - if attr.action == DiffAction.ADDED: - await add_relationship( - src_node_id=node.db_id, - dst_node_id=attr.db_id, - rel_type="HAS_ATTRIBUTE", - at=at, - branch_name=default_branch.name, - branch_level=default_branch.hierarchy_level, - db=self.db, - ) - rel_ids_to_update.append(attr.rel_id) - - elif attr.action == DiffAction.REMOVED and attr.origin_rel_id: - await add_relationship( - src_node_id=node.db_id, - dst_node_id=attr.db_id, - rel_type="HAS_ATTRIBUTE", - branch_name=default_branch.name, - branch_level=default_branch.hierarchy_level, - at=at, - status=RelationshipStatus.DELETED, - db=self.db, - ) - rel_ids_to_update.extend([attr.rel_id, attr.origin_rel_id]) - - for prop_type, prop in attr.properties.items(): - if prop.action == DiffAction.ADDED: - await add_relationship( - src_node_id=attr.db_id, - dst_node_id=prop.db_id, - rel_type=prop_type, - at=at, - branch_name=default_branch.name, - branch_level=default_branch.hierarchy_level, - db=self.db, - ) - rel_ids_to_update.append(prop.rel_id) - - elif ( - prop.action == DiffAction.UPDATED - and (prop.path not in conflict_resolution or conflict_resolution[prop.path]) - and prop.origin_rel_id - ): - await add_relationship( - src_node_id=attr.db_id, - dst_node_id=prop.db_id, - rel_type=prop_type, - at=at, - branch_name=default_branch.name, - branch_level=default_branch.hierarchy_level, - db=self.db, - ) - rel_ids_to_update.extend([prop.rel_id, prop.origin_rel_id]) - - elif prop.action == DiffAction.REMOVED and prop.origin_rel_id: - await add_relationship( - src_node_id=attr.db_id, - dst_node_id=prop.db_id, - rel_type=prop_type, - at=at, - branch_name=default_branch.name, - branch_level=default_branch.hierarchy_level, - status=RelationshipStatus.DELETED, - db=self.db, - ) - rel_ids_to_update.extend([prop.rel_id, prop.origin_rel_id]) - - # --------------------------------------------- - # RELATIONSHIPS - # --------------------------------------------- - rels = await diff.get_relationships() - branch_relationships = rels.get(self.source_branch.name, {}) - - for rel_name in branch_relationships.keys(): - for rel_element in branch_relationships[rel_name].values(): - for rel_node in rel_element.nodes.values(): - matched_conflict_path = [path for path in rel_element.conflict_paths if path in conflict_resolution] - conflict_path = None - if matched_conflict_path: - conflict_path = matched_conflict_path[0] - - if rel_element.action in [DiffAction.ADDED, DiffAction.REMOVED] and ( - conflict_path not in conflict_resolution or conflict_resolution[conflict_path] - ): - rel_status = RelationshipStatus.ACTIVE - if rel_element.action == DiffAction.REMOVED: - rel_status = RelationshipStatus.DELETED - - if not rel_node.rel_id or not rel_node.db_id or not rel_element.db_id: - raise ValueError("node.rel_id, rel_node.db_id and rel_element.db_id must be defined") - - await add_relationship( - src_node_id=rel_node.db_id, - dst_node_id=rel_element.db_id, - rel_type="IS_RELATED", - at=at, - branch_name=default_branch.name, - branch_level=default_branch.hierarchy_level, - status=rel_status, - db=self.db, - ) - rel_ids_to_update.append(rel_node.rel_id) - - for prop_type, prop in rel_element.properties.items(): - rel_status = RelationshipStatus.ACTIVE - if prop.action == DiffAction.REMOVED: - rel_status = RelationshipStatus.DELETED - - await add_relationship( - src_node_id=rel_element.db_id, - dst_node_id=prop.db_id, - rel_type=prop.type, - at=at, - branch_name=default_branch.name, - branch_level=default_branch.hierarchy_level, - db=self.db, - ) - rel_ids_to_update.append(prop.rel_id) - - if rel_element.action in [DiffAction.UPDATED, DiffAction.REMOVED] and prop.origin_rel_id: - rel_ids_to_update.append(prop.origin_rel_id) - - if rel_ids_to_update: - await update_relationships_to(ids=rel_ids_to_update, to=at, db=self.db) - - # Update the branched_from time and update the registry - # provided that an update is needed - self.source_branch.branched_from = Timestamp().to_string() - await self.source_branch.save(db=self.db) - registry.branch[self.source_branch.name] = self.source_branch + await self.diff_merger.merge_graph(at=at) + await self.merge_repositories() async def merge_repositories(self) -> None: # Collect all Repositories in Main because we'll need the commit in Main for each one. 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..616deb5a39 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 @@ -61,11 +61,18 @@ def __init__(self, **kwargs: Any): def render_match(self) -> str: query = """ // Find all the active nodes - MATCH (node:Node) - WHERE ( "Profile%(node_kind)s" IN LABELS(node) OR "%(node_kind)s" IN LABELS(node) ) - AND exists((node)-[:HAS_ATTRIBUTE]-(:Attribute { name: $prev_attr.name })) - AND NOT exists((node)-[:HAS_ATTRIBUTE]-(:Attribute { name: $new_attr.name })) - + CALL { + MATCH (node:%(node_kind)s) + WHERE exists((node)-[:HAS_ATTRIBUTE]-(:Attribute { name: $prev_attr.name })) + AND NOT exists((node)-[:HAS_ATTRIBUTE]-(:Attribute { name: $new_attr.name })) + RETURN node + UNION + MATCH (node:Profile%(node_kind)s) + WHERE exists((node)-[:HAS_ATTRIBUTE]-(:Attribute { name: $prev_attr.name })) + AND NOT exists((node)-[:HAS_ATTRIBUTE]-(:Attribute { name: $new_attr.name })) + RETURN node + } + WITH node """ % {"node_kind": self.previous_attr.node_kind} return query @@ -85,12 +92,14 @@ def __init__(self, **kwargs: Any): InfrahubKind.LINEAGEOWNER, InfrahubKind.LINEAGESOURCE, ], + kind=InfrahubKind.ACCOUNT, ) previous_node = SchemaNodeInfo( name="Account", namespace="Core", branch_support=BranchSupportType.AGNOSTIC.value, labels=[InfrahubKind.ACCOUNT, InfrahubKind.LINEAGEOWNER, InfrahubKind.LINEAGESOURCE], + kind=InfrahubKind.ACCOUNT, ) branch = Branch( diff --git a/backend/infrahub/core/migrations/query/attribute_add.py b/backend/infrahub/core/migrations/query/attribute_add.py index d34a8e285d..f55dc84b2f 100644 --- a/backend/infrahub/core/migrations/query/attribute_add.py +++ b/backend/infrahub/core/migrations/query/attribute_add.py @@ -37,6 +37,7 @@ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> No self.params["attr_name"] = self.attribute_name self.params["attr_type"] = self.attribute_kind self.params["branch_support"] = self.branch_support + self.params["current_time"] = self.at.to_string() if self.default_value is not None: self.params["attr_value"] = self.default_value @@ -55,28 +56,34 @@ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> No query = """ MATCH p = (n:%(node_kind)s) - WHERE NOT exists((n)-[:HAS_ATTRIBUTE]-(:Attribute { name: $attr_name })) CALL { WITH n - MATCH (root:Root)<-[r:IS_PART_OF]-(n) - WHERE %(branch_filter)s - RETURN n as n1, r as r1 - ORDER BY r.branch_level DESC, r.from DESC + MATCH (root:Root)<-[r1:IS_PART_OF]-(n) + OPTIONAL MATCH (n)-[r2:HAS_ATTRIBUTE]-(:Attribute { name: $attr_name }) + WHERE all(r in [r1, r2] WHERE (%(branch_filter)s)) + RETURN n as n1, r1 as r11, r2 as r12 + ORDER BY r2.branch_level DESC, r2.from ASC, r1.branch_level DESC, r1.from ASC LIMIT 1 } - WITH n1 as n, r1 as rb - WHERE rb.status = "active" + WITH n1 as n, r11 as r1, r12 as r2 + WHERE r1.status = "active" AND (r2 IS NULL OR r2.status = "deleted") MERGE (av:AttributeValue { value: $attr_value, is_default: true }) MERGE (is_protected_value:Boolean { value: $is_protected_default }) MERGE (is_visible_value:Boolean { value: $is_visible_default }) - WITH n, av, is_protected_value, is_visible_value + WITH n, av, is_protected_value, is_visible_value, r2 CREATE (a:Attribute { name: $attr_name, branch_support: $branch_support }) CREATE (n)-[:HAS_ATTRIBUTE $rel_props ]->(a) CREATE (a)-[:HAS_VALUE $rel_props ]->(av) CREATE (a)-[:IS_PROTECTED $rel_props]->(is_protected_value) CREATE (a)-[:IS_VISIBLE $rel_props]->(is_visible_value) - """ % {"branch_filter": branch_filter, "node_kind": self.node_kind} + %(uuid_generation)s + FOREACH (i in CASE WHEN r2.status = "deleted" THEN [1] ELSE [] END | + SET r2.to = $current_time + ) + """ % { + "branch_filter": branch_filter, + "node_kind": self.node_kind, + "uuid_generation": db.render_uuid_generation(node_label="a", node_attr="uuid"), + } self.add_to_query(query) self.return_labels = ["n.uuid", "a.uuid"] - - self.add_to_query(db.render_uuid_generation(node_label="a", node_attr="uuid")) diff --git a/backend/infrahub/core/migrations/query/attribute_rename.py b/backend/infrahub/core/migrations/query/attribute_rename.py index e712ad7482..efca8fffbb 100644 --- a/backend/infrahub/core/migrations/query/attribute_rename.py +++ b/backend/infrahub/core/migrations/query/attribute_rename.py @@ -38,9 +38,16 @@ def __init__( def render_match(self) -> str: query = """ // Find all the active nodes - MATCH (node:Node) - WHERE ( "Profile%(node_kind)s" IN LABELS(node) OR "%(node_kind)s" IN LABELS(node) ) - AND exists((node)-[:HAS_ATTRIBUTE]-(:Attribute { name: $prev_attr.name })) + CALL { + MATCH (node:%(node_kind)s) + WHERE exists((node)-[:HAS_ATTRIBUTE]-(:Attribute { name: $prev_attr.name })) + RETURN node + UNION + MATCH (node:Profile%(node_kind)s) + WHERE exists((node)-[:HAS_ATTRIBUTE]-(:Attribute { name: $prev_attr.name })) + RETURN node + } + WITH node """ % {"node_kind": self.previous_attr.node_kind} return query diff --git a/backend/infrahub/core/migrations/query/node_duplicate.py b/backend/infrahub/core/migrations/query/node_duplicate.py index df8df51bf6..3dc03c25d4 100644 --- a/backend/infrahub/core/migrations/query/node_duplicate.py +++ b/backend/infrahub/core/migrations/query/node_duplicate.py @@ -19,10 +19,7 @@ class SchemaNodeInfo(BaseModel): namespace: str branch_support: str = BranchSupportType.AWARE.value labels: list[str] - - @property - def kind(self) -> str: - return self.namespace + self.name + kind: str class NodeDuplicateQuery(Query): 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/node_attribute_remove.py b/backend/infrahub/core/migrations/schema/node_attribute_remove.py index f73a79cffb..0e20cfdeae 100644 --- a/backend/infrahub/core/migrations/schema/node_attribute_remove.py +++ b/backend/infrahub/core/migrations/schema/node_attribute_remove.py @@ -59,8 +59,8 @@ def render_sub_query_per_rel_type(rel_type: str, rel_def: FieldInfo) -> str: query = """ // Find all the active nodes - MATCH (node:Node) - WHERE $node_kind IN LABELS(node) AND exists((node)-[:HAS_ATTRIBUTE]-(:Attribute { name: $attr_name })) + MATCH (node:%(node_kind)s) + WHERE exists((node)-[:HAS_ATTRIBUTE]-(:Attribute { name: $attr_name })) CALL { WITH node MATCH (root:Root)<-[r:IS_PART_OF]-(node) @@ -102,7 +102,11 @@ def render_sub_query_per_rel_type(rel_type: str, rel_def: FieldInfo) -> str: SET rb.to = $current_time ) RETURN DISTINCT active_attr - """ % {"branch_filter": branch_filter, "sub_query_all": sub_query_all} + """ % { + "branch_filter": branch_filter, + "sub_query_all": sub_query_all, + "node_kind": self.migration.new_schema.kind, + } self.add_to_query(query) diff --git a/backend/infrahub/core/migrations/schema/node_kind_update.py b/backend/infrahub/core/migrations/schema/node_kind_update.py index 4ad01dd4db..1df36ec90f 100644 --- a/backend/infrahub/core/migrations/schema/node_kind_update.py +++ b/backend/infrahub/core/migrations/schema/node_kind_update.py @@ -19,12 +19,14 @@ def __init__( namespace=migration.new_schema.namespace, branch_support=migration.new_schema.branch.value, labels=migration.new_schema.get_labels(), + kind=migration.new_schema.kind, ) previous_node = SchemaNodeInfo( name=migration.previous_schema.name, namespace=migration.previous_schema.namespace, branch_support=migration.previous_schema.branch.value, labels=migration.previous_schema.get_labels(), + kind=migration.previous_schema.kind, ) super().__init__(migration=migration, new_node=new_node, previous_node=previous_node, **kwargs) diff --git a/backend/infrahub/core/migrations/schema/node_remove.py b/backend/infrahub/core/migrations/schema/node_remove.py index 5867806390..223ea08bbd 100644 --- a/backend/infrahub/core/migrations/schema/node_remove.py +++ b/backend/infrahub/core/migrations/schema/node_remove.py @@ -36,7 +36,6 @@ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> No branch_filter, branch_params = self.branch.get_query_filter_path(at=self.at.to_string()) self.params.update(branch_params) - self.params["node_kind"] = self.migration.previous_schema.kind self.params["current_time"] = self.at.to_string() self.params["branch_name"] = self.branch.name @@ -52,8 +51,7 @@ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> No # ruff: noqa: E501 query = """ // Find all the active nodes - MATCH (node:Node) - WHERE $node_kind IN LABELS(node) + MATCH (node:%(node_kind)s) CALL { WITH node MATCH (root:Root)<-[r:IS_PART_OF]-(node) @@ -66,7 +64,11 @@ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> No WHERE rb.status = "active" %(node_remove_query)s RETURN DISTINCT active_node - """ % {"branch_filter": branch_filter, "node_remove_query": node_remove_query} + """ % { + "branch_filter": branch_filter, + "node_remove_query": node_remove_query, + "node_kind": self.migration.previous_schema.kind, + } self.add_to_query(query) def get_nbr_migrations_executed(self) -> int: 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..4d19d65679 --- /dev/null +++ b/backend/infrahub/core/migrations/schema/tasks.py @@ -0,0 +1,67 @@ +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 infrahub.workflows.utils import add_branch_tag + +from .models import SchemaApplyMigrationData # noqa: TCH001 + +if TYPE_CHECKING: + from infrahub.core.schema import MainSchemaTypes + + +@flow(name="schema-migrations-apply") +async def schema_apply_migrations(message: SchemaApplyMigrationData) -> list[str]: + service = services.service + await add_branch_tag(branch_name=message.branch.name) + + 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..92e65d2409 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): @@ -366,7 +366,7 @@ def _get_signature_field(cls, value: Any) -> list[bytes]: else: hashes.append(cls._get_hash_value(value)) - return hashes + return sorted(hashes) @property def _sorting_id(self) -> tuple[Any]: @@ -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..679a4ec240 100644 --- a/backend/infrahub/core/node/__init__.py +++ b/backend/infrahub/core/node/__init__.py @@ -1,10 +1,10 @@ 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 +from infrahub_sdk.uuidt import UUIDT from infrahub.core import registry from infrahub.core.constants import BranchSupportType, InfrahubKind, RelationshipCardinality @@ -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") @@ -501,6 +528,7 @@ async def to_graphql( fields: Optional[dict] = None, related_node_ids: Optional[set] = None, filter_sensitive: bool = False, + permissions: Optional[dict] = None, ) -> dict: """Generate GraphQL Payload for all attributes @@ -552,11 +580,11 @@ async def to_graphql( fields=fields.get(field_name), related_node_ids=related_node_ids, filter_sensitive=filter_sensitive, + permissions=permissions, ) else: response[field_name] = await field.to_graphql( - db=db, - filter_sensitive=filter_sensitive, + db=db, filter_sensitive=filter_sensitive, permissions=permissions ) return response 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/ipam.py b/backend/infrahub/core/node/ipam.py index 971bbed3ae..f605c83e23 100644 --- a/backend/infrahub/core/node/ipam.py +++ b/backend/infrahub/core/node/ipam.py @@ -19,6 +19,7 @@ async def to_graphql( fields: Optional[dict] = None, related_node_ids: Optional[set] = None, filter_sensitive: bool = False, + permissions: Optional[dict] = None, ) -> dict: response = await super().to_graphql( db, fields=fields, related_node_ids=related_node_ids, filter_sensitive=filter_sensitive diff --git a/backend/infrahub/core/node/permissions.py b/backend/infrahub/core/node/permissions.py new file mode 100644 index 0000000000..b4864e3bdc --- /dev/null +++ b/backend/infrahub/core/node/permissions.py @@ -0,0 +1,62 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Optional + +from infrahub.permissions.constants import PermissionDecisionFlag + +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, + permissions: Optional[dict] = None, + ) -> dict: + response = await super().to_graphql( + db, + fields=fields, + related_node_ids=related_node_ids, + filter_sensitive=filter_sensitive, + permissions=permissions, + ) + + if fields: + if "identifier" in fields: + decision = PermissionDecisionFlag(value=self.decision.value.value) # type: ignore[attr-defined] + response["identifier"] = {"value": f"global:{self.action.value}:{decision.name.lower()}"} # type: ignore[attr-defined,union-attr] + + 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, + permissions: Optional[dict] = None, + ) -> dict: + response = await super().to_graphql( + db, + fields=fields, + related_node_ids=related_node_ids, + filter_sensitive=filter_sensitive, + permissions=permissions, + ) + + if fields: + if "identifier" in fields: + decision = PermissionDecisionFlag(value=self.decision.value.value) # type: ignore[attr-defined] + response["identifier"] = { + "value": f"object:{self.namespace.value}:{self.name.value}:{self.action.value.value}:{decision.name.lower()}" # type: ignore[attr-defined,union-attr] + } + + return response diff --git a/backend/infrahub/core/node/standard.py b/backend/infrahub/core/node/standard.py index eaf762f852..a869690b5c 100644 --- a/backend/infrahub/core/node/standard.py +++ b/backend/infrahub/core/node/standard.py @@ -2,10 +2,10 @@ 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 +from infrahub_sdk.uuidt import UUIDT from pydantic import BaseModel from infrahub.core.constants import NULL_VALUE @@ -207,9 +207,11 @@ def to_db(self) -> dict[str, Any]: @classmethod async def get_list( - cls, db: InfrahubDatabase, limit: int = 1000, ids: Optional[list[str]] = None, **kwargs + cls, db: InfrahubDatabase, limit: int = 1000, ids: list[str] | None = None, name: str | None = None, **kwargs ) -> list[Self]: - query: Query = await StandardNodeGetListQuery.init(db=db, node_class=cls, ids=ids, limit=limit, **kwargs) + query: Query = await StandardNodeGetListQuery.init( + db=db, node_class=cls, ids=ids, node_name=name, limit=limit, **kwargs + ) await query.execute(db=db) return [cls.from_db(result.get("n")) for result in query.get_results()] diff --git a/backend/infrahub/core/protocols.py b/backend/infrahub/core/protocols.py index 3138cc6dc8..afb936d294 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): + description: StringOptional + identifier: StringOptional + roles: RelationshipManager + + class CoreCheck(CoreNode): name: StringOptional label: StringOptional @@ -127,6 +133,22 @@ class CoreGroup(CoreNode): children: RelationshipManager +class CoreMenu(CoreNode): + namespace: String + name: String + label: StringOptional + kind: StringOptional + path: StringOptional + description: StringOptional + icon: StringOptional + protected: Boolean + order_weight: Integer + required_permissions: ListAttributeOptional + section: Enum + parent: RelationshipManager + children: RelationshipManager + + class CoreProfile(CoreNode): profile_name: String profile_priority: IntegerOptional @@ -194,6 +216,16 @@ class CoreAccount(LineageOwner, LineageSource, CoreGenericAccount): pass +class CoreAccountGroup(LineageOwner, LineageSource, CoreGroup): + roles: RelationshipManager + + +class CoreAccountRole(CoreNode): + name: String + groups: RelationshipManager + permissions: RelationshipManager + + class CoreArtifact(CoreTaskTarget): name: String status: Enum @@ -311,6 +343,11 @@ class CoreGeneratorValidator(CoreValidator): definition: RelationshipManager +class CoreGlobalPermission(CoreBasePermission): + action: Dropdown + decision: Enum + + class CoreGraphQLQuery(CoreNode): name: String description: StringOptional @@ -344,6 +381,10 @@ class CoreIPPrefixPool(CoreResourcePool, LineageSource): ip_namespace: RelationshipManager +class CoreMenuItem(CoreMenu): + pass + + class CoreNumberPool(CoreResourcePool, LineageSource): node: String node_attribute: String @@ -351,6 +392,13 @@ class CoreNumberPool(CoreResourcePool, LineageSource): end_range: Integer +class CoreObjectPermission(CoreBasePermission): + namespace: String + name: String + action: Enum + decision: 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..10f970bfe1 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 @@ -46,15 +44,15 @@ async def transaction(self, name: Optional[str]) -> AsyncTransaction: ... async def close(self) -> None: ... async def execute_query( - self, query: str, params: Optional[dict[str, Any]] = None, name: Optional[str] = "undefined" + self, query: str, params: Optional[dict[str, Any]] = None, name: str = "undefined" ) -> list[Record]: ... async def execute_query_with_metadata( - self, query: str, params: Optional[dict[str, Any]] = None, name: Optional[str] = "undefined" + self, query: str, params: Optional[dict[str, Any]] = None, name: str = "undefined" ) -> tuple[list[Record], dict[str, Any]]: ... async def run_query( - self, query: str, params: Optional[dict[str, Any]] = None, name: Optional[str] = "undefined" + self, query: str, params: Optional[dict[str, Any]] = None, name: str = "undefined" ) -> AsyncResult: ... def render_list_comprehension(self, items: str, item_name: str) -> str: ... @@ -95,6 +93,7 @@ async def to_graphql( fields: Optional[dict] = None, related_node_ids: Optional[set] = None, filter_sensitive: bool = False, + permissions: Optional[dict] = None, ) -> dict: ... async def render_display_label(self, db: Optional[InfrahubDatabase] = None) -> str: ... async def from_graphql(self, data: dict, db: InfrahubDatabase) -> bool: ... diff --git a/backend/infrahub/core/query/__init__.py b/backend/infrahub/core/query/__init__.py index 516cba5951..e4b7190bd4 100644 --- a/backend/infrahub/core/query/__init__.py +++ b/backend/infrahub/core/query/__init__.py @@ -14,7 +14,6 @@ from infrahub import config from infrahub.core.constants import PermissionLevel from infrahub.core.timestamp import Timestamp -from infrahub.database.constants import DatabaseType, Neo4jRuntime from infrahub.exceptions import QueryError if TYPE_CHECKING: @@ -24,6 +23,7 @@ from infrahub.core.branch import Branch from infrahub.database import InfrahubDatabase + RETURN_TYPE = TypeVar("RETURN_TYPE") @@ -406,6 +406,11 @@ async def init( async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: raise NotImplementedError + def get_context(self) -> dict[str, str]: + """Provide additional context for this query, beyond the name. + Right now it's mainly used to add more labels to the metrics.""" + return {} + def add_to_query(self, query: Union[str, list[str]]) -> None: """Add a new section at the end of the query. @@ -518,9 +523,7 @@ def _get_params_for_neo4j_shell(self) -> str: return ":params { " + ", ".join(params) + " }" - async def execute( - self, db: InfrahubDatabase, profile: bool = False, runtime: Neo4jRuntime = Neo4jRuntime.DEFAULT - ) -> Self: + async def execute(self, db: InfrahubDatabase) -> Self: # Ensure all mandatory params have been provided # Ensure at least 1 return obj has been defined @@ -529,21 +532,17 @@ async def execute( query_str = self.get_query() - if profile: - query_str = "PROFILE\n" + query_str - - if runtime != Neo4jRuntime.DEFAULT and db.db_type == DatabaseType.NEO4J: - query_str = f"CYPHER runtime={runtime.value}\n" + query_str - if self.type == QueryType.READ: if self.limit or self.offset: - results = await db.execute_query(query=query_str, params=self.params, name=self.name) + results = await db.execute_query( + query=query_str, params=self.params, name=self.name, context=self.get_context() + ) else: results = await self.query_with_size_limit(db=db) elif self.type == QueryType.WRITE: results, metadata = await db.execute_query_with_metadata( - query=query_str, params=self.params, name=self.name + query=query_str, params=self.params, name=self.name, context=self.get_context() ) if "stats" in metadata: self.stats.add(metadata.get("stats")) @@ -568,6 +567,7 @@ async def query_with_size_limit(self, db: InfrahubDatabase) -> list[Record]: query=self.get_query(limit=query_limit, offset=offset), params=self.params, name=self.name, + context=self.get_context(), ) if "stats" in metadata: self.stats.add(metadata.get("stats")) diff --git a/backend/infrahub/core/query/attribute.py b/backend/infrahub/core/query/attribute.py index f85a508ebc..9dd050a885 100644 --- a/backend/infrahub/core/query/attribute.py +++ b/backend/infrahub/core/query/attribute.py @@ -18,15 +18,12 @@ class AttributeQuery(Query): def __init__( self, - attr: BaseAttribute = None, + attr: BaseAttribute, attr_id: Optional[str] = None, at: Optional[Union[Timestamp, str]] = None, branch: Optional[Branch] = None, - **kwargs, + **kwargs: Any, ): - if not attr and not attr_id: - raise ValueError("Either attr or attr_id must be defined, none provided") - self.attr = attr self.attr_id = attr_id or attr.db_id @@ -46,7 +43,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: dict[str, Any]) -> None: at = self.at or self.attr.at self.params["attr_uuid"] = self.attr.id @@ -84,8 +81,8 @@ class AttributeUpdateFlagQuery(AttributeQuery): def __init__( self, flag_name: str, - **kwargs, - ): + **kwargs: Any, + ) -> None: SUPPORTED_FLAGS = ["is_visible", "is_protected"] if flag_name not in SUPPORTED_FLAGS: @@ -95,7 +92,7 @@ def __init__( super().__init__(**kwargs) - async def query_init(self, db: InfrahubDatabase, **kwargs): + async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None: at = self.at or self.attr.at self.params["attr_uuid"] = self.attr.id @@ -125,14 +122,14 @@ def __init__( self, prop_name: str, prop_id: str, - **kwargs, + **kwargs: Any, ): self.prop_name = prop_name self.prop_id = prop_id super().__init__(**kwargs) - async def query_init(self, db: InfrahubDatabase, **kwargs): + async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None: at = self.at or self.attr.at self.params["attr_uuid"] = self.attr.id @@ -161,7 +158,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: Any) -> 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..8a26feb13c 100644 --- a/backend/infrahub/core/query/diff.py +++ b/backend/infrahub/core/query/diff.py @@ -1,8 +1,8 @@ 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.constants import GLOBAL_BRANCH_NAME, BranchSupportType from infrahub.core.query import Query, QueryResult, QueryType, sort_results_by_time from infrahub.core.timestamp import Timestamp @@ -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 " @@ -216,25 +216,31 @@ async def query_init(self, db: InfrahubDatabase, **kwargs): query = ( """ CALL { - MATCH p = ((src:Node)-[r1:IS_RELATED]-(rel:Relationship)-[r2:IS_RELATED]-(dst:Node)) + MATCH p = (src:Node)-[r1:IS_RELATED]-(rel:Relationship)-[r2:IS_RELATED]-(dst:Node) WHERE (rel.branch_support IN $branch_support AND %s r1.branch = r2.branch AND - (r1.to = r2.to OR (r1.to is NULL AND r2.to is NULL)) AND r1.from = r2.from AND r1.status = r2.status - AND all(r IN relationships(p) WHERE (r.branch IN $branch_names AND r.from >= $diff_from AND r.from <= $diff_to - AND ((r.to >= $diff_from AND r.to <= $diff_to) OR r.to is NULL)) + (r1.to = r2.to OR (r1.to is NULL AND r2.to is NULL)) + AND r1.from = r2.from AND r1.status = r2.status + AND r1.branch IN $branch_names + AND ( + (r1.from >= $diff_from AND r1.from <= $diff_to AND r1.to is NULL) + OR (r1.to >= $diff_from AND r1.to <= $diff_to) ) ) RETURN DISTINCT [rel.uuid, r1.branch] as identifier, rel, r1.branch as branch_name } CALL { WITH rel, branch_name - MATCH p = ((sn:Node)-[r1:IS_RELATED]-(rel:Relationship)-[r2:IS_RELATED]-(dn:Node)) + MATCH p = (sn:Node)-[r1:IS_RELATED]-(rel:Relationship)-[r2:IS_RELATED]-(dn:Node) WHERE (rel.branch_support IN $branch_support AND r1.branch = r2.branch AND - (r1.to = r2.to OR (r1.to is NULL AND r2.to is NULL)) AND r1.from = r2.from AND r1.status = r2.status - AND all(r IN relationships(p) WHERE (r.branch = branch_name AND r.from >= $diff_from AND r.from <= $diff_to - AND ((r.to >= $diff_from AND r.to <= $diff_to) OR r.to is NULL)) + (r1.to = r2.to OR (r1.to is NULL AND r2.to is NULL)) + AND r1.from = r2.from AND r1.status = r2.status + AND r1.branch = branch_name + AND ( + (r1.from >= $diff_from AND r1.from <= $diff_to AND r1.to is NULL) + OR (r1.to >= $diff_from AND r1.to <= $diff_to) ) + AND sn <> dn ) - AND sn <> dn RETURN rel as rel1, sn as sn1, dn as dn1, r1 as r11, r2 as r21 ORDER BY r1.branch_level DESC, r1.from DESC LIMIT 1 @@ -257,7 +263,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 ) @@ -266,8 +272,11 @@ async def query_init(self, db: InfrahubDatabase, **kwargs): query = """ CALL { MATCH (rel:Relationship)-[r3:IS_VISIBLE|IS_PROTECTED|HAS_SOURCE|HAS_OWNER]-() - WHERE (r3.branch IN $branch_names AND r3.from >= $diff_from AND r3.from <= $diff_to - AND ((r3.to >= $diff_from AND r3.to <= $diff_to ) OR r3.to is NULL)) + WHERE ( + r3.branch IN $branch_names + AND (r3.from >= $diff_from AND r3.from <= $diff_to AND r3.to is NULL) + OR (r3.to >= $diff_from AND r3.to <= $diff_to) + ) RETURN DISTINCT rel } CALL { @@ -281,11 +290,22 @@ async def query_init(self, db: InfrahubDatabase, **kwargs): LIMIT 1 } WITH rel1 as rel, sn1 as sn, dn1 as dn, r11 as r1, r21 as r2 - MATCH (rel:Relationship)-[r3:IS_VISIBLE|IS_PROTECTED|HAS_SOURCE|HAS_OWNER]-(rp) - WHERE ( - r3.branch IN $branch_names AND r3.from >= $diff_from AND r3.from <= $diff_to - AND ((r3.to >= $diff_from AND r3.to <= $diff_to) OR r3.to is NULL) - ) + CALL { + // ----------------------- + // group results to the latest entry for each edge type (IS_VISIBLE, etc.) + // ----------------------- + WITH rel + MATCH (rel:Relationship)-[r3:IS_VISIBLE|IS_PROTECTED|HAS_SOURCE|HAS_OWNER]->(prop) + WHERE ( + r3.branch IN $branch_names + AND (r3.from >= $diff_from AND r3.from <= $diff_to AND r3.to is NULL) + OR (r3.to >= $diff_from AND r3.to <= $diff_to) + ) + WITH r3, prop + ORDER BY r3.branch, type(r3), r3.from DESC + WITH r3.branch AS r3_branch, type(r3) AS type_r3, head(collect([r3, prop])) AS data + RETURN data[0] AS r3, data[1] AS rp + } """ % "\n AND ".join(rels_filter) self.add_to_query(query) @@ -307,7 +327,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 +378,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 +432,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 +484,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 +531,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,14 +539,15 @@ 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( { "base_branch_name": self.base_branch.name, "branch_name": self.branch.name, + "global_branch_name": GLOBAL_BRANCH_NAME, "branch_from_time": self.diff_branch_from_time.to_string(), "from_time": from_str, "to_time": self.diff_to.to_string(), @@ -634,30 +654,31 @@ async def query_init(self, db: InfrahubDatabase, **kwargs): MATCH (root:Root)<-[r_root:IS_PART_OF]-(p:Node)-[diff_rel:HAS_ATTRIBUTE {branch: $branch_name}]->(q:Attribute) // exclude attributes and relationships under added/removed nodes b/c they are covered above WHERE (node_field_specifiers_list IS NULL OR [p.uuid, q.name] IN node_field_specifiers_list) - AND r_root.branch IN [$branch_name, $base_branch_name] - AND r_root.from < from_time + AND r_root.branch IN [$branch_name, $base_branch_name, $global_branch_name] + AND (p.branch_support IN $branch_support OR q.branch_support IN $branch_support) + // if p has a different type of branch support and was addded within our timeframe + AND (r_root.from < from_time OR NOT (p.branch_support IN $branch_support)) AND r_root.status = "active" // get attributes and relationships added on the branch during the timeframe AND (from_time <= diff_rel.from < $to_time) AND (diff_rel.to IS NULL OR (from_time <= diff_rel.to < $to_time)) AND r_root.from <= diff_rel.from AND (r_root.to IS NULL OR r_root.to >= diff_rel.from) - AND (p.branch_support IN $branch_support OR q.branch_support IN $branch_support) RETURN root, r_root, p, diff_rel, q UNION ALL WITH node_field_specifiers_list, from_time MATCH (root:Root)<-[r_root:IS_PART_OF]-(p:Node)-[diff_rel:IS_RELATED {branch: $branch_name}]-(q:Relationship) // exclude attributes and relationships under added/removed nodes b/c they are covered above WHERE (node_field_specifiers_list IS NULL OR [p.uuid, q.name] IN node_field_specifiers_list) - AND r_root.branch IN [$branch_name, $base_branch_name] - AND r_root.from < from_time - AND r_root.status = "active" + AND r_root.branch IN [$branch_name, $base_branch_name, $global_branch_name] + AND (p.branch_support IN $branch_support OR q.branch_support IN $branch_support) + // if p has a different type of branch support and was addded within our timeframe + AND (r_root.from < from_time OR NOT (p.branch_support IN $branch_support)) // get attributes and relationships added on the branch during the timeframe AND (from_time <= diff_rel.from < $to_time) AND (diff_rel.to IS NULL OR (from_time <= diff_rel.to < $to_time)) AND r_root.from <= diff_rel.from AND (r_root.to IS NULL OR r_root.to >= diff_rel.from) - AND (p.branch_support IN $branch_support OR q.branch_support IN $branch_support) RETURN root, r_root, p, diff_rel, q } WITH root, r_root, p, diff_rel, q, from_time 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..6f12a96fa7 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) @@ -792,7 +792,6 @@ def _get_tracked_variables(self) -> list[str]: async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: self.order_by = [] - self.params["node_kind"] = self.schema.kind self.return_labels = ["n.uuid", "rb.branch", f"{db.get_id_function_name()}(rb) as rb_id"] where_clause_elements = [] @@ -803,8 +802,7 @@ async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: self.params.update(branch_params) query = """ - MATCH p = (n:Node) - WHERE $node_kind IN LABELS(n) + MATCH (n:%(node_kind)s) CALL { WITH n MATCH (root:Root)<-[r:IS_PART_OF]-(n) @@ -815,7 +813,7 @@ async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: } WITH n, r as rb WHERE rb.status = "active" - """ % {"branch_filter": branch_filter} + """ % {"branch_filter": branch_filter, "node_kind": self.schema.kind} self.add_to_query(query) use_simple = False if self.filters and "id" in self.filters: @@ -859,6 +857,7 @@ async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: self.order_by.append(far.final_value_query_variable) continue self.order_by.append(far.node_value_query_variable) + self.order_by.append("n.uuid") async def _add_node_filter_attributes( self, @@ -1108,7 +1107,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 +1137,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..a8842837b4 100644 --- a/backend/infrahub/core/query/relationship.py +++ b/backend/infrahub/core/query/relationship.py @@ -5,7 +5,7 @@ from dataclasses import dataclass from typing import TYPE_CHECKING, Generator, Optional, Union -from infrahub_sdk import UUIDT +from infrahub_sdk.uuidt import UUIDT from infrahub.core.constants import RelationshipDirection, RelationshipStatus from infrahub.core.query import Query, QueryType @@ -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")) @@ -128,15 +128,15 @@ class FullRelationshipIdentifier: class RelationshipQuery(Query): def __init__( self, - rel: Union[type[Relationship], Relationship] = None, + rel: Union[type[Relationship], Relationship] | None = None, rel_type: Optional[str] = None, - source: Node = None, - source_id: UUID = None, - destination: Node = None, - destination_id: UUID = None, - schema: RelationshipSchema = None, - branch: Branch = None, - at: Union[Timestamp, str] = None, + source: Node | None = None, + source_id: UUID | None = None, + destination: Node | None = None, + destination_id: UUID | None = None, + schema: RelationshipSchema | None = None, + branch: Branch | None = None, + at: Union[Timestamp, str] | None = None, **kwargs, ): if not source and not source_id: @@ -196,7 +196,7 @@ class RelationshipCreateQuery(RelationshipQuery): def __init__( self, destination: Node = None, - destination_id: UUID = None, + destination_id: UUID | None = None, **kwargs, ): if not destination and not destination_id: @@ -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 ) @@ -701,7 +701,7 @@ def get_peers(self) -> Generator[RelationshipPeerData, None, None]: rel_node_id=result.get("rl").get("uuid"), updated_at=rels[0]["from"], rels=[RelData.from_db(rel) for rel in rels], - branch=self.branch, + branch=self.branch.name, properties={}, ) @@ -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/query/resource_manager.py b/backend/infrahub/core/query/resource_manager.py index e5989c9ff3..806e273874 100644 --- a/backend/infrahub/core/query/resource_manager.py +++ b/backend/infrahub/core/query/resource_manager.py @@ -116,27 +116,28 @@ def __init__( super().__init__(**kwargs) # type: ignore[arg-type] async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None: - self.params["pool_id"] = self.pool.get_id() self.params["node_attribute"] = self.pool.node_attribute.value self.params["start_range"] = self.pool.start_range.value self.params["end_range"] = self.pool.end_range.value - self.params["time_at"] = self.at.to_string() - - def rel_filter(rel_name: str) -> str: - return f"{rel_name}.from <= $time_at AND ({rel_name}.to IS NULL OR {rel_name}.to >= $time_at)" + 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 = f""" - MATCH (n:%(node)s)-[ha:HAS_ATTRIBUTE]-(a:Attribute {{name: $node_attribute}})-[hv:HAS_VALUE]-(av:AttributeValue) + query = """ + MATCH (n:%(node)s)-[ha:HAS_ATTRIBUTE]-(a:Attribute {name: $node_attribute})-[hv:HAS_VALUE]-(av:AttributeValue) MATCH (a)-[hs:HAS_SOURCE]-(pool:%(number_pool_kind)s) WHERE av.value >= $start_range and av.value <= $end_range - AND ({rel_filter("ha")}) - AND ({rel_filter("hv")}) - AND ({rel_filter("hs")}) + AND all(r in [ha, hv, hs] WHERE (%(branch_filter)s)) + AND ha.status = "active" + AND hv.status = "active" + AND hs.status = "active" """ % { "node": self.pool.node.value, "number_pool_kind": InfrahubKind.NUMBERPOOL, + "branch_filter": branch_filter, } self.add_to_query(query) @@ -207,15 +208,27 @@ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> No ) self.params.update(branch_params) + self.params["attribute_name"] = self.pool.node_attribute.value query = """ - MATCH (pool:%(number_pool)s { uuid: $pool_id })-[r:IS_RESERVED]->(av:AttributeValue ) - WHERE - toInteger(av.value) >= $start_range and toInteger(av.value) <= $end_range - AND - %(branch_filter)s - """ % {"branch_filter": branch_filter, "number_pool": InfrahubKind.NUMBERPOOL} - + MATCH (pool:%(number_pool)s { uuid: $pool_id }) + CALL { + WITH pool + MATCH (pool)-[res:IS_RESERVED]->(av:AttributeValue)<-[hv:HAS_VALUE]-(attr:Attribute) + WHERE + attr.name = $attribute_name + AND + toInteger(av.value) >= $start_range and toInteger(av.value) <= $end_range + AND + all(r in [res, hv] WHERE (%(branch_filter)s)) + RETURN av, (res.status = "active" AND hv.status = "active") AS is_active + } + WITH av, is_active + WHERE is_active = TRUE + """ % { + "branch_filter": branch_filter, + "number_pool": InfrahubKind.NUMBERPOOL, + } self.add_to_query(query) self.return_labels = ["av.value"] self.order_by = ["av.value"] diff --git a/backend/infrahub/core/query/standard_node.py b/backend/infrahub/core/query/standard_node.py index e36d4d5ac0..e60264dc29 100644 --- a/backend/infrahub/core/query/standard_node.py +++ b/backend/infrahub/core/query/standard_node.py @@ -126,10 +126,10 @@ class StandardNodeGetListQuery(Query): type: QueryType = QueryType.WRITE def __init__( - self, node_class: StandardNode, ids: Optional[list[str]] = None, name: Optional[str] = None, **kwargs: Any + self, node_class: StandardNode, ids: Optional[list[str]] = None, node_name: Optional[str] = None, **kwargs: Any ) -> None: self.ids = ids - self.name = name + self.node_name = node_name self.node_class = node_class super().__init__(**kwargs) @@ -139,9 +139,9 @@ async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: if self.ids: filters.append("n.uuid in $ids_value") self.params["ids_value"] = self.ids - if self.name: + if self.node_name: filters.append("n.name = $name") - self.params["name"] = self.name + self.params["name"] = self.node_name where = "" if filters: diff --git a/backend/infrahub/core/registry.py b/backend/infrahub/core/registry.py index 774e9af466..f6bcbd4635 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,10 +35,10 @@ 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) + input_type: dict[str, type[BaseAttributeCreate | BaseAttributeUpdate]] = field(default_factory=dict) account: dict = field(default_factory=dict) account_id: dict = field(default_factory=dict) node_group: dict = 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..9c55197724 100644 --- a/backend/infrahub/core/relationship/model.py +++ b/backend/infrahub/core/relationship/model.py @@ -17,8 +17,8 @@ overload, ) -from infrahub_sdk import UUIDT from infrahub_sdk.utils import intersection, is_valid_uuid +from infrahub_sdk.uuidt import UUIDT from pydantic import BaseModel, Field from infrahub.core import registry @@ -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) @@ -1081,13 +1081,18 @@ async def remove_in_db( remove_at = Timestamp(at) branch = self.get_branch_based_on_support_type() - # when we remove a relationship we need to : # - Update the existing relationship if we are on the same branch - # - Create a new rel of type DELETED in the right branch rel_ids_per_branch = peer_data.rel_ids_per_branch() if branch.name in rel_ids_per_branch: await update_relationships_to([str(ri) for ri in rel_ids_per_branch[self.branch.name]], to=remove_at, db=db) + # - Create a new rel of type DELETED if the existing relationship is on a different branch + rel_branches: set[str] = set() + if peer_data.rels: + rel_branches = {r.branch for r in peer_data.rels} + if rel_branches == {peer_data.branch}: + return + query = await RelationshipDataDeleteQuery.init( db=db, rel=self.rel_class, 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..8db9acb0fb 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,49 @@ ], } +# ----------------------------------------------- +# Menu Items +# ----------------------------------------------- +generic_menu_item: dict[str, Any] = { + "name": "Menu", + "namespace": "Core", + "include_in_menu": False, + "description": "Element of the Menu", + "label": "Menu", + "hierarchical": True, + "human_friendly_id": ["namespace__value", "name__value"], + "display_labels": ["label__value"], + "generate_profile": False, + "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": "kind", "kind": "Text", "optional": True, "order_weight": 2500}, + {"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": "required_permissions", "kind": "List", "optional": True, "order_weight": 7000}, + { + "name": "section", + "kind": "Text", + "enum": ["object", "internal"], + "default_value": "object", + "order_weight": 8000, + }, + ], +} + +menu_item: dict[str, Any] = { + "name": "MenuItem", + "namespace": "Core", + "include_in_menu": False, + "description": "Menu Item", + "label": "Menu Item", + "inherit_from": ["CoreMenu"], + "generate_profile": False, +} core_models: dict[str, Any] = { "generics": [ @@ -877,7 +924,36 @@ }, ], "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": "description", "kind": "Text", "optional": True}, + { + "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 +976,10 @@ {"name": "description", "kind": "Text", "optional": True, "order_weight": 3000}, ], }, + generic_menu_item, ], "nodes": [ + menu_item, { "name": "StandardGroup", "namespace": "Core", @@ -2080,5 +2158,124 @@ }, ], }, + { + "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": ["action__value", "decision__value"], + "display_labels": ["action__value", "decision__value"], + "human_friendly_id": ["action__value", "decision__value"], + "generate_profile": False, + "inherit_from": [InfrahubKind.BASEPERMISSION], + "branch": BranchSupportType.AGNOSTIC.value, + "attributes": [ + { + "name": "action", + "kind": "Dropdown", + "choices": [{"name": permission.value} for permission in GlobalPermissions], + "order_weight": 2000, + }, + { + "name": "decision", + "kind": "Number", + "enum": PermissionDecision.available_types(), + "default_value": PermissionDecision.ALLOW_ALL.value, + "order_weight": 3000, + "description": "Decide to deny or allow the action at a global level", + }, + ], + }, + { + "name": "ObjectPermission", + "namespace": "Core", + "description": "A permission that grants rights to perform actions on objects", + "label": "Object permission", + "include_in_menu": False, + "order_by": ["namespace__value", "name__value", "action__value", "decision__value"], + "display_labels": ["namespace__value", "name__value", "action__value", "decision__value"], + "human_friendly_id": ["namespace__value", "name__value", "action__value", "decision__value"], + "uniqueness_constraints": [["namespace__value", "name__value", "action__value", "decision__value"]], + "generate_profile": False, + "inherit_from": [InfrahubKind.BASEPERMISSION], + "attributes": [ + {"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": "decision", + "kind": "Number", + "enum": PermissionDecision.available_types(), + "default_value": PermissionDecision.ALLOW_ALL.value, + "order_weight": 5000, + "description": ( + "Decide to deny or allow the action. If allowed, it can be configured for the default branch, any other branches or all " + "branches" + ), + }, + ], + }, + { + "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"], + "human_friendly_id": ["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"], + "human_friendly_id": ["name__value"], + "generate_profile": False, + "inherit_from": [InfrahubKind.LINEAGEOWNER, InfrahubKind.LINEAGESOURCE, 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..abafd790bf --- /dev/null +++ b/backend/infrahub/core/schema/manager.py @@ -0,0 +1,713 @@ +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, + ProfileSchema, + 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_profile_schema( + self, name: str, branch: Optional[Union[Branch, str]] = None, duplicate: bool = True + ) -> ProfileSchema: + schema = self.get(name=name, branch=branch, duplicate=duplicate) + if isinstance(schema, ProfileSchema): + return schema + + raise ValueError("The selected node is not of type ProfileSchema") + + 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() + + diff_attributes = diff.changed.get("attributes") + diff_relationships = diff.changed.get("relationships") + attrs_rels_to_update: set[str] = set() + if diff_attributes: + attrs_rels_to_update.update(set(diff_attributes.added.keys())) + attrs_rels_to_update.update(set(diff_attributes.changed.keys())) + attrs_rels_to_update.update(set(diff_attributes.removed.keys())) + if diff_relationships: + attrs_rels_to_update.update(set(diff_relationships.added.keys())) + attrs_rels_to_update.update(set(diff_relationships.changed.keys())) + attrs_rels_to_update.update(set(diff_relationships.removed.keys())) + + item_ids = set() + item_names = set() + for field in node.local_attributes + node.local_relationships: + if field.name not in attrs_rels_to_update: + continue + if field.id: + item_ids.add(field.id) + item_names.add(field.name) + missing_field_names = list(attrs_rels_to_update - item_names) + + items: dict[str, Node] = {} + if item_ids: + items = await self.get_many( + ids=list(item_ids), + db=db, + branch=branch, + include_owner=True, + include_source=True, + ) + if missing_field_names: + missing_attrs = await self.query( + db=db, + branch=branch, + schema=attribute_schema, + filters={"name__values": missing_field_names, "node__id": node.id}, + include_owner=True, + include_source=True, + ) + missing_rels = await self.query( + db=db, + branch=branch, + schema=relationship_schema, + filters={"name__values": missing_field_names, "node__id": node.id}, + include_owner=True, + include_source=True, + ) + items.update({field.id: field for field in missing_attrs + missing_rels}) + + if diff_attributes: + await obj.attributes.update(db=db, data=[item.id for item in node.local_attributes if item.id]) + + if diff_relationships: + await obj.relationships.update(db=db, data=[item.id for item in node.local_relationships if item.id]) + + await obj.save(db=db) + + if diff_attributes: + for item in node.local_attributes: + if item.name in diff_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_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_attributes.removed and item.id and item.id in items: + await items[item.id].delete(db=db) + elif ( + (item.name in diff_attributes.removed or item.name in diff_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 diff_relationships: + for item in node.local_relationships: + if item.name in diff_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_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_relationships.removed and item.id and item.id in items: + await items[item.id].delete(db=db) + elif ( + (item.name in diff_relationships.removed or item.name in diff_relationships.changed) + and item.id + and item.id not in items + ): + raise ValueError(f"Unable to find a relationship {item.name!r} to update or delete") + + field_names_to_remove = [] + if diff_attributes and diff_attributes.removed: + attr_names_to_remove = set(diff_attributes.removed.keys()) - set(node.local_attribute_names) + field_names_to_remove.extend(list(attr_names_to_remove)) + if diff_relationships and diff_relationships.removed: + rel_names_to_remove = set(diff_relationships.removed.keys()) - set(node.local_relationship_names) + field_names_to_remove.extend(list(rel_names_to_remove)) + if field_names_to_remove: + for field_schema in items.values(): + if field_schema.name.value in field_names_to_remove: + await field_schema.delete(db=db) + + # 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 + ) + 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 65% rename from backend/infrahub/core/schema_manager.py rename to backend/infrahub/core/schema/schema_branch.py index eac391f9e9..d1a2127212 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,66 +41,51 @@ 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 from infrahub.log import get_logger from infrahub.types import ATTRIBUTE_TYPES 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] = {} - self._graphql_schema: Optional[GraphQLSchema] = None - self._graphql_manager: Optional[GraphQLSchemaManager] = None if data: self.nodes = data.get("nodes", {}) 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()) @@ -141,7 +118,7 @@ def all_names(self) -> list[str]: def get_hash(self) -> str: """Calculate the hash for this objects based on the content of nodes and generics. - Since the object themselves are considered immuable we just need to use the hash from each object to calculate the global hash. + Since the object themselves are considered immutable we just need to use the hash from each object to calculate the global hash. """ md5hash = hashlib.md5(usedforsecurity=False) for key, value in sorted(tuple(self.nodes.items()) + tuple(self.generics.items())): @@ -163,30 +140,26 @@ 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): - self._graphql_manager = None - self._graphql_schema = None + @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": {}} - def get_graphql_manager(self) -> GraphQLSchemaManager: - if not self._graphql_manager: - self._graphql_manager = GraphQLSchemaManager(schema=self) - return self._graphql_manager + 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 - def get_graphql_schema( - self, - include_query: bool = True, - include_mutation: bool = True, - include_subscription: bool = True, - include_types: bool = True, - ) -> GraphQLSchema: - if not self._graphql_schema: - self._graphql_schema = self.get_graphql_manager().generate( - include_query=include_query, - include_mutation=include_mutation, - include_subscription=include_subscription, - include_types=include_types, - ) - return self._graphql_schema + cache[node_hash] = node + + return cls(cache=cache, data=nodes) def diff(self, other: SchemaBranch) -> SchemaDiff: # Identify the nodes or generics that have been added or removed @@ -484,7 +457,6 @@ def process_pre_validation(self) -> None: def process_validate(self) -> None: self.validate_names() - self.validate_menu_placements() self.validate_kinds() self.validate_default_values() self.validate_count_against_cardinality() @@ -499,9 +471,9 @@ def process_validate(self) -> None: self.validate_required_relationships() def process_post_validation(self) -> None: + self.cleanup_inherited_elements() self.add_groups() self.add_hierarchy() - self.process_filters() self.generate_weight() self.process_labels() self.process_dropdowns() @@ -659,7 +631,13 @@ def sync_uniqueness_constraints_and_unique_attributes(self) -> None: if len(constraint_paths) > 1: continue constraint_path = constraint_paths[0] - schema_attribute_path = node_schema.parse_schema_path(path=constraint_path, schema=self) + try: + schema_attribute_path = node_schema.parse_schema_path(path=constraint_path, schema=self) + except AttributePathParsingError as exc: + raise ValueError( + f"{node_schema.kind}: Requested unique constraint not found within node. (`{constraint_path}`)" + ) from exc + if ( schema_attribute_path.is_type_attribute and schema_attribute_path.attribute_property_name == "value" @@ -756,7 +734,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: @@ -898,28 +876,6 @@ def validate_names(self) -> None: ): raise ValueError(f"{node.kind}: {rel.name} isn't allowed as a relationship name.") - def validate_menu_placements(self) -> None: - menu_placements: dict[str, str] = {} - - for name in list(self.nodes.keys()) + list(self.generics.keys()): - node = self.get(name=name, duplicate=False) - if node.menu_placement: - try: - placement_node = self.get(name=node.menu_placement, duplicate=False) - except SchemaNotFoundError as exc: - raise SchemaNotFoundError( - branch_name=self.name, - identifier=node.menu_placement, - message=f"{node.kind} refers to an invalid menu placement node: {node.menu_placement}.", - ) from exc - if node == placement_node: - raise ValueError(f"{node.kind}: cannot be placed under itself in the menu") from None - - if menu_placements.get(placement_node.kind) == node.kind: - raise ValueError(f"{node.kind}: cyclic menu placement with {placement_node.kind}") from None - - menu_placements[node.kind] = placement_node.kind - def validate_kinds(self) -> None: for name in list(self.nodes.keys()): node = self.get_node(name=name, duplicate=False) @@ -1253,22 +1209,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 @@ -1334,6 +1274,50 @@ def generate_weight(self) -> None: self.set(name=name, schema=node) + def cleanup_inherited_elements(self) -> None: + # pylint: disable=too-many-branches + for name in self.node_names: + node = self.get_node(name=name, duplicate=False) + + attributes_to_delete = [] + relationships_to_delete = [] + + inherited_attribute_names = set(node.attribute_names) - set(node.local_attribute_names) + inherited_relationship_names = set(node.relationship_names) - set(node.local_relationship_names) + for item_name in inherited_attribute_names: + found = False + for generic_name in node.inherit_from: + generic = self.get_generic(name=generic_name, duplicate=False) + if item_name in generic.attribute_names: + attr = generic.get_attribute(name=item_name) + if attr.state != HashableModelState.ABSENT: + found = True + if not found: + attributes_to_delete.append(item_name) + + for item_name in inherited_relationship_names: + found = False + for generic_name in node.inherit_from: + generic = self.get_generic(name=generic_name, duplicate=False) + if item_name in generic.relationship_names: + rel = generic.get_relationship(name=item_name) + if rel.state != HashableModelState.ABSENT: + found = True + if not found: + relationships_to_delete.append(item_name) + + # If there is either an attribute or a relationship to delete + # We clone the node and we set the attribute / relationship as ABSENT + if attributes_to_delete or relationships_to_delete: + node_copy = self.get_node(name=name, duplicate=True) + for item_name in attributes_to_delete: + attr = node_copy.get_attribute(name=item_name) + attr.state = HashableModelState.ABSENT + for item_name in relationships_to_delete: + rel = node_copy.get_relationship(name=item_name) + rel.state = HashableModelState.ABSENT + self.set(name=name, schema=node_copy) + def add_groups(self) -> None: if not self.has(name=InfrahubKind.GENERICGROUP): return @@ -1601,709 +1585,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/timestamp.py b/backend/infrahub/core/timestamp.py index 1eb5047a4e..43fc151f15 100644 --- a/backend/infrahub/core/timestamp.py +++ b/backend/infrahub/core/timestamp.py @@ -2,7 +2,7 @@ from typing import TYPE_CHECKING, Any -from infrahub_sdk import Timestamp as BaseTimestamp +from infrahub_sdk.timestamp import Timestamp as BaseTimestamp if TYPE_CHECKING: from pendulum.datetime import DateTime diff --git a/backend/infrahub/core/utils.py b/backend/infrahub/core/utils.py index 8befab1a6e..00743d7506 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 "" @@ -131,12 +132,10 @@ async def count_relationships(db: InfrahubDatabase, label: Optional[str] = None) async def get_nodes(db: InfrahubDatabase, label: str) -> list[Neo4jNode]: """Return theall nodes of a given label in the database.""" query = """ - MATCH (node) - WHERE $label IN LABELS(node) + MATCH (node:%(node_kind)s) RETURN node - """ - params: dict = {"label": label} - results = await db.execute_query(query=query, params=params, name="get_nodes") + """ % {"node_kind": label} + results = await db.execute_query(query=query, name="get_nodes") return [result[0] for result in results] @@ -154,7 +153,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 +209,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 +253,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/choices.py b/backend/infrahub/core/validators/attribute/choices.py index b269723aac..3f72c9617a 100644 --- a/backend/infrahub/core/validators/attribute/choices.py +++ b/backend/infrahub/core/validators/attribute/choices.py @@ -25,14 +25,12 @@ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> No branch_filter, branch_params = self.branch.get_query_filter_path(at=self.at.to_string()) self.params.update(branch_params) - self.params["node_kind"] = self.node_schema.kind self.params["attr_name"] = self.attribute_schema.name self.params["allowed_values"] = [choice.name for choice in self.attribute_schema.choices] self.params["null_value"] = NULL_VALUE query = """ - MATCH p = (n:Node) - WHERE $node_kind IN LABELS(n) + MATCH p = (n:%(node_kind)s) CALL { WITH n MATCH path = (root:Root)<-[rr:IS_PART_OF]-(n)-[ra:HAS_ATTRIBUTE]-(:Attribute { name: $attr_name } )-[rv:HAS_VALUE]-(av:AttributeValue) @@ -50,7 +48,7 @@ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> No AND attribute_value IS NOT NULL AND attribute_value <> $null_value AND NOT (attribute_value IN $allowed_values) - """ % {"branch_filter": branch_filter} + """ % {"branch_filter": branch_filter, "node_kind": self.node_schema.kind} self.add_to_query(query) self.return_labels = ["node.uuid", "attribute_value", "value_relationship"] diff --git a/backend/infrahub/core/validators/attribute/enum.py b/backend/infrahub/core/validators/attribute/enum.py index 139fba2c26..8736d2b2b3 100644 --- a/backend/infrahub/core/validators/attribute/enum.py +++ b/backend/infrahub/core/validators/attribute/enum.py @@ -25,13 +25,11 @@ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> No branch_filter, branch_params = self.branch.get_query_filter_path(at=self.at.to_string()) self.params.update(branch_params) - self.params["node_kind"] = self.node_schema.kind self.params["attr_name"] = self.attribute_schema.name self.params["allowed_values"] = self.attribute_schema.enum self.params["null_value"] = NULL_VALUE query = """ - MATCH p = (n:Node) - WHERE $node_kind IN LABELS(n) + MATCH (n:%(node_kind)s) CALL { WITH n MATCH path = (root:Root)<-[rr:IS_PART_OF]-(n)-[ra:HAS_ATTRIBUTE]-(:Attribute { name: $attr_name } )-[rv:HAS_VALUE]-(av:AttributeValue) @@ -49,7 +47,7 @@ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> No AND attribute_value IS NOT NULL AND attribute_value <> $null_value AND NOT (attribute_value IN $allowed_values) - """ % {"branch_filter": branch_filter} + """ % {"branch_filter": branch_filter, "node_kind": self.node_schema.kind} self.add_to_query(query) self.return_labels = ["node.uuid", "attribute_value", "value_relationship"] diff --git a/backend/infrahub/core/validators/attribute/kind.py b/backend/infrahub/core/validators/attribute/kind.py index d00bc2f22a..d12366ec6f 100644 --- a/backend/infrahub/core/validators/attribute/kind.py +++ b/backend/infrahub/core/validators/attribute/kind.py @@ -32,13 +32,11 @@ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> No branch_filter, branch_params = self.branch.get_query_filter_path(at=self.at.to_string()) self.params.update(branch_params) - self.params["node_kind"] = self.node_schema.kind self.params["attr_name"] = self.attribute_schema.name self.params["null_value"] = NULL_VALUE query = """ - MATCH p = (n:Node) - WHERE $node_kind IN LABELS(n) + MATCH p = (n:%(node_kind)s) CALL { WITH n MATCH path = (root:Root)<-[rr:IS_PART_OF]-(n)-[ra:HAS_ATTRIBUTE]-(:Attribute { name: $attr_name } )-[rv:HAS_VALUE]-(av:AttributeValue) @@ -55,7 +53,7 @@ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> No WHERE all(r in relationships(full_path) WHERE r.status = "active") AND attribute_value IS NOT NULL AND attribute_value <> $null_value - """ % {"branch_filter": branch_filter} + """ % {"branch_filter": branch_filter, "node_kind": self.node_schema.kind} self.add_to_query(query) self.return_labels = ["node.uuid", "attribute_value", "value_relationship.branch as value_branch"] diff --git a/backend/infrahub/core/validators/attribute/length.py b/backend/infrahub/core/validators/attribute/length.py index 822b0d4dd9..3b4526e4e6 100644 --- a/backend/infrahub/core/validators/attribute/length.py +++ b/backend/infrahub/core/validators/attribute/length.py @@ -22,14 +22,12 @@ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> No branch_filter, branch_params = self.branch.get_query_filter_path(at=self.at.to_string()) self.params.update(branch_params) - self.params["node_kind"] = self.node_schema.kind self.params["attr_name"] = self.attribute_schema.name self.params["min_length"] = self.attribute_schema.min_length self.params["max_length"] = self.attribute_schema.max_length query = """ - MATCH p = (n:Node) - WHERE $node_kind IN LABELS(n) + MATCH (n:%(node_kind)s) CALL { WITH n MATCH path = (root:Root)<-[rr:IS_PART_OF]-(n)-[ra:HAS_ATTRIBUTE]-(:Attribute { name: $attr_name } )-[rv:HAS_VALUE]-(av:AttributeValue) @@ -48,7 +46,7 @@ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> No (toInteger($min_length) IS NOT NULL AND size(attribute_value) < toInteger($min_length)) OR (toInteger($max_length) IS NOT NULL AND size(attribute_value) > toInteger($max_length)) ) - """ % {"branch_filter": branch_filter} + """ % {"branch_filter": branch_filter, "node_kind": self.node_schema.kind} self.add_to_query(query) self.return_labels = ["node.uuid", "value_relationship", "attribute_value"] diff --git a/backend/infrahub/core/validators/attribute/optional.py b/backend/infrahub/core/validators/attribute/optional.py index 03670a42c3..5c0c46461a 100644 --- a/backend/infrahub/core/validators/attribute/optional.py +++ b/backend/infrahub/core/validators/attribute/optional.py @@ -22,13 +22,11 @@ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> No branch_filter, branch_params = self.branch.get_query_filter_path(at=self.at.to_string()) self.params.update(branch_params) - self.params["node_kind"] = self.node_schema.kind self.params["attr_name"] = self.attribute_schema.name self.params["null_value"] = NULL_VALUE query = """ - MATCH p = (n:Node) - WHERE $node_kind IN LABELS(n) + MATCH (n:%(node_kind)s) CALL { WITH n MATCH path = (root:Root)<-[rr:IS_PART_OF]-(n)-[ra:HAS_ATTRIBUTE]-(:Attribute { name: $attr_name } )-[rv:HAS_VALUE]-(av:AttributeValue) @@ -44,7 +42,7 @@ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> No WITH full_path, node, attribute_value, value_relationship WHERE all(r in relationships(full_path) WHERE r.status = "active") AND (attribute_value IS NULL OR attribute_value = $null_value) - """ % {"branch_filter": branch_filter} + """ % {"branch_filter": branch_filter, "node_kind": self.node_schema.kind} self.add_to_query(query) self.return_labels = ["node.uuid", "value_relationship"] diff --git a/backend/infrahub/core/validators/attribute/regex.py b/backend/infrahub/core/validators/attribute/regex.py index 008409050b..282b158b35 100644 --- a/backend/infrahub/core/validators/attribute/regex.py +++ b/backend/infrahub/core/validators/attribute/regex.py @@ -22,13 +22,11 @@ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> No branch_filter, branch_params = self.branch.get_query_filter_path(at=self.at.to_string()) self.params.update(branch_params) - self.params["node_kind"] = self.node_schema.kind self.params["attr_name"] = self.attribute_schema.name self.params["attr_value_regex"] = self.attribute_schema.regex self.params["null_value"] = NULL_VALUE query = """ - MATCH p = (n:Node) - WHERE $node_kind IN LABELS(n) + MATCH p = (n:%(node_kind)s) CALL { WITH n MATCH path = (root:Root)<-[rr:IS_PART_OF]-(n)-[ra:HAS_ATTRIBUTE]-(:Attribute { name: $attr_name } )-[rv:HAS_VALUE]-(av:AttributeValue) @@ -45,7 +43,7 @@ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> No WHERE all(r in relationships(full_path) WHERE r.status = "active") AND attribute_value <> $null_value AND NOT attribute_value =~ $attr_value_regex - """ % {"branch_filter": branch_filter} + """ % {"branch_filter": branch_filter, "node_kind": self.node_schema.kind} self.add_to_query(query) self.return_labels = ["node.uuid", "attribute_value", "value_relationship"] 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 4e60af76ca..82022d2d35 100644 --- a/backend/infrahub/core/validators/determiner.py +++ b/backend/infrahub/core/validators/determiner.py @@ -1,16 +1,13 @@ -from collections import defaultdict from typing import Union -from infrahub_sdk.diff import NodeDiff - from infrahub.core.constants import RelationshipKind, SchemaPathType from infrahub.core.constants.schema import UpdateSupport -from infrahub.core.diff.model.diff import DiffElementType +from infrahub.core.diff.model.path import NodeDiffFieldSummary from infrahub.core.models import SchemaUpdateConstraintInfo 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,45 +15,32 @@ 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]]] = {} - self._relationship_element_map: dict[str, dict[str, list[NodeDiff]]] = {} + self._node_kinds: set[str] = set() + self._attribute_element_map: dict[str, set[str]] = {} + self._relationship_element_map: dict[str, set[str]] = {} - def _index_node_diffs(self, node_diffs: list[NodeDiff]) -> None: + def _index_node_diffs(self, node_diffs: list[NodeDiffFieldSummary]) -> None: for node_diff in node_diffs: - node_kind = node_diff["kind"] - self._node_diffs_by_kind[node_kind].append(node_diff) - if node_kind not in self._attribute_element_map: - self._attribute_element_map[node_kind] = {} - for element in node_diff["elements"]: - element_name = element["name"] - element_type = element["element_type"] - if element_type.lower() in ( - DiffElementType.RELATIONSHIP_MANY.value.lower(), - DiffElementType.RELATIONSHIP_ONE.value.lower(), - ): - if node_kind not in self._relationship_element_map: - self._relationship_element_map[node_kind] = {} - if element_name not in self._relationship_element_map[node_kind]: - self._relationship_element_map[node_kind][element_name] = [] - self._relationship_element_map[node_kind][element_name].append(element) - elif element_type.lower() in (DiffElementType.ATTRIBUTE.value.lower(),): - if node_kind not in self._attribute_element_map: - self._attribute_element_map[node_kind] = {} - if element_name not in self._attribute_element_map[node_kind]: - self._attribute_element_map[node_kind][element_name] = [] - self._attribute_element_map[node_kind][element_name].append(element) - - def _get_attribute_diffs(self, kind: str, name: str) -> list[NodeDiff]: - return self._attribute_element_map.get(kind, {}).get(name, []) - - def _get_relationship_diffs(self, kind: str, name: str) -> list[NodeDiff]: - return self._relationship_element_map.get(kind, {}).get(name, []) + self._node_kinds.add(node_diff.kind) + if node_diff.kind not in self._attribute_element_map: + self._attribute_element_map[node_diff.kind] = set() + for attribute_name in node_diff.attribute_names: + self._attribute_element_map[node_diff.kind].add(attribute_name) + if node_diff.kind not in self._relationship_element_map: + self._relationship_element_map[node_diff.kind] = set() + for relationship_name in node_diff.relationship_names: + self._relationship_element_map[node_diff.kind].add(relationship_name) + + def _has_attribute_diff(self, kind: str, name: str) -> bool: + return name in self._attribute_element_map.get(kind, set()) + + def _has_relationship_diff(self, kind: str, name: str) -> bool: + return name in self._relationship_element_map.get(kind, set()) async def get_constraints( - self, node_diffs: list[NodeDiff], filter_invalid: bool = True + self, node_diffs: list[NodeDiffFieldSummary], filter_invalid: bool = True ) -> list[SchemaUpdateConstraintInfo]: self._index_node_diffs(node_diffs) constraints: list[SchemaUpdateConstraintInfo] = [] @@ -65,7 +49,7 @@ async def get_constraints( constraints.extend(await self._get_all_property_constraints()) - for kind in self._node_diffs_by_kind.keys(): + for kind in self._node_kinds: schema = self.schema_branch.get(name=kind, duplicate=False) constraints.extend(await self._get_constraints_for_one_schema(schema)) @@ -132,11 +116,9 @@ async def _get_attribute_constraints_for_one_schema( ) -> list[SchemaUpdateConstraintInfo]: constraints: list[SchemaUpdateConstraintInfo] = [] for field_name in schema.attribute_names: - node_diffs_for_attribute = self._get_attribute_diffs(kind=schema.kind, name=field_name) - if not node_diffs_for_attribute: - continue - field = schema.get_attribute(field_name) - constraints.extend(await self._get_constraints_for_one_field(schema=schema, field=field)) + if self._has_attribute_diff(kind=schema.kind, name=field_name): + field = schema.get_attribute(field_name) + constraints.extend(await self._get_constraints_for_one_field(schema=schema, field=field)) return constraints async def _get_relationship_constraints_for_one_schema( @@ -144,11 +126,9 @@ async def _get_relationship_constraints_for_one_schema( ) -> list[SchemaUpdateConstraintInfo]: constraints: list[SchemaUpdateConstraintInfo] = [] for field_name in schema.relationship_names: - node_diffs_for_relationship = self._get_relationship_diffs(kind=schema.kind, name=field_name) - if not node_diffs_for_relationship: - continue - field = schema.get_relationship(field_name) - constraints.extend(await self._get_constraints_for_one_field(schema=schema, field=field)) + if self._has_relationship_diff(kind=schema.kind, name=field_name): + field = schema.get_relationship(field_name) + constraints.extend(await self._get_constraints_for_one_field(schema=schema, field=field)) return constraints async def _get_constraints_for_one_field( 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/backend/tests/unit/message_bus/operations/transform/__init__.py b/backend/infrahub/core/validators/models/__init__.py similarity index 100% rename from backend/tests/unit/message_bus/operations/transform/__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..1ced350a97 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) @@ -52,7 +52,6 @@ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> No branch_filter, branch_params = self.branch.get_query_filter_path(at=self.at.to_string(), is_isolated=False) self.params.update(branch_params) - self.params["node_kind"] = self.node_schema.kind if hierarchy := getattr(self.node_schema, "hierarchy", None): self.params["hierarchy_kind"] = hierarchy else: @@ -61,8 +60,7 @@ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> No # ruff: noqa: E501 query = """ - MATCH (n:Node) - WHERE $node_kind IN LABELS(n) + MATCH (n:%(node_kind)s) CALL { WITH n MATCH path = (root:Root)<-[rroot:IS_PART_OF]-(n) @@ -117,7 +115,12 @@ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> No any(r in relationships(current_path) WHERE r.hierarchy <> $hierarchy_kind) OR NOT ($peer_kind IN labels(current_peer)) ) - """ % {"branch_filter": branch_filter, "to_children": to_children, "to_parent": to_parent} + """ % { + "branch_filter": branch_filter, + "to_children": to_children, + "to_parent": to_parent, + "node_kind": self.node_schema.kind, + } self.add_to_query(query) self.return_labels = ["start_node.uuid", "branch_name", "current_peer.uuid"] @@ -142,7 +145,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..08044413c4 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) @@ -34,20 +34,22 @@ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> No branch_filter, branch_params = self.branch.get_query_filter_path(at=self.at.to_string(), is_isolated=False) self.params.update(branch_params) - self.params["node_kind"] = self.node_schema.kind self.params["relationship_id"] = self.relationship_schema.identifier + self.params["relationship_direction"] = self.relationship_schema.direction.value self.params["min_count"] = ( self.min_count_override if self.min_count_override is not None else self.relationship_schema.min_count ) - self.params["max_count"] = ( - self.max_count_override if self.max_count_override is not None else self.relationship_schema.max_count - ) + max_count: int | None = self.relationship_schema.max_count + if self.max_count_override: + max_count = self.max_count_override + if max_count == 0: + max_count = None + self.params["max_count"] = max_count # ruff: noqa: E501 query = """ // get the nodes on these branches nodes - MATCH (n:Node) - WHERE $node_kind IN LABELS(n) + MATCH (n:%(node_kind)s) CALL { WITH n MATCH path = (root:Root)<-[rroot:IS_PART_OF]-(n) @@ -64,7 +66,9 @@ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> No CALL { WITH active_node MATCH path = (active_node)-[rrel1:IS_RELATED]-(rel:Relationship { name: $relationship_id })-[rrel2:IS_RELATED]-(peer:Node) - WHERE all( + WHERE ($relationship_direction <> "outbound" OR (startNode(rrel1) = active_node AND startNode(rrel2) = rel)) + AND ($relationship_direction <> "inbound" OR (startNode(rrel1) = rel AND startNode(rrel2) = peer)) + AND all( r in relationships(path) WHERE (%(branch_filter)s) ) @@ -115,7 +119,7 @@ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> No } // return a row for each node-branch combination with a count for that branch UNWIND violation_branches_and_counts as violation_branch_and_count - """ % {"branch_filter": branch_filter} + """ % {"branch_filter": branch_filter, "node_kind": self.node_schema.kind} self.add_to_query(query) self.return_labels = [ @@ -146,7 +150,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..2b9c438e9f 100644 --- a/backend/infrahub/core/validators/relationship/optional.py +++ b/backend/infrahub/core/validators/relationship/optional.py @@ -24,14 +24,12 @@ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> No branch_filter, branch_params = self.branch.get_query_filter_path(at=self.at.to_string(), is_isolated=False) self.params.update(branch_params) - self.params["node_kind"] = self.node_schema.kind self.params["relationship_id"] = self.relationship_schema.identifier query = """ // Query all Active Nodes of type // and store their UUID in uuids_active_node - MATCH (n:Node) - WHERE $node_kind IN LABELS(n) + MATCH (n:%(node_kind)s) CALL { WITH n MATCH (root:Root)<-[r:IS_PART_OF]-(n) @@ -45,8 +43,7 @@ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> No WITH COLLECT(active_node.uuid) AS uuids_active_node // identifier all nodes with at least one active member for this relationship // and store their UUID in uuids_with_rel - MATCH (n:Node) - WHERE $node_kind IN LABELS(n) + MATCH (n:%(node_kind)s) CALL { WITH n, uuids_active_node MATCH path = (n)-[r:IS_RELATED]-(:Relationship { name: $relationship_id }) @@ -58,12 +55,11 @@ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> No WITH n1 as node_with_rel, r1 as r, uuids_active_node WHERE r.status = "active" WITH COLLECT(node_with_rel.uuid) AS uuids_with_rel, uuids_active_node - MATCH (n:Node)-[r:IS_PART_OF]->(:Root) - WHERE $node_kind IN LABELS(n) - AND n.uuid IN uuids_active_node + MATCH (n:%(node_kind)s)-[r:IS_PART_OF]->(:Root) + WHERE n.uuid IN uuids_active_node AND not n.uuid IN uuids_with_rel AND NOT exists((n)-[:IS_RELATED]-(:Relationship { name: $relationship_id })) - """ % {"branch_filter": branch_filter} + """ % {"branch_filter": branch_filter, "node_kind": self.node_schema.kind} self.add_to_query(query) self.return_labels = ["n.uuid", "r as root_relationship"] @@ -86,7 +82,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..c21d388de0 100644 --- a/backend/infrahub/core/validators/relationship/peer.py +++ b/backend/infrahub/core/validators/relationship/peer.py @@ -30,14 +30,12 @@ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> No branch_filter, branch_params = self.branch.get_query_filter_path(at=self.at.to_string(), is_isolated=False) self.params.update(branch_params) - self.params["node_kind"] = self.node_schema.kind self.params["relationship_id"] = self.relationship_schema.identifier self.params["allowed_peer_kinds"] = allowed_peer_kinds # ruff: noqa: E501 query = """ - MATCH (n:Node) - WHERE $node_kind IN LABELS(n) + MATCH (n:%(node_kind)s) CALL { WITH n MATCH path = (root:Root)<-[rroot:IS_PART_OF]-(n) @@ -85,7 +83,7 @@ async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> No WITH start_node, current_peer, branch_name, current_path WHERE all(r in relationships(current_path) WHERE r.status = "active") AND NOT any(label IN LABELS(current_peer) WHERE label IN $allowed_peer_kinds) - """ % {"branch_filter": branch_filter} + """ % {"branch_filter": branch_filter, "node_kind": self.node_schema.kind} self.add_to_query(query) self.return_labels = ["start_node.uuid", "branch_name", "current_peer.uuid"] @@ -110,7 +108,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..e80055a745 --- /dev/null +++ b/backend/infrahub/core/validators/tasks.py @@ -0,0 +1,46 @@ +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 infrahub.workflows.utils import add_branch_tag + +from .models.validate_migration import SchemaValidateMigrationData # noqa: TCH001 + + +@flow(name="schema-migrations-validate") +async def schema_validate_migrations(message: SchemaValidateMigrationData) -> list[str]: + batch = InfrahubBatch(return_exceptions=True) + error_messages: list[str] = [] + service = services.service + await add_branch_tag(branch_name=message.branch.name) + + 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/model.py b/backend/infrahub/core/validators/uniqueness/model.py index e2cb7a4100..0be3e0072e 100644 --- a/backend/infrahub/core/validators/uniqueness/model.py +++ b/backend/infrahub/core/validators/uniqueness/model.py @@ -44,6 +44,20 @@ def __bool__(self) -> bool: return True return False + def __str__(self) -> str: + return ( + "ATTRS: " + + "; ".join( + q.attribute_name + " " + str(q.property_name) + " " + (str(q.value) if q.value is not None else "") + for q in self.unique_attribute_paths + ) + + " RELS: " + + "; ".join( + q.identifier + " " + str(q.attribute_name) + " " + (str(q.value) if q.value is not None else "") + for q in self.relationship_attribute_paths + ) + ) + class NonUniqueRelatedAttribute(BaseModel): relationship: RelationshipSchema diff --git a/backend/infrahub/core/validators/uniqueness/query.py b/backend/infrahub/core/validators/uniqueness/query.py index d709fb50f3..388c561d45 100644 --- a/backend/infrahub/core/validators/uniqueness/query.py +++ b/backend/infrahub/core/validators/uniqueness/query.py @@ -21,11 +21,14 @@ 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) + def get_context(self) -> dict[str, str]: + return {"kind": self.query_request.kind} + async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: branch_filter, branch_params = self.branch.get_query_filter_path(at=self.at.to_string(), is_isolated=False) self.params.update(branch_params) diff --git a/backend/infrahub/database/__init__.py b/backend/infrahub/database/__init__.py index 1de2f5afef..86a7ec5b5b 100644 --- a/backend/infrahub/database/__init__.py +++ b/backend/infrahub/database/__init__.py @@ -2,7 +2,8 @@ import asyncio import random -from typing import TYPE_CHECKING, Any, Optional, Union +from dataclasses import dataclass +from typing import TYPE_CHECKING, Any, Callable, Coroutine, Optional, TypeVar, Union from neo4j import ( READ_ACCESS, @@ -30,7 +31,7 @@ from infrahub.log import get_logger from infrahub.utils import InfrahubStringEnum -from .constants import DatabaseType +from .constants import DatabaseType, Neo4jRuntime from .memgraph import DatabaseManagerMemgraph from .metrics import QUERY_EXECUTION_METRICS, TRANSACTION_RETRIES from .neo4j import DatabaseManagerNeo4j @@ -40,15 +41,22 @@ 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() +@dataclass +class QueryConfig: + neo4j_runtime: Neo4jRuntime = Neo4jRuntime.DEFAULT + profile_memory: bool = False + + class InfrahubDatabaseMode(InfrahubStringEnum): DRIVER = "driver" SESSION = "session" @@ -70,7 +78,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,6 +141,7 @@ def __init__( session: Optional[AsyncSession] = None, session_mode: InfrahubDatabaseSessionMode = InfrahubDatabaseSessionMode.WRITE, transaction: Optional[AsyncTransaction] = None, + queries_names_to_config: Optional[dict[str, QueryConfig]] = None, ): self._mode: InfrahubDatabaseMode = mode self._driver: AsyncDriver = driver @@ -140,6 +149,7 @@ def __init__( self._session_mode: InfrahubDatabaseSessionMode = session_mode self._is_session_local: bool = False self._transaction: Optional[AsyncTransaction] = transaction + self.queries_names_to_config = queries_names_to_config if queries_names_to_config is not None else {} if schemas: self._schemas: dict[str, SchemaBranch] = {schema.name: schema for schema in schemas} @@ -172,6 +182,14 @@ def is_transaction(self) -> bool: return True return False + def get_context(self) -> dict[str, Any]: + """ + This method is meant to be overridden by subclasses in order to fill in subclass attributes + to methods returning a copy of this object using self.__class__ constructor. + """ + + return {} + def add_schema(self, schema: SchemaBranch, name: Optional[str] = None) -> None: self._schemas[name or schema.name] = schema @@ -181,6 +199,8 @@ def start_session(self, read_only: bool = False, schemas: Optional[list[SchemaBr if read_only: session_mode = InfrahubDatabaseSessionMode.READ + context = self.get_context() + return self.__class__( mode=InfrahubDatabaseMode.SESSION, db_type=self.db_type, @@ -188,9 +208,13 @@ def start_session(self, read_only: bool = False, schemas: Optional[list[SchemaBr db_manager=self.manager, driver=self._driver, session_mode=session_mode, + queries_names_to_config=self.queries_names_to_config, + **context, ) def start_transaction(self, schemas: Optional[list[SchemaBranch]] = None) -> InfrahubDatabase: + context = self.get_context() + return self.__class__( mode=InfrahubDatabaseMode.TRANSACTION, db_type=self.db_type, @@ -199,6 +223,8 @@ def start_transaction(self, schemas: Optional[list[SchemaBranch]] = None) -> Inf driver=self._driver, session=self._session, session_mode=self._session_mode, + queries_names_to_config=self.queries_names_to_config, + **context, ) async def session(self) -> AsyncSession: @@ -271,26 +297,57 @@ async def close(self) -> None: await self._driver.close() async def execute_query( - self, query: str, params: Optional[dict[str, Any]] = None, name: Optional[str] = "undefined" + self, + query: str, + params: dict[str, Any] | None = None, + name: str = "undefined", + context: dict[str, str] | None = None, ) -> list[Record]: - with trace.get_tracer(__name__).start_as_current_span("execute_db_query") as span: - span.set_attribute("query", query) - if name: - span.set_attribute("query_name", name) - - with QUERY_EXECUTION_METRICS.labels(self._session_mode.value, name).time(): - response = await self.run_query(query=query, params=params) - return [item async for item in response] + results, _ = await self.execute_query_with_metadata(query=query, params=params, name=name, context=context) + return results async def execute_query_with_metadata( - self, query: str, params: Optional[dict[str, Any]] = None, name: Optional[str] = "undefined" + self, + query: str, + params: dict[str, Any] | None = None, + name: str = "undefined", + context: dict[str, str] | None = None, ) -> tuple[list[Record], dict[str, Any]]: with trace.get_tracer(__name__).start_as_current_span("execute_db_query_with_metadata") as span: span.set_attribute("query", query) if name: span.set_attribute("query_name", name) - with QUERY_EXECUTION_METRICS.labels(self._session_mode.value, name).time(): + runtime = Neo4jRuntime.UNDEFINED + + try: + query_config = self.queries_names_to_config[name] + if self.db_type == DatabaseType.NEO4J: + runtime = self.queries_names_to_config[name].neo4j_runtime + if runtime not in [Neo4jRuntime.DEFAULT, Neo4jRuntime.UNDEFINED]: + query = f"CYPHER runtime = {runtime.value}\n" + query + if query_config.profile_memory: + query = "PROFILE\n" + query + except KeyError: + pass # No specific config for this query + + labels = { + "type": self._session_mode.value, + "query": name, + "runtime": runtime.value, + "context1": "", + "context2": "", + } + if context: + labels.update( + { + f"context{idx + 1}": f"{key}__{value}" + for idx, (key, value) in enumerate(context.items()) + if idx <= 1 + } + ) + + with QUERY_EXECUTION_METRICS.labels(**labels).time(): response = await self.run_query(query=query, params=params, name=name) results = [item async for item in response] return results, response._metadata or {} @@ -400,7 +457,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 +468,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) @@ -426,11 +486,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/analyzer.py b/backend/infrahub/database/analyzer.py deleted file mode 100644 index c9db48fb49..0000000000 --- a/backend/infrahub/database/analyzer.py +++ /dev/null @@ -1,206 +0,0 @@ -import time -from collections import defaultdict -from dataclasses import dataclass -from pathlib import Path -from typing import Any, Optional - -import matplotlib.pyplot as plt -import pandas as pd -from infrahub_sdk import Timestamp -from neo4j import Record - -# pylint: skip-file -from infrahub.database import InfrahubDatabase -from infrahub.log import get_logger - -log = get_logger() - - -@dataclass -class QueryMeasurement: - duration: float - query_name: str - start_time: float - memory: Optional[float] = None - index: Optional[int] = None - profile: bool = False - - -class QueryAnalyzer: - def __init__(self) -> None: - self._start_time: Optional[Timestamp] = None - self.name = "query_analyzer" - self.index = 0 - self.measurements: list[QueryMeasurement] = [] - self.count_per_query: dict[str, int] = defaultdict(int) - self._df: Optional[pd.DataFrame] = None - self.measure_memory_usage: bool = False - self.sampling_memory_usage: int = 25 - self.output_location: Path = Path.cwd() - - @property - def start_time(self) -> Timestamp: - if self._start_time: - return self._start_time - raise ValueError("start_time hasnt't been initialized yet") - - def create_directory(self) -> Path: - time_str = self.start_time.to_string() - for char in [":", "-", "."]: - time_str = time_str.replace(char, "_") - directory_name = f"{time_str}_{self.name}" - full_directory = self.output_location / directory_name - if not full_directory.exists(): - full_directory.mkdir(parents=True) - return full_directory - - def start_tracking(self, name: Optional[str] = None) -> None: - self._start_time = Timestamp() - self.index = 0 - if name: - self.name = name - - def get_df(self) -> pd.DataFrame: - data = {} - for item in QueryMeasurement.__dataclass_fields__.keys(): - data[item] = [getattr(m, item) for m in self.measurements] - - return pd.DataFrame(data) - - def sample_memory(self, name: str) -> bool: - if not self._start_time or not self.measure_memory_usage: - return False - - if self.count_per_query[name] % self.sampling_memory_usage == 0: - return True - - return False - - def add_measurement(self, measurement: QueryMeasurement) -> None: - if not self._start_time: - return - - self.index += 1 - measurement.index = self.index - - self.measurements.append(measurement) - self.count_per_query[measurement.query_name] += 1 - - def create_graphs(self, prefix: Optional[str] = None) -> None: - df = self.get_df() - query_names = set(df["query_name"].tolist()) - - output_dir = self.create_directory() - - for query_name in query_names: - self.create_duration_graph( - query_name=query_name, metric="duration", prefix=self.name, output_dir=output_dir - ) - self.create_memory_graph(query_name=query_name, metric="memory", prefix=self.name, output_dir=output_dir) - - def create_duration_graph( - self, query_name: str, metric: str = "duration", prefix: Optional[str] = None, output_dir: Optional[Path] = None - ) -> None: - df = self.get_df() - df_query = df[(df["query_name"] == query_name) & (df["profile"] == False)] # noqa: E712 - - name = f"{query_name}_{metric}" - plt.figure(name) - - serie_name = f"{metric}_min_r10" - serie2 = df_query[metric].rolling(10).min().multiply(1000).round(2) - plt.plot(df_query.index, serie2, label=serie_name) - - plt.ylabel("msec", fontsize=15) - plt.title(f"Query - {query_name} | {metric}", fontsize=20) - plt.grid() - - file_name = f"{name}.png" - if prefix: - file_name = f"{prefix}_{name}.png" - - if output_dir: - plt.savefig(str(output_dir / file_name)) - else: - plt.savefig(f"{self.start_time.to_string()}_{file_name}") - - def create_memory_graph( - self, query_name: str, metric: str = "memory", prefix: Optional[str] = None, output_dir: Optional[Path] = None - ) -> None: - df = self.get_df() - df_query = df[(df["query_name"] == query_name) & (df["profile"] == True)] # noqa: E712 - - plt.figure(query_name) - - serie_name = f"{metric}" - serie1 = df_query[metric] - plt.plot(df_query.index, serie1, label=serie_name) - - plt.ylabel("memory", fontsize=15) - plt.title(f"Query - {query_name} | {metric}", fontsize=20) - - file_name = f"{query_name}_{metric}.png" - if prefix: - file_name = f"{prefix}_{query_name}_{metric}.png" - - if output_dir: - plt.savefig(str(output_dir / file_name)) - else: - plt.savefig(f"{self.start_time.to_string()}_{file_name}") - - -query_stats = QueryAnalyzer() - - -class InfrahubDatabaseAnalyzer(InfrahubDatabase): - async def execute_query( - self, query: str, params: dict[str, Any] | None = None, name: str | None = "undefined" - ) -> list[Record]: - time_start = time.time() - if name and query_stats.sample_memory(name=name): - query = "PROFILE\n" + query - response, metadata = await super().execute_query_with_metadata(query, params, name) - duration_time = time.time() - time_start - query_stats.add_measurement( - QueryMeasurement( - duration=duration_time, - profile=True, - memory=metadata["profile"]["args"]["GlobalMemory"], - query_name=str(name), - start_time=time_start, - ) - ) - else: - response = await super().execute_query(query, params, name) - duration_time = time.time() - time_start - query_stats.add_measurement( - QueryMeasurement(duration=duration_time, profile=False, query_name=str(name), start_time=time_start) - ) - - return response - - async def execute_query_with_metadata( - self, query: str, params: dict[str, Any] | None = None, name: str | None = "undefined" - ) -> tuple[list[Record], dict[str, Any]]: - time_start = time.time() - if name and query_stats.sample_memory(name=name): - query = "PROFILE\n" + query - response, metadata = await super().execute_query_with_metadata(query, params, name) - duration_time = time.time() - time_start - query_stats.add_measurement( - QueryMeasurement( - duration=duration_time, - profile=True, - memory=metadata["profile"]["args"]["GlobalMemory"], - query_name=str(name), - start_time=time_start, - ) - ) - else: - response, metadata = await super().execute_query_with_metadata(query, params, name) - duration_time = time.time() - time_start - query_stats.add_measurement( - QueryMeasurement(duration=duration_time, profile=False, query_name=str(name), start_time=time_start) - ) - - return response, metadata diff --git a/backend/infrahub/database/constants.py b/backend/infrahub/database/constants.py index 4b79526bda..42897163a0 100644 --- a/backend/infrahub/database/constants.py +++ b/backend/infrahub/database/constants.py @@ -11,6 +11,8 @@ class Neo4jRuntime(str, Enum): INTERPRETED = "interpreted" SLOTTED = "slotted" PIPELINED = "pipelined" + PARALLEL = "parallel" + UNDEFINED = "undefined" class IndexType(str, Enum): 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/metrics.py b/backend/infrahub/database/metrics.py index d7cf2d0772..d9e4ba2e2a 100644 --- a/backend/infrahub/database/metrics.py +++ b/backend/infrahub/database/metrics.py @@ -7,7 +7,7 @@ QUERY_EXECUTION_METRICS = Histogram( f"{METRIC_PREFIX}_query_execution_seconds", "Execution time to query the database", - labelnames=["type", "query"], + labelnames=["type", "query", "runtime", "context1", "context2"], buckets=[0.005, 0.01, 0.02, 0.03, 0.04, 0.05, 0.1, 0.5, 1], ) 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/dependencies/builder/diff/diff_merger.py b/backend/infrahub/dependencies/builder/diff/diff_merger.py new file mode 100644 index 0000000000..d8eaa7aeda --- /dev/null +++ b/backend/infrahub/dependencies/builder/diff/diff_merger.py @@ -0,0 +1,18 @@ +from infrahub.core import registry +from infrahub.core.diff.merger.merger import DiffMerger +from infrahub.core.diff.merger.serializer import DiffMergeSerializer +from infrahub.dependencies.interface import DependencyBuilder, DependencyBuilderContext + +from .repository import DiffRepositoryDependency + + +class DiffMergerDependency(DependencyBuilder[DiffMerger]): + @classmethod + def build(cls, context: DependencyBuilderContext) -> DiffMerger: + return DiffMerger( + db=context.db, + source_branch=context.branch, + destination_branch=registry.get_branch_from_registry(), + diff_repository=DiffRepositoryDependency.build(context=context), + serializer=DiffMergeSerializer(db=context.db, max_batch_size=100), + ) diff --git a/backend/infrahub/dependencies/registry.py b/backend/infrahub/dependencies/registry.py index d9acc6de5c..74d35fbd6f 100644 --- a/backend/infrahub/dependencies/registry.py +++ b/backend/infrahub/dependencies/registry.py @@ -14,6 +14,7 @@ from .builder.diff.conflict_transferer import DiffConflictTransfererDependency from .builder.diff.coordinator import DiffCoordinatorDependency from .builder.diff.data_check_synchronizer import DiffDataCheckSynchronizerDependency +from .builder.diff.diff_merger import DiffMergerDependency from .builder.diff.enricher.aggregated import DiffAggregatedEnricherDependency from .builder.diff.enricher.cardinality_one import DiffCardinalityOneEnricherDependency from .builder.diff.enricher.hierarchy import DiffHierarchyEnricherDependency @@ -45,6 +46,7 @@ def build_component_registry() -> ComponentDependencyRegistry: component_registry.track_dependency(DiffConflictTransfererDependency) component_registry.track_dependency(DiffCoordinatorDependency) component_registry.track_dependency(DiffDataCheckSynchronizerDependency) + component_registry.track_dependency(DiffMergerDependency) return component_registry 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/sync/examples/infrahub_to_peering-manager/infrahub/__init__.py b/backend/infrahub/generators/__init__.py similarity index 100% rename from sync/examples/infrahub_to_peering-manager/infrahub/__init__.py rename to backend/infrahub/generators/__init__.py diff --git a/backend/infrahub/message_bus/messages/request_generator_run.py b/backend/infrahub/generators/models.py similarity index 90% rename from backend/infrahub/message_bus/messages/request_generator_run.py rename to backend/infrahub/generators/models.py index e76d736bfe..72772e3bdb 100644 --- a/backend/infrahub/message_bus/messages/request_generator_run.py +++ b/backend/infrahub/generators/models.py @@ -1,12 +1,11 @@ from typing import Optional -from pydantic import Field +from pydantic import BaseModel, Field -from infrahub.message_bus import InfrahubMessage from infrahub.message_bus.types import ProposedChangeGeneratorDefinition -class RequestGeneratorRun(InfrahubMessage): +class RequestGeneratorRun(BaseModel): """Runs a generator.""" generator_definition: ProposedChangeGeneratorDefinition = Field(..., description="The Generator definition") diff --git a/backend/infrahub/message_bus/operations/requests/generator.py b/backend/infrahub/generators/tasks.py similarity index 52% rename from backend/infrahub/message_bus/operations/requests/generator.py rename to backend/infrahub/generators/tasks.py index 1b62104332..ea269cc760 100644 --- a/backend/infrahub/message_bus/operations/requests/generator.py +++ b/backend/infrahub/generators/tasks.py @@ -1,43 +1,47 @@ import os -from infrahub_sdk import InfrahubNode from infrahub_sdk.exceptions import ModuleImportError +from infrahub_sdk.node import InfrahubNode from infrahub_sdk.protocols import CoreGeneratorInstance from infrahub_sdk.schema import InfrahubGeneratorDefinitionConfig +from prefect import flow, task from infrahub import lock -from infrahub.core.constants import GeneratorInstanceStatus, InfrahubKind +from infrahub.core.constants import GeneratorInstanceStatus +from infrahub.generators.models import RequestGeneratorRun 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 +from infrahub.services import InfrahubServices, services -async def run(message: messages.RequestGeneratorRun, service: InfrahubServices) -> None: +@flow(name="generator-run") +async def run_generator(model: RequestGeneratorRun) -> None: + service = services.service + repository = await get_initialized_repo( - repository_id=message.repository_id, - name=message.repository_name, + repository_id=model.repository_id, + name=model.repository_name, service=service, - repository_kind=message.repository_kind, + repository_kind=model.repository_kind, ) generator_definition = InfrahubGeneratorDefinitionConfig( - name=message.generator_definition.definition_name, - class_name=message.generator_definition.class_name, - file_path=message.generator_definition.file_path, - query=message.generator_definition.query_name, - targets=message.generator_definition.group_id, - convert_query_response=message.generator_definition.convert_query_response, + name=model.generator_definition.definition_name, + class_name=model.generator_definition.class_name, + file_path=model.generator_definition.file_path, + query=model.generator_definition.query_name, + targets=model.generator_definition.group_id, + convert_query_response=model.generator_definition.convert_query_response, ) - commit_worktree = repository.get_commit_worktree(commit=message.commit) + commit_worktree = repository.get_commit_worktree(commit=model.commit) file_info = extract_repo_file_information( full_filename=os.path.join(commit_worktree.directory, generator_definition.file_path.as_posix()), repo_directory=repository.directory_root, worktree_directory=commit_worktree.directory, ) - generator_instance = await _define_instance(message=message, service=service) + generator_instance = await _define_instance(model=model, service=service) try: generator_class = generator_definition.load_class( @@ -47,8 +51,8 @@ async def run(message: messages.RequestGeneratorRun, service: InfrahubServices) generator = generator_class( query=generator_definition.query, client=service.client, - branch=message.branch_name, - params=message.variables, + branch=model.branch_name, + params=model.variables, generator_instance=generator_instance.id, convert_query_response=generator_definition.convert_query_response, infrahub_node=InfrahubNode, @@ -63,23 +67,24 @@ async def run(message: messages.RequestGeneratorRun, service: InfrahubServices) await generator_instance.update(do_full_update=True) -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 +@task +async def _define_instance(model: RequestGeneratorRun, service: InfrahubServices) -> CoreGeneratorInstance: + if model.generator_instance: + instance = await service.client.get( + kind=CoreGeneratorInstance, id=model.generator_instance, branch=model.branch_name ) instance.status.value = GeneratorInstanceStatus.PENDING.value await instance.update(do_full_update=True) else: async with lock.registry.get( - f"{message.target_id}-{message.generator_definition.definition_id}", namespace="generator" + f"{model.target_id}-{model.generator_definition.definition_id}", namespace="generator" ): - instances: list[CoreGeneratorInstance] = await service.client.filters( - kind=InfrahubKind.GENERATORINSTANCE, - definition__ids=[message.generator_definition.definition_id], - object__ids=[message.target_id], - branch=message.branch_name, + instances = await service.client.filters( + kind=CoreGeneratorInstance, + definition__ids=[model.generator_definition.definition_id], + object__ids=[model.target_id], + branch=model.branch_name, ) if instances: instance = instances[0] @@ -87,13 +92,13 @@ 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, - branch=message.branch_name, + kind=CoreGeneratorInstance, + branch=model.branch_name, data={ - "name": f"{message.generator_definition.definition_name}: {message.target_name}", + "name": f"{model.generator_definition.definition_name}: {model.target_name}", "status": GeneratorInstanceStatus.PENDING.value, - "object": message.target_id, - "definition": message.generator_definition.definition_id, + "object": model.target_id, + "definition": model.generator_definition.definition_id, }, ) await instance.save() diff --git a/backend/infrahub/git/actions.py b/backend/infrahub/git/actions.py deleted file mode 100644 index 0989531ac8..0000000000 --- a/backend/infrahub/git/actions.py +++ /dev/null @@ -1,72 +0,0 @@ -from infrahub import lock -from infrahub.core.constants import InfrahubKind, RepositoryInternalStatus -from infrahub.core.registry import registry -from infrahub.exceptions import RepositoryError -from infrahub.services import InfrahubServices - -from .repository import InfrahubRepository - - -async def sync_remote_repositories(service: InfrahubServices) -> None: - branches = await service.client.branch.all() - repositories = await service.client.get_list_repositories(branches=branches, kind=InfrahubKind.REPOSITORY) - - for repo_name, repository_data in repositories.items(): - async with service.git_report( - title="Syncing repository", related_node=repository_data.repository.id, create_with_context=False - ) as git_report: - active_internal_status = RepositoryInternalStatus.ACTIVE.value - default_internal_status = repository_data.branch_info[registry.default_branch].internal_status - staging_branch = None - if default_internal_status != RepositoryInternalStatus.ACTIVE.value: - active_internal_status = RepositoryInternalStatus.STAGING.value - staging_branch = repository_data.get_staging_branch() - - infrahub_branch = staging_branch or registry.default_branch - - async with lock.registry.get(name=repo_name, namespace="repository"): - init_failed = False - try: - repo = await InfrahubRepository.init( - service=service, - id=repository_data.repository.id, - name=repository_data.repository.name.value, - location=repository_data.repository.location.value, - client=service.client, - task_report=git_report, - internal_status=active_internal_status, - default_branch_name=repository_data.repository.default_branch.value, - ) - except RepositoryError as exc: - service.log.error(str(exc)) - init_failed = True - - if init_failed: - try: - repo = await InfrahubRepository.new( - service=service, - id=repository_data.repository.id, - name=repository_data.repository.name.value, - location=repository_data.repository.location.value, - client=service.client, - task_report=git_report, - internal_status=active_internal_status, - default_branch_name=repository_data.repository.default_branch.value, - ) - await repo.import_objects_from_files( - git_branch_name=registry.default_branch, infrahub_branch_name=infrahub_branch - ) - except RepositoryError as exc: - await git_report.error(str(exc)) - continue - - error: RepositoryError | None = None - - try: - await repo.sync(staging_branch=staging_branch) - except RepositoryError as exc: - error = exc - - await git_report.set_status( - previous_status=repository_data.repository.operational_status.value, error=error - ) diff --git a/backend/infrahub/git/integrator.py b/backend/infrahub/git/integrator.py index 8f3b807949..234b8b8aed 100644 --- a/backend/infrahub/git/integrator.py +++ b/backend/infrahub/git/integrator.py @@ -10,17 +10,24 @@ import jinja2 import ujson import yaml -from infrahub_sdk import ( - InfrahubClient, - InfrahubNode, - InfrahubRepositoryConfig, - ValidationError, +from infrahub_sdk import InfrahubClient # noqa: TCH002 +from infrahub_sdk.exceptions import ValidationError +from infrahub_sdk.protocols import ( + CoreArtifact, + CoreArtifactDefinition, + CoreCheckDefinition, + CoreGeneratorDefinition, + CoreGraphQLQuery, + CoreTransformation, + CoreTransformJinja2, + CoreTransformPython, ) from infrahub_sdk.schema import ( InfrahubCheckDefinitionConfig, InfrahubGeneratorDefinitionConfig, InfrahubJinja2TransformConfig, InfrahubPythonTransformConfig, + InfrahubRepositoryConfig, ) from infrahub_sdk.utils import compare_lists from infrahub_sdk.yaml import SchemaFile @@ -36,9 +43,11 @@ import types from infrahub_sdk.checks import InfrahubCheck + from infrahub_sdk.node import InfrahubNode from infrahub_sdk.schema import InfrahubRepositoryArtifactDefinitionConfig from infrahub_sdk.transforms import InfrahubTransform + from infrahub.git.models import RequestArtifactGenerate from infrahub.message_bus import messages # pylint: disable=too-many-lines @@ -191,7 +200,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 +265,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 +289,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 +312,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 +373,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 +388,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 +542,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 +583,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 +593,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 +638,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 +706,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 +753,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 +967,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 +989,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 +1013,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 +1021,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 +1039,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 +1054,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 +1072,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 +1092,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 @@ -1213,11 +1229,9 @@ async def execute_python_transform( module = importlib.import_module(file_info.module_name) - transform_class: InfrahubTransform = getattr(module, class_name) + transform_class: type[InfrahubTransform] = getattr(module, class_name) - transform = await transform_class.init( - root_directory=commit_worktree.directory, branch=branch_name, client=client - ) + transform = transform_class(root_directory=commit_worktree.directory, branch=branch_name, client=client) return await transform.run(data=data) except ModuleNotFoundError as exc: @@ -1244,11 +1258,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 +1312,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, RequestArtifactGenerate] ) -> ArtifactGenerateResult: response = await self.sdk.query_gql_query( name=message.query, diff --git a/backend/infrahub/message_bus/messages/request_artifact_generate.py b/backend/infrahub/git/models.py similarity index 72% rename from backend/infrahub/message_bus/messages/request_artifact_generate.py rename to backend/infrahub/git/models.py index 048c37057f..11d566414b 100644 --- a/backend/infrahub/message_bus/messages/request_artifact_generate.py +++ b/backend/infrahub/git/models.py @@ -1,11 +1,20 @@ from typing import Optional -from pydantic import Field +from pydantic import BaseModel, Field -from infrahub.message_bus import InfrahubMessage +class RequestArtifactDefinitionGenerate(BaseModel): + """Sent to trigger the generation of artifacts for a given branch.""" -class RequestArtifactGenerate(InfrahubMessage): + artifact_definition: str = Field(..., description="The unique ID of the Artifact Definition") + branch: str = Field(..., description="The branch to target") + limit: list[str] = Field( + default_factory=list, + description="List of targets to limit the scope of the generation, if populated only the included artifacts will be regenerated", + ) + + +class RequestArtifactGenerate(BaseModel): """Runs to generate an artifact""" artifact_name: str = Field(..., description="Name of the artifact") diff --git a/backend/infrahub/git/repository.py b/backend/infrahub/git/repository.py index 5566cedfcb..58841e885d 100644 --- a/backend/infrahub/git/repository.py +++ b/backend/infrahub/git/repository.py @@ -3,7 +3,7 @@ from typing import Any, Optional, Union from git.exc import BadName, GitCommandError -from infrahub_sdk import GraphQLError +from infrahub_sdk.exceptions import GraphQLError from pydantic import Field from infrahub.core.constants import InfrahubKind, RepositoryInternalStatus diff --git a/backend/infrahub/git/tasks.py b/backend/infrahub/git/tasks.py new file mode 100644 index 0000000000..eb33aa7c43 --- /dev/null +++ b/backend/infrahub/git/tasks.py @@ -0,0 +1,236 @@ +from infrahub_sdk import InfrahubClient +from prefect import flow, task + +from infrahub import lock +from infrahub.core.constants import InfrahubKind, RepositoryInternalStatus +from infrahub.core.protocols import CoreRepository +from infrahub.core.registry import registry +from infrahub.exceptions import RepositoryError +from infrahub.services import services + +from ..log import get_logger +from ..tasks.artifact import define_artifact +from ..workflows.catalogue import REQUEST_ARTIFACT_DEFINITION_GENERATE, REQUEST_ARTIFACT_GENERATE +from ..workflows.utils import add_branch_tag +from .models import RequestArtifactDefinitionGenerate, RequestArtifactGenerate +from .repository import InfrahubRepository, get_initialized_repo + +log = get_logger() + + +@flow(name="git-repositories-branch-create") +async def create_branch(branch: str, branch_id: str) -> None: + """Request to the creation of git branches in available repositories.""" + service = services.service + await add_branch_tag(branch_name=branch) + + repositories: list[CoreRepository] = await service.client.filters(kind=CoreRepository) + + batch = await service.client.create_batch() + + for repository in repositories: + batch.add( + task=git_branch_create, + client=service.client.client, + branch=branch, + branch_id=branch_id, + repository_name=repository.name.value, + repository_id=repository.id, + ) + + async for _, _ in batch.execute(): + pass + + +@flow(name="git-repository-sync") +async def sync_remote_repositories() -> None: + service = services.service + + branches = await service.client.branch.all() + repositories = await service.client.get_list_repositories(branches=branches, kind=InfrahubKind.REPOSITORY) + + for repo_name, repository_data in repositories.items(): + async with service.git_report( + title="Syncing repository", related_node=repository_data.repository.id, create_with_context=False + ) as git_report: + active_internal_status = RepositoryInternalStatus.ACTIVE.value + default_internal_status = repository_data.branch_info[registry.default_branch].internal_status + staging_branch = None + if default_internal_status != RepositoryInternalStatus.ACTIVE.value: + active_internal_status = RepositoryInternalStatus.STAGING.value + staging_branch = repository_data.get_staging_branch() + + infrahub_branch = staging_branch or registry.default_branch + + async with lock.registry.get(name=repo_name, namespace="repository"): + init_failed = False + try: + repo = await InfrahubRepository.init( + service=service, + id=repository_data.repository.id, + name=repository_data.repository.name.value, + location=repository_data.repository.location.value, + client=service.client, + task_report=git_report, + internal_status=active_internal_status, + default_branch_name=repository_data.repository.default_branch.value, + ) + except RepositoryError as exc: + service.log.error(str(exc)) + init_failed = True + + if init_failed: + try: + repo = await InfrahubRepository.new( + service=service, + id=repository_data.repository.id, + name=repository_data.repository.name.value, + location=repository_data.repository.location.value, + client=service.client, + task_report=git_report, + internal_status=active_internal_status, + default_branch_name=repository_data.repository.default_branch.value, + ) + await repo.import_objects_from_files( + git_branch_name=registry.default_branch, infrahub_branch_name=infrahub_branch + ) + except RepositoryError as exc: + await git_report.error(str(exc)) + continue + + error: RepositoryError | None = None + + try: + await repo.sync(staging_branch=staging_branch) + except RepositoryError as exc: + error = exc + + await git_report.set_status( + previous_status=repository_data.repository.operational_status.value, error=error + ) + + +@task +async def git_branch_create( + client: InfrahubClient, branch: str, branch_id: str, repository_id: str, repository_name: str +) -> None: + repo = await InfrahubRepository.init(id=repository_id, name=repository_name, client=client) + async with lock.registry.get(name=repository_name, namespace="repository"): + await repo.create_branch_in_git(branch_name=branch, branch_id=branch_id) + + +@flow(name="artifact-definition-generate") +async def generate_artifact_definition(branch: str) -> None: + service = services.service + artifact_definitions = await service.client.all(kind=InfrahubKind.ARTIFACTDEFINITION, branch=branch, include=["id"]) + + for artifact_definition in artifact_definitions: + model = RequestArtifactDefinitionGenerate(branch=branch, artifact_definition=artifact_definition.id) + await service.workflow.submit_workflow( + workflow=REQUEST_ARTIFACT_DEFINITION_GENERATE, parameters={"model": model} + ) + + +@flow(name="artifact-generate") +async def generate_artifact(model: RequestArtifactGenerate) -> None: + log.debug("Generating artifact", message=model) + + service = services.service + + repo = await get_initialized_repo( + repository_id=model.repository_id, + name=model.repository_name, + service=service, + repository_kind=model.repository_kind, + ) + + artifact = await define_artifact(message=model, service=service) + + try: + result = await repo.render_artifact(artifact=artifact, message=model) + log.debug( + "Generated artifact", + name=model.artifact_name, + changed=result.changed, + checksum=result.checksum, + artifact_id=result.artifact_id, + storage_id=result.storage_id, + ) + except Exception as exc: # pylint: disable=broad-except + log.exception("Failed to generate artifact", error=exc) + artifact.status.value = "Error" + await artifact.save() + + +@flow(name="artifact-definition-generate") +async def generate_request_artifact_definition(model: RequestArtifactDefinitionGenerate) -> None: + await add_branch_tag(branch_name=model.branch) + + service = services.service + artifact_definition = await service.client.get( + kind=InfrahubKind.ARTIFACTDEFINITION, id=model.artifact_definition, branch=model.branch + ) + + await artifact_definition.targets.fetch() + group = artifact_definition.targets.peer + await group.members.fetch() + + existing_artifacts = await service.client.filters( + kind=InfrahubKind.ARTIFACT, + definition__ids=[model.artifact_definition], + include=["object"], + branch=model.branch, + ) + artifacts_by_member = {} + for artifact in existing_artifacts: + artifacts_by_member[artifact.object.peer.id] = artifact.id + + await artifact_definition.transformation.fetch() + transformation_repository = artifact_definition.transformation.peer.repository + + await transformation_repository.fetch() + + transform = artifact_definition.transformation.peer + await transform.query.fetch() + query = transform.query.peer + repository = transformation_repository.peer + branch = await service.client.branch.get(branch_name=model.branch) + if branch.sync_with_git: + repository = await service.client.get( + kind=InfrahubKind.GENERICREPOSITORY, id=repository.id, branch=model.branch, fragment=True + ) + transform_location = "" + + if transform.typename == InfrahubKind.TRANSFORMJINJA2: + transform_location = transform.template_path.value + elif transform.typename == InfrahubKind.TRANSFORMPYTHON: + transform_location = f"{transform.file_path.value}::{transform.class_name.value}" + + for relationship in group.members.peers: + member = relationship.peer + artifact_id = artifacts_by_member.get(member.id) + if model.limit and artifact_id not in model.limit: + continue + + request_artifact_generate_model = RequestArtifactGenerate( + artifact_name=artifact_definition.name.value, + artifact_id=artifact_id, + artifact_definition=model.artifact_definition, + commit=repository.commit.value, + content_type=artifact_definition.content_type.value, + transform_type=transform.typename, + transform_location=transform_location, + repository_id=repository.id, + repository_name=repository.name.value, + repository_kind=repository.get_kind(), + branch_name=model.branch, + query=query.name.value, + variables=member.extract(params=artifact_definition.parameters.value), + target_id=member.id, + target_name=member.display_label, + timeout=transform.timeout.value, + ) + + await service.workflow.submit_workflow( + workflow=REQUEST_ARTIFACT_GENERATE, parameters={"model": request_artifact_generate_model} + ) 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..250f0c05b1 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,10 +9,23 @@ 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) + @property + def operation_names(self) -> list[str]: + return [operation.name for operation in self.operations if operation.name is not None] + async def get_models_in_use(self, types: dict[str, Any]) -> set[str]: """List of Infrahub models that are referenced in the query.""" graphql_types = set() diff --git a/backend/infrahub/graphql/api/dependencies.py b/backend/infrahub/graphql/api/dependencies.py index 71aa7574f5..1d6308dff8 100644 --- a/backend/infrahub/graphql/api/dependencies.py +++ b/backend/infrahub/graphql/api/dependencies.py @@ -5,9 +5,16 @@ 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_checker import DefaultGraphQLPermissionChecker +from ..auth.query_permission_checker.default_branch_checker import DefaultBranchPermissionChecker +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,10 +24,16 @@ def get_anonymous_access_setting() -> bool: def build_graphql_query_permission_checker() -> GraphQLQueryPermissionChecker: return GraphQLQueryPermissionChecker( [ - ReadWriteGraphQLPermissionChecker(), - ReadOnlyGraphQLPermissionChecker(), AnonymousGraphQLPermissionChecker(get_anonymous_access_setting), - DefaultGraphQLPermissionChecker(), + # 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 ] ) diff --git a/backend/infrahub/graphql/api/endpoints.py b/backend/infrahub/graphql/api/endpoints.py index f96e727afe..c7cd4d52bd 100644 --- a/backend/infrahub/graphql/api/endpoints.py +++ b/backend/infrahub/graphql/api/endpoints.py @@ -6,6 +6,7 @@ from infrahub.api.dependencies import get_branch_dep from infrahub.core import registry from infrahub.core.branch import Branch +from infrahub.graphql.manager import GraphQLSchemaManager from .dependencies import build_graphql_app @@ -21,7 +22,7 @@ @router.get("/schema.graphql", include_in_schema=False) async def get_graphql_schema(branch: Branch = Depends(get_branch_dep)) -> PlainTextResponse: - schema = registry.schema.get_schema_branch(name=branch.name) - gql_schema = schema.get_graphql_schema() - - return PlainTextResponse(content=print_schema(gql_schema)) + schema_branch = registry.schema.get_schema_branch(name=branch.name) + gqlm = GraphQLSchemaManager.get_manager_for_branch(branch=branch, schema_branch=schema_branch) + graphql_schema = gqlm.get_graphql_schema() + return PlainTextResponse(content=print_schema(graphql_schema)) 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..27086f4d26 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: - if self.anonymous_access_allowed_func() and not analyzed_query.contains_mutation: - return - raise AuthorizationError("Authentication is required to perform this operation") + async def check( + self, + db: InfrahubDatabase, + account_session: AccountSession, + analyzed_query: InfrahubGraphQLQueryAnalyzer, + query_parameters: GraphqlParams, + branch: Branch, + ) -> CheckerResolution: + if not self.anonymous_access_allowed_func() or analyzed_query.contains_mutation: + raise AuthorizationError("Authentication is required to perform this operation") + return CheckerResolution.NEXT_CHECKER 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..9219a89f97 --- /dev/null +++ b/backend/infrahub/graphql/auth/query_permission_checker/default_branch_checker.py @@ -0,0 +1,66 @@ +from infrahub import config +from infrahub.auth import AccountSession +from infrahub.core import registry +from infrahub.core.account import GlobalPermission +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 = GlobalPermission( + action=GlobalPermissions.EDIT_DEFAULT_BRANCH.value, decision=PermissionDecision.ALLOW_ALL.value + ) + exempt_operations = [ + "BranchCreate", + "DiffUpdate", + "InfrahubAccountSelfUpdate", + "InfrahubAccountTokenCreate", + "InfrahubAccountTokenDelete", + ] + + async def supports(self, db: InfrahubDatabase, account_session: AccountSession, branch: Branch) -> bool: + return config.SETTINGS.main.allow_anonymous_access or 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_session=account_session, 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 = all( + operation_name in self.exempt_operations for operation_name in analyzed_query.operation_names + ) + + 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 deleted file mode 100644 index c4c1754fe5..0000000000 --- a/backend/infrahub/graphql/auth/query_permission_checker/default_checker.py +++ /dev/null @@ -1,13 +0,0 @@ -from infrahub.auth import AccountSession -from infrahub.exceptions import AuthorizationError -from infrahub.graphql.analyzer import InfrahubGraphQLQueryAnalyzer - -from .interface import GraphQLQueryPermissionCheckerInterface - - -class DefaultGraphQLPermissionChecker(GraphQLQueryPermissionCheckerInterface): - async def supports(self, account_session: AccountSession) -> bool: - return True - - async def check(self, analyzed_query: InfrahubGraphQLQueryAnalyzer) -> None: - 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..a8de539292 --- /dev/null +++ b/backend/infrahub/graphql/auth/query_permission_checker/merge_operation_checker.py @@ -0,0 +1,47 @@ +from infrahub import config +from infrahub.auth import AccountSession +from infrahub.core import registry +from infrahub.core.account import GlobalPermission +from infrahub.core.branch import Branch +from infrahub.core.constants import 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 MergeBranchPermissionChecker(GraphQLQueryPermissionCheckerInterface): + """Checker that makes sure a user account can merge a branch without going through a proposed change.""" + + permission_required = GlobalPermission( + action=GlobalPermissions.MERGE_BRANCH.value, decision=PermissionDecision.ALLOW_ALL.value + ) + + async def supports(self, db: InfrahubDatabase, account_session: AccountSession, branch: Branch) -> bool: + return config.SETTINGS.main.allow_anonymous_access or 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_session=account_session, 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..514a284793 --- /dev/null +++ b/backend/infrahub/graphql/auth/query_permission_checker/object_permission_checker.py @@ -0,0 +1,222 @@ +from infrahub import config +from infrahub.auth import AccountSession +from infrahub.core import registry +from infrahub.core.account import GlobalPermission, ObjectPermission +from infrahub.core.branch import Branch +from infrahub.core.constants import GLOBAL_BRANCH_NAME, 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.permissions.constants import PermissionDecisionFlag +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 config.SETTINGS.main.allow_anonymous_access or account_session.authenticated + + async def check( + self, + db: InfrahubDatabase, + account_session: AccountSession, + analyzed_query: InfrahubGraphQLQueryAnalyzer, + query_parameters: GraphqlParams, + branch: Branch, + ) -> CheckerResolution: + required_decision = ( + PermissionDecisionFlag.ALLOW_DEFAULT + if analyzed_query.branch is None + or analyzed_query.branch.name in (GLOBAL_BRANCH_NAME, registry.default_branch) + else PermissionDecisionFlag.ALLOW_OTHER + ) + + 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[ObjectPermission] = [] + for action in actions: + for kind in kinds: + extracted_words = extract_camelcase_words(kind) + permissions.append( + ObjectPermission( + namespace=extracted_words[0], + name="".join(extracted_words[1:]), + action=action.lower(), + decision=required_decision, + ) + ) + + for permission in permissions: + has_permission = False + for permission_backend in registry.permission_backends: + has_permission = await permission_backend.has_permission( + db=db, account_session=account_session, permission=permission, branch=branch + ) + if not has_permission: + raise PermissionDeniedError(f"You do not have the following permission: {permission}") + + return CheckerResolution.TERMINATE + + +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 = GlobalPermission( + action=GlobalPermissions.MANAGE_ACCOUNTS.value, decision=PermissionDecision.ALLOW_ALL.value + ) + + async def supports(self, db: InfrahubDatabase, account_session: AccountSession, branch: Branch) -> bool: + return config.SETTINGS.main.allow_anonymous_access or 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_session=account_session, 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 = GlobalPermission( + action=GlobalPermissions.MANAGE_PERMISSIONS.value, decision=PermissionDecision.ALLOW_ALL.value + ) + + async def supports(self, db: InfrahubDatabase, account_session: AccountSession, branch: Branch) -> bool: + return config.SETTINGS.main.allow_anonymous_access or 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_session=account_session, 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 = GlobalPermission( + action=GlobalPermissions.MANAGE_REPOSITORIES.value, decision=PermissionDecision.ALLOW_ALL.value + ) + + async def supports(self, db: InfrahubDatabase, account_session: AccountSession, branch: Branch) -> bool: + return config.SETTINGS.main.allow_anonymous_access or 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_session=account_session, 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..216607fcb8 --- /dev/null +++ b/backend/infrahub/graphql/auth/query_permission_checker/super_admin_checker.py @@ -0,0 +1,38 @@ +from infrahub import config +from infrahub.auth import AccountSession +from infrahub.core import registry +from infrahub.core.account import GlobalPermission +from infrahub.core.branch import Branch +from infrahub.core.constants import GlobalPermissions, PermissionDecision +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 = GlobalPermission( + action=GlobalPermissions.SUPER_ADMIN.value, decision=PermissionDecision.ALLOW_ALL.value + ) + + async def supports(self, db: InfrahubDatabase, account_session: AccountSession, branch: Branch) -> bool: + return config.SETTINGS.main.allow_anonymous_access or 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_session=account_session, 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..5efef8fd3a --- /dev/null +++ b/backend/infrahub/graphql/initialization.py @@ -0,0 +1,91 @@ +from __future__ import annotations + +from dataclasses import dataclass +from typing import TYPE_CHECKING, Optional + +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: Branch | str, + at: Timestamp | str | None = None, + account_session: AccountSession | None = None, + request: HTTPConnection | None = None, + service: InfrahubServices | None = 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_branch = registry.schema.get_schema_branch(name=branch.name) + gqlm = GraphQLSchemaManager.get_manager_for_branch(branch=branch, schema_branch=schema_branch) + gql_schema = gqlm.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.get_graphql_types(), + related_node_ids=set(), + background=BackgroundTasks(), + request=request, + service=service, + account_session=account_session, + ), + ) diff --git a/backend/infrahub/graphql/manager.py b/backend/infrahub/graphql/manager.py index 246105db18..6d5599580f 100644 --- a/backend/infrahub/graphql/manager.py +++ b/backend/infrahub/graphql/manager.py @@ -16,6 +16,7 @@ ProfileSchema, RelationshipSchema, ) +from infrahub.core.timestamp import Timestamp from infrahub.graphql.mutations.attribute import BaseAttributeCreate, BaseAttributeUpdate from infrahub.graphql.mutations.graphql_query import InfrahubGraphQLQueryMutation from infrahub.types import ATTRIBUTE_TYPES, InfrahubDataType, get_attribute_type @@ -23,15 +24,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 +43,21 @@ DiffSummaryElementRelationshipOne, ) from .resolver import ( + account_resolver, ancestors_resolver, + default_paginated_list_resolver, default_resolver, descendants_resolver, many_relationship_resolver, + parent_field_name_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 +68,10 @@ if TYPE_CHECKING: from graphql import GraphQLSchema - from infrahub.core.schema_manager import SchemaBranch + from infrahub.core.branch import Branch + from infrahub.core.schema.schema_branch import SchemaBranch + +# pylint: disable=redefined-builtin,c-extension-no-member,too-many-lines,too-many-public-methods class DeleteInput(graphene.InputObjectType): @@ -87,16 +98,71 @@ def get_attr_kind(node_schema: MainSchemaTypes, attr_schema: AttributeSchema) -> return get_enum_attribute_type_name(node_schema=node_schema, attr_schema=attr_schema) +@dataclass +class BranchDetails: + branch_name: str + schema_changed_at: Timestamp + schema_hash: str + gql_manager: GraphQLSchemaManager + + class GraphQLSchemaManager: # pylint: disable=too-many-public-methods _extra_types: dict[str, GraphQLTypes] = { "DiffSummaryElementAttribute": DiffSummaryElementAttribute, "DiffSummaryElementRelationshipOne": DiffSummaryElementRelationshipOne, "DiffSummaryElementRelationshipMany": DiffSummaryElementRelationshipMany, } + _branch_details_by_name: dict[str, BranchDetails] = {} + + @classmethod + def clear_cache(cls) -> None: + cls._branch_details_by_name = {} + + @classmethod + def _cache_branch( + cls, branch: Branch, schema_branch: SchemaBranch, schema_hash: str | None = None + ) -> BranchDetails: + if not schema_hash: + if branch.schema_hash: + schema_hash = branch.schema_hash.main + else: + schema_hash = schema_branch.get_hash() + branch_details = BranchDetails( + branch_name=branch.name, + schema_changed_at=Timestamp(branch.schema_changed_at) if branch.schema_changed_at else Timestamp(), + schema_hash=schema_hash, + gql_manager=cls(schema=schema_branch), + ) + cls._branch_details_by_name[branch.name] = branch_details + return branch_details + + @classmethod + def get_manager_for_branch(cls, branch: Branch, schema_branch: SchemaBranch) -> GraphQLSchemaManager: + if branch.name not in cls._branch_details_by_name: + branch_details = cls._cache_branch(branch=branch, schema_branch=schema_branch) + return branch_details.gql_manager + cached_branch_details = cls._branch_details_by_name[branch.name] + # try to use the schema_changed_at time b/c it is faster than checking the hash + if branch.schema_changed_at: + changed_at_time = Timestamp(branch.schema_changed_at) + if changed_at_time > cached_branch_details.schema_changed_at: + cached_branch_details = cls._cache_branch(branch=branch, schema_branch=schema_branch) + return cached_branch_details.gql_manager + if branch.schema_hash: + current_hash = branch.active_schema_hash.main + else: + current_hash = schema_branch.get_hash() + if cached_branch_details.schema_hash != current_hash: + cached_branch_details = cls._cache_branch( + branch=branch, schema_branch=schema_branch, schema_hash=current_hash + ) + + return cached_branch_details.gql_manager - def __init__(self, schema: SchemaBranch): + def __init__(self, schema: SchemaBranch) -> None: self.schema = schema + self._full_graphql_schema: GraphQLSchema | None = None self._graphql_types: dict[str, GraphQLTypes] = {} self._load_attribute_types() @@ -104,6 +170,27 @@ def __init__(self, schema: SchemaBranch): self._load_all_enum_types(node_schemas=self.schema.get_all().values()) self._load_node_interface() + def get_graphql_types(self) -> dict[str, GraphQLTypes]: + return self._graphql_types + + def get_graphql_schema( + self, + include_query: bool = True, + include_mutation: bool = True, + include_subscription: bool = True, + include_types: bool = True, + ) -> GraphQLSchema: + if all((include_query, include_mutation, include_subscription, include_types)): + if not self._full_graphql_schema: + self._full_graphql_schema = self.generate() + return self._full_graphql_schema + return self.generate( + include_query=include_query, + include_mutation=include_mutation, + include_subscription=include_subscription, + include_types=include_types, + ) + def generate( self, include_query: bool = True, @@ -121,7 +208,16 @@ def generate( query = self.get_gql_query() if include_query else None mutation = self.get_gql_mutation() if include_mutation else None - subscription = self.get_gql_subscription() if include_subscription else None + subscription = None + if include_subscription: + partial_graphene_schema = graphene.Schema( + query=query, + mutation=mutation, + types=types, + auto_camelcase=False, + directives=DIRECTIVES, + ) + subscription = self.get_gql_subscription(partial_graphql_schema=partial_graphene_schema.graphql_schema) graphene_schema = graphene.Schema( query=query, @@ -150,9 +246,9 @@ class Mutation(InfrahubBaseMutation, MutationMixin): # type: ignore return Mutation - def get_gql_subscription(self) -> type[InfrahubBaseSubscription]: + def get_gql_subscription(self, partial_graphql_schema: graphene.Schema) -> type[InfrahubBaseSubscription]: class Subscription(InfrahubBaseSubscription): - pass + graphql_schema = partial_graphql_schema return Subscription @@ -235,12 +331,12 @@ def _load_enum_type(self, node_schema: MainSchemaTypes) -> None: def _get_related_input_type(self, relationship: RelationshipSchema) -> type[RelatedNodeInput]: peer_schema = self.schema.get(name=relationship.peer, duplicate=False) if (isinstance(peer_schema, NodeSchema) and peer_schema.is_ip_prefix()) or ( - isinstance(peer_schema, GenericSchema) and InfrahubKind.IPPREFIX == relationship.peer + isinstance(peer_schema, GenericSchema) and relationship.peer == InfrahubKind.IPPREFIX ): return RelatedPrefixNodeInput if (isinstance(peer_schema, NodeSchema) and peer_schema.is_ip_address()) or ( - isinstance(peer_schema, GenericSchema) and InfrahubKind.IPADDRESS == relationship.peer + isinstance(peer_schema, GenericSchema) and relationship.peer == InfrahubKind.IPADDRESS ): return RelatedIPAddressNodeInput @@ -410,6 +506,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 +975,11 @@ def generate_graphql_paginated_object( "Meta": type("Meta", (object,), meta_attrs), } + if isinstance(schema, (NodeSchema, GenericSchema)): + main_attrs["permissions"] = graphene.Field( + PaginatedObjectPermission, required=True, resolver=parent_field_name_resolver + ) + 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..ff97853eb1 100644 --- a/backend/infrahub/graphql/mutations/account.py +++ b/backend/infrahub/graphql/mutations/account.py @@ -2,8 +2,8 @@ from graphene import Boolean, Field, InputField, InputObjectType, Mutation, String from graphql import GraphQLResolveInfo -from infrahub_sdk import UUIDT from infrahub_sdk.utils import extract_fields +from infrahub_sdk.uuidt import UUIDT from typing_extensions import Self from infrahub.auth import AuthType @@ -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..ff4daa262c 100644 --- a/backend/infrahub/graphql/mutations/artifact_definition.py +++ b/backend/infrahub/graphql/mutations/artifact_definition.py @@ -6,8 +6,9 @@ from typing_extensions import Self from infrahub.core.schema import NodeSchema +from infrahub.git.models import RequestArtifactDefinitionGenerate from infrahub.log import get_logger -from infrahub.message_bus import messages +from infrahub.workflows.catalogue import REQUEST_ARTIFACT_DEFINITION_GENERATE from .main import InfrahubMutationMixin, InfrahubMutationOptions @@ -17,7 +18,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 +45,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,22 +53,19 @@ 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) - - events = [ - messages.RequestArtifactDefinitionGenerate(artifact_definition=artifact_definition.id, branch=branch.name), - ] + artifact_definition, result = await super().mutate_create(info=info, data=data, branch=branch, at=at) if context.service: - for event in events: - await context.service.send(message=event) + model = RequestArtifactDefinitionGenerate(branch=branch.name, artifact_definition=artifact_definition.id) + await context.service.workflow.submit_workflow( + workflow=REQUEST_ARTIFACT_DEFINITION_GENERATE, parameters={"model": model} + ) return artifact_definition, result @classmethod async def mutate_update( cls, - root: dict, info: GraphQLResolveInfo, data: InputObjectType, branch: Branch, @@ -78,14 +75,12 @@ 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) - - events = [ - messages.RequestArtifactDefinitionGenerate(artifact_definition=artifact_definition.id, branch=branch.name), - ] + artifact_definition, result = await super().mutate_update(info=info, data=data, branch=branch, at=at) if context.service: - for event in events: - await context.service.send(message=event) + model = RequestArtifactDefinitionGenerate(branch=branch.name, artifact_definition=artifact_definition.id) + await context.service.workflow.submit_workflow( + workflow=REQUEST_ARTIFACT_DEFINITION_GENERATE, parameters={"model": model} + ) return artifact_definition, result diff --git a/backend/infrahub/graphql/mutations/attribute.py b/backend/infrahub/graphql/mutations/attribute.py index 244ad10ab8..a55be26122 100644 --- a/backend/infrahub/graphql/mutations/attribute.py +++ b/backend/infrahub/graphql/mutations/attribute.py @@ -1,3 +1,5 @@ +from typing import Any + from graphene import BigInt, Boolean, Field, InputObjectType, Int, String from graphene.types.generic import GenericScalar @@ -12,7 +14,7 @@ class BaseAttributeCreate(InputObjectType): owner = String(required=False) @classmethod - def __init_subclass__(cls, **kwargs): + def __init_subclass__(cls, **kwargs: dict[str, Any]) -> None: super().__init_subclass__(**kwargs) registry.input_type[cls.__name__] = cls @@ -25,7 +27,7 @@ class BaseAttributeUpdate(InputObjectType): owner = String(required=False) @classmethod - def __init_subclass__(cls, **kwargs): + def __init_subclass__(cls, **kwargs: dict[str, Any]) -> 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..d556c1a6ca 100644 --- a/backend/infrahub/graphql/mutations/branch.py +++ b/backend/infrahub/graphql/mutations/branch.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Optional +from typing import TYPE_CHECKING, Any import pydantic from graphene import Boolean, Field, InputField, InputObjectType, List, Mutation, String @@ -8,28 +8,32 @@ from opentelemetry import trace from typing_extensions import Self -from infrahub import config, lock +from infrahub import lock from infrahub.core import registry from infrahub.core.branch import Branch from infrahub.core.diff.branch_differ import BranchDiffer -from infrahub.core.diff.ipam_diff_parser import IpamDiffParser +from infrahub.core.diff.coordinator import DiffCoordinator +from infrahub.core.diff.merger.merger import DiffMerger +from infrahub.core.diff.repository.repository import DiffRepository from infrahub.core.merge import BranchMerger -from infrahub.core.migrations.schema.runner import schema_migrations_runner from infrahub.core.task import UserTask -from infrahub.core.validators.checker import schema_validators_checker +from infrahub.core.validators.determiner import ConstraintValidatorDeterminer +from infrahub.core.validators.models.validate_migration import SchemaValidateMigrationData +from infrahub.core.validators.tasks import schema_validate_migrations from infrahub.database import retry_db_transaction +from infrahub.dependencies.registry import get_component_registry from infrahub.exceptions import BranchNotFoundError, 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 infrahub.workflows.catalogue import BRANCH_MERGE, BRANCH_REBASE from ..types import BranchType if TYPE_CHECKING: from graphql import GraphQLResolveInfo - from .. import GraphqlContext + from ..initialization import GraphqlContext # pylint: disable=unused-argument @@ -183,85 +187,22 @@ class Arguments: object = Field(BranchType) @classmethod - @retry_db_transaction(name="branch_rebase") async def mutate(cls, root: dict, info: GraphQLResolveInfo, data: BranchNameInput) -> Self: context: GraphqlContext = info.context if not context.service: raise ValueError("Service must be provided to rebase a branch.") - async with UserTask.from_graphql_context(title=f"Rebase branch : {data.name}", context=context) as task: - obj = await Branch.get_by_name(db=context.db, name=str(data.name)) - merger = BranchMerger(db=context.db, source_branch=obj, service=context.service) - - # If there are some changes related to the schema between this branch and main, we need to - # - Run all the validations to ensure everything if correct before rebasing the branch - # - Run all the migrations after the rebase - if obj.has_schema_changes: - candidate_schema = merger.get_candidate_schema() - constraints = await merger.calculate_validations(target_schema=candidate_schema) - error_messages, _ = await schema_validators_checker( - branch=obj, schema=candidate_schema, constraints=constraints, service=context.service - ) - if error_messages: - raise ValidationError(",\n".join(error_messages)) - - schema_in_main_before = merger.destination_schema.duplicate() - - async with context.db.start_transaction() as dbt: - await obj.rebase(db=dbt) - await task.info(message="Branch successfully rebased", db=dbt) - - if obj.has_schema_changes: - # NOTE there is a bit additional work in order to calculate a proper diff that will - # allow us to pull only the part of the schema that has changed, for now the safest option is to pull - # Everything - # schema_diff = await merger.has_schema_changes() - updated_schema = await registry.schema.load_schema_from_db( - db=context.db, - branch=obj, - # schema=merger.source_schema.duplicate(), - # schema_diff=schema_diff, - ) - registry.schema.set_schema_branch(name=obj.name, schema=updated_schema) - obj.update_schema_hash() - await obj.save(db=context.db) - - # Execute the migrations - migrations = await merger.calculate_migrations(target_schema=updated_schema) - - errors = await schema_migrations_runner( - branch=merger.source_branch, - new_schema=candidate_schema, - previous_schema=schema_in_main_before, - migrations=migrations, - service=context.service, - ) - for error in errors: - context.service.log.error(error) + obj = await Branch.get_by_name(db=context.db, name=str(data.name)) - fields = await extract_fields_first_node(info=info) + await context.service.workflow.execute_workflow(workflow=BRANCH_REBASE, parameters={"branch": obj.name}) - ok = True - - log_data = get_log_data() - request_id = log_data.get("request_id", "") - differ = await merger.get_graph_diff() - diff_parser = IpamDiffParser( - db=context.db, - differ=differ, - source_branch_name=obj.name, - target_branch_name=registry.default_branch, - ) - ipam_node_details = await diff_parser.get_changed_ipam_node_details() - message = messages.EventBranchRebased( - branch=obj.name, - ipam_node_details=ipam_node_details, - meta=Meta(initiator_id=WORKER_IDENTITY, request_id=request_id), - ) - await context.service.send(message=message) + # Pull the latest information about the branch from the database directly + obj = await Branch.get_by_name(db=context.db, name=str(data.name)) + fields = await extract_fields_first_node(info=info) + ok = True - return cls(object=await obj.to_graphql(fields=fields.get("object", {})), ok=ok) + return cls(object=await obj.to_graphql(fields=fields.get("object", {})), ok=ok) class BranchValidate(Mutation): @@ -305,53 +246,55 @@ class Arguments: object = Field(BranchType) @classmethod - @retry_db_transaction(name="branch_merge") async def mutate(cls, root: dict, info: GraphQLResolveInfo, data: BranchNameInput) -> Self: context: GraphqlContext = info.context - async with UserTask.from_graphql_context(title=f"Merge branch: {data['name']}", context=context) as task: - obj = await Branch.get_by_name(db=context.db, name=data["name"]) - - merger: Optional[BranchMerger] = None - async with lock.registry.global_graph_lock(): - async with context.db.start_transaction() as db: - merger = BranchMerger(db=db, source_branch=obj, service=context.service) - await merger.merge() - await merger.update_schema() - - fields = await extract_fields(info.field_nodes[0].selection_set) + if not context.service: + raise ValueError("Service must be provided to merge a branch.") - ok = True + obj = await Branch.get_by_name(db=context.db, name=data["name"]) + base_branch = await Branch.get_by_name(db=context.db, name=registry.default_branch) + + component_registry = get_component_registry() + diff_coordinator = await component_registry.get_component(DiffCoordinator, db=context.db, branch=obj) + diff_repository = await component_registry.get_component(DiffRepository, db=context.db, branch=obj) + diff_merger = await component_registry.get_component(DiffMerger, db=context.db, branch=obj) + enriched_diff = await diff_coordinator.update_branch_diff(base_branch=base_branch, diff_branch=obj) + if enriched_diff.get_all_conflicts(): + raise ValidationError( + f"Branch {obj.name} contains conflicts with the default branch." + " Please create a Proposed Change to resolve the conflicts or manually update them before merging." + ) + node_diff_field_summaries = await diff_repository.get_node_field_summaries( + diff_branch_name=enriched_diff.diff_branch_name, diff_id=enriched_diff.uuid + ) + + merger = BranchMerger( + db=context.db, + diff_coordinator=diff_coordinator, + diff_merger=diff_merger, + source_branch=obj, + service=context.service, + ) + candidate_schema = merger.get_candidate_schema() + determiner = ConstraintValidatorDeterminer(schema_branch=candidate_schema) + constraints = await determiner.get_constraints(node_diffs=node_diff_field_summaries) + if obj.has_schema_changes: + constraints += await merger.calculate_validations(target_schema=candidate_schema) + + if constraints: + error_messages = await schema_validate_migrations( + message=SchemaValidateMigrationData(branch=obj, schema_branch=candidate_schema, constraints=constraints) + ) + if error_messages: + raise ValidationError(",\n".join(error_messages)) - if merger and merger.migrations and context.service: - errors = await schema_migrations_runner( - branch=merger.destination_branch, - new_schema=merger.destination_schema, - previous_schema=merger.initial_source_schema, - migrations=merger.migrations, - service=context.service, - ) - for error in errors: - await task.error(message=error) + await context.service.workflow.execute_workflow(workflow=BRANCH_MERGE, parameters={"branch": obj.name}) - if config.SETTINGS.broker.enable and context.background: - log_data = get_log_data() - request_id = log_data.get("request_id", "") + # Pull the latest information about the branch from the database directly + obj = await Branch.get_by_name(db=context.db, name=data["name"]) - differ = await merger.get_graph_diff() - diff_parser = IpamDiffParser( - db=context.db, - differ=differ, - source_branch_name=obj.name, - target_branch_name=registry.default_branch, - ) - ipam_node_details = await diff_parser.get_changed_ipam_node_details() - message = messages.EventBranchMerge( - source_branch=obj.name, - target_branch=registry.default_branch, - ipam_node_details=ipam_node_details, - meta=Meta(initiator_id=WORKER_IDENTITY, request_id=request_id), - ) - context.background.add_task(services.send, message) + fields = await extract_fields(info.field_nodes[0].selection_set) + ok = True - return cls(object=await obj.to_graphql(fields=fields.get("object", {})), ok=ok) + return cls(object=await obj.to_graphql(fields=fields.get("object", {})), ok=ok) diff --git a/backend/infrahub/graphql/mutations/diff.py b/backend/infrahub/graphql/mutations/diff.py index 5c2e32f137..631bc3d3ca 100644 --- a/backend/infrahub/graphql/mutations/diff.py +++ b/backend/infrahub/graphql/mutations/diff.py @@ -5,11 +5,12 @@ from infrahub.core import registry from infrahub.core.diff.coordinator import DiffCoordinator +from infrahub.core.diff.models import RequestDiffUpdate from infrahub.dependencies.registry import get_component_registry -from infrahub.message_bus import messages +from infrahub.workflows.catalogue import REQUEST_DIFF_UPDATE if TYPE_CHECKING: - from .. import GraphqlContext + from ..initialization import GraphqlContext class DiffUpdateInput(InputObjectType): @@ -55,13 +56,13 @@ async def mutate( return {"ok": True} - message = messages.RequestDiffUpdate( + model = RequestDiffUpdate( branch_name=str(data.branch), name=data.name, from_time=from_timestamp_str, to_time=to_timestamp_str, ) if context.service: - await context.service.send(message=message) + await context.service.workflow.submit_workflow(workflow=REQUEST_DIFF_UPDATE, parameters={"model": model}) return {"ok": True} 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..3bf8c0761e 100644 --- a/backend/infrahub/graphql/mutations/proposed_change.py +++ b/backend/infrahub/graphql/mutations/proposed_change.py @@ -3,13 +3,17 @@ from graphene import Boolean, InputObjectType, Mutation, String from graphql import GraphQLResolveInfo -from infrahub import lock +from infrahub.core.account import GlobalPermission from infrahub.core.branch import Branch -from infrahub.core.constants import CheckType, InfrahubKind, ProposedChangeState, ValidatorConclusion -from infrahub.core.diff.ipam_diff_parser import IpamDiffParser +from infrahub.core.constants import ( + CheckType, + GlobalPermissions, + InfrahubKind, + PermissionDecision, + ProposedChangeState, + ValidatorConclusion, +) from infrahub.core.manager import NodeManager -from infrahub.core.merge import BranchMerger -from infrahub.core.migrations.schema.runner import schema_migrations_runner from infrahub.core.node import Node from infrahub.core.registry import registry from infrahub.core.schema import NodeSchema @@ -17,15 +21,13 @@ from infrahub.exceptions import BranchNotFoundError, ValidationError from infrahub.graphql.mutations.main import InfrahubMutationMixin from infrahub.graphql.types.enums import CheckType as GraphQLCheckType -from infrahub.log import get_log_data -from infrahub.message_bus import Meta, messages -from infrahub.services import services -from infrahub.worker import WORKER_IDENTITY +from infrahub.message_bus import messages +from infrahub.workflows.catalogue import BRANCH_MERGE from .main import InfrahubMutationOptions if TYPE_CHECKING: - from .. import GraphqlContext + from ..initialization import GraphqlContext class InfrahubProposedChangeMutation(InfrahubMutationMixin, Mutation): @@ -45,7 +47,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 +58,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 +86,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 +97,22 @@ 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_session=context.active_account_session, + permission=GlobalPermission( + action=GlobalPermissions.MERGE_PROPOSED_CHANGE.value, + decision=PermissionDecision.ALLOW_ALL.value, + ), + 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 +130,13 @@ async def mutate_update( updated_state = ProposedChangeState(state_update) state.validate_state_transition(updated_state) - merger: Optional[BranchMerger] = None + # 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") + 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: @@ -143,40 +162,10 @@ async def mutate_update( keep_source_value = check.keep_branch.value.value == "source" conflict_resolution[check.conflicts.value[0]["path"]] = keep_source_value - async with lock.registry.global_graph_lock(): - merger = BranchMerger(db=dbt, source_branch=source_branch, service=context.service) - await merger.merge(conflict_resolution=conflict_resolution) - await merger.update_schema() - - if context.background: - log_data = get_log_data() - request_id = log_data.get("request_id", "") - differ = await merger.get_graph_diff() - diff_parser = IpamDiffParser( - db=context.db, - differ=differ, - source_branch_name=obj.name, - target_branch_name=registry.default_branch, - ) - ipam_node_details = await diff_parser.get_changed_ipam_node_details() - message = messages.EventBranchMerge( - source_branch=source_branch.name, - target_branch=registry.default_branch, - ipam_node_details=ipam_node_details, - meta=Meta(initiator_id=WORKER_IDENTITY, request_id=request_id), - ) - context.background.add_task(services.send, message) - - if merger and merger.migrations: - errors = await schema_migrations_runner( - branch=merger.destination_branch, - new_schema=merger.destination_schema, - previous_schema=merger.initial_source_schema, - migrations=merger.migrations, - service=context.service, - ) - for error in errors: - context.service.log.error(error) + await context.service.workflow.execute_workflow( + workflow=BRANCH_MERGE, + parameters={"branch": source_branch.name, "conflict_resolution": conflict_resolution}, + ) return proposed_change, result 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..0c99e6f407 --- /dev/null +++ b/backend/infrahub/graphql/permissions.py @@ -0,0 +1,35 @@ +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_object: MainSchemaTypes + try: + schema_object = registry.schema.get_node_schema(name=node_name, branch=context.branch, duplicate=False) + except ValueError: + schema_object = registry.schema.get_profile_schema( + name=node_name, branch=context.branch, duplicate=False + ) + schema_objects.append(schema_object) + + 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..28e2a7faab 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,102 @@ 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) + description = Field(String, required=False) + 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) + description = Field(String, required=False) + 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_session=context.account_session, 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, + "description": obj.description, + "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, + "description": obj.description, + "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..85bafba6d0 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) @@ -384,7 +384,7 @@ async def resolve( base_branch = await registry.get_branch(db=context.db, branch=registry.default_branch) diff_branch = await registry.get_branch(db=context.db, branch=branch) diff_repo = await component_registry.get_component(DiffRepository, db=context.db, branch=diff_branch) - branch_start_timestamp = Timestamp(diff_branch.get_created_at()) + branch_start_timestamp = Timestamp(diff_branch.get_branched_from()) if from_time: from_timestamp = Timestamp(from_time.isoformat()) else: @@ -454,7 +454,7 @@ async def summary( base_branch = await registry.get_branch(db=context.db, branch=registry.default_branch) diff_branch = await registry.get_branch(db=context.db, branch=branch) diff_repo = await component_registry.get_component(DiffRepository, db=context.db, branch=diff_branch) - branch_start_timestamp = Timestamp(diff_branch.get_created_at()) + branch_start_timestamp = Timestamp(diff_branch.get_branched_from()) if from_time: from_timestamp = Timestamp(from_time.isoformat()) else: 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..7fb6a75e22 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,93 @@ 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 parent_field_name_resolver(parent: dict[str, dict], info: GraphQLResolveInfo) -> dict: + """This resolver gets used when we know that the parent resolver has already gathered the required information. + + An example of this is the permissions field at the top level within default_paginated_list_resolver() + """ + + return parent[info.field_name] + + +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", {}) + + permission_set: Optional[dict[str, Any]] = None + permissions = await get_permissions(db=db, schema=schema, context=context) if context.account_session else None + if fields.get("permissions"): + response["permissions"] = permissions + + if permissions: + for edge in permissions["edges"]: + if edge["node"]["kind"] == schema.kind: + permission_set = edge["node"] + + 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, + permissions=permission_set, + ) + } + 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/__init__.py b/backend/infrahub/graphql/subscription/__init__.py index 8f9ed088c4..f023a34590 100644 --- a/backend/infrahub/graphql/subscription/__init__.py +++ b/backend/infrahub/graphql/subscription/__init__.py @@ -1,23 +1,37 @@ -from typing import Any, Iterable, Optional +from typing import Any, AsyncGenerator -from graphene import ObjectType +from graphene import Field, Int, ObjectType, Schema, String +from graphene.types.generic import GenericScalar from graphql import GraphQLResolveInfo -from .graphql_query import GraphQLQuerySubscription, resolver_graphql_query +from .graphql_query import resolver_graphql_query + +GraphQLQuerySubscription = Field( + GenericScalar(), + name=String(), + params=GenericScalar(required=False), + interval=Int(required=False), +) class InfrahubBaseSubscription(ObjectType): query = GraphQLQuerySubscription + graphql_schema: Schema | None = None - @staticmethod + @classmethod async def subscribe_query( + cls, parent: dict, # pylint: disable=unused-argument info: GraphQLResolveInfo, name: str, - params: Optional[dict[str, Any]] = None, - interval: Optional[int] = 10, - ) -> Iterable[dict]: + params: dict[str, Any] | None = None, + interval: int | None = 10, + ) -> AsyncGenerator[dict[str, Any], None]: + if not cls.graphql_schema: + raise RuntimeError("Subscription initialized without graphql schema") + if not interval: + interval = 10 async for result in resolver_graphql_query( - parent=parent, info=info, name=name, params=params, interval=interval + parent=parent, info=info, name=name, graphql_schema=cls.graphql_schema, params=params, interval=interval ): yield result diff --git a/backend/infrahub/graphql/subscription/graphql_query.py b/backend/infrahub/graphql/subscription/graphql_query.py index 3eb1d6c536..45b3e6745f 100644 --- a/backend/infrahub/graphql/subscription/graphql_query.py +++ b/backend/infrahub/graphql/subscription/graphql_query.py @@ -1,11 +1,9 @@ import asyncio from typing import TYPE_CHECKING, Any, AsyncGenerator -from graphene import Field, Int, String -from graphene.types.generic import GenericScalar +from graphene import Schema from graphql import GraphQLResolveInfo, graphql -from infrahub.core import registry from infrahub.core.constants import InfrahubKind from infrahub.core.manager import NodeManager from infrahub.core.protocols import CoreGraphQLQuery @@ -13,7 +11,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") @@ -22,6 +20,7 @@ async def resolver_graphql_query( parent: dict, # pylint: disable=unused-argument info: GraphQLResolveInfo, name: str, + graphql_schema: Schema, params: dict[str, Any] | None = None, interval: int = 10, ) -> AsyncGenerator[dict[str, Any], None]: @@ -36,9 +35,6 @@ async def resolver_graphql_query( if not graphql_query: raise ValueError(f"Unable to find the {InfrahubKind.GRAPHQLQUERY} {name}") - schema_branch = registry.schema.get_schema_branch(name=context.branch.name) - graphql_schema = schema_branch.get_graphql_schema() - while True: async with context.db.start_session() as db: result = await graphql( @@ -54,11 +50,3 @@ async def resolver_graphql_query( yield result.data await asyncio.sleep(delay=float(interval)) - - -GraphQLQuerySubscription = Field( - GenericScalar(), - name=String(), - params=GenericScalar(required=False), - interval=Int(required=False), -) 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..7c9c4868bb 100644 --- a/backend/infrahub/graphql/types/attribute.py +++ b/backend/infrahub/graphql/types/attribute.py @@ -1,10 +1,13 @@ 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 from infrahub.core import registry +from .enums import BranchRelativePermissionDecision from .interface import InfrahubInterface @@ -53,6 +56,10 @@ class RelatedPrefixNodeInput(InputObjectType): _relation__source = String(required=False) +class PermissionType(ObjectType): + update_value = Field(BranchRelativePermissionDecision, required=False) + + class AttributeInterface(InfrahubInterface): is_default = Field(Boolean) is_inherited = Field(Boolean) @@ -68,9 +75,10 @@ class AttributeInterface(InfrahubInterface): class BaseAttribute(ObjectType): id = Field(String) is_from_profile = Field(Boolean) + permissions = Field(PermissionType, required=False) @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..0ea9d67ea8 100644 --- a/backend/infrahub/graphql/types/enums.py +++ b/backend/infrahub/graphql/types/enums.py @@ -1,7 +1,10 @@ from graphene import Enum from infrahub.core import constants +from infrahub.permissions import constants as permission_constants CheckType = Enum.from_enum(constants.CheckType) Severity = Enum.from_enum(constants.Severity) + +BranchRelativePermissionDecision = Enum.from_enum(permission_constants.BranchRelativePermissionDecision) 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..19a9d949b2 --- /dev/null +++ b/backend/infrahub/graphql/types/permission.py @@ -0,0 +1,42 @@ +from __future__ import annotations + +from graphene import Field, Int, List, ObjectType, String + +from infrahub.graphql.types.enums import BranchRelativePermissionDecision + + +class ObjectPermission(ObjectType): + kind = Field(String, required=True, description="The kind this permission refers to.") + view = Field( + BranchRelativePermissionDecision, + required=True, + description="Indicates the permission level for the read action.", + ) + create = Field( + BranchRelativePermissionDecision, + required=True, + description="Indicates the permission level for the create action.", + ) + update = Field( + BranchRelativePermissionDecision, + required=True, + description="Indicates the permission level for the update action.", + ) + delete = Field( + BranchRelativePermissionDecision, + required=True, + description="Indicates the permission level for the delete action.", + ) + + +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/backend/infrahub/log.py b/backend/infrahub/log.py index 5e3a5cb621..1ccbf2e0d5 100644 --- a/backend/infrahub/log.py +++ b/backend/infrahub/log.py @@ -1,3 +1,4 @@ +import importlib import logging import os from typing import TYPE_CHECKING, Any @@ -29,6 +30,11 @@ def set_log_data(key: str, value: Any) -> None: def configure_logging(production: bool = True, log_level: str = "INFO") -> None: + # Importing prefect.main here triggers prefect.logging.configuration.setup_logging() + # to be executed, this function wipes out the previous logging configuration and + # starts from a clean slate. After this has been imported once we can reinject + # the infrahub logger + importlib.import_module("prefect.main") shared_processors: list[Processor] = [ structlog.contextvars.merge_contextvars, structlog.processors.StackInfoRenderer(), @@ -36,6 +42,7 @@ def configure_logging(production: bool = True, log_level: str = "INFO") -> None: structlog.stdlib.add_logger_name, structlog.stdlib.add_log_level, ] + logging.getLogger("httpx").setLevel(logging.ERROR) if production: shared_processors.append(structlog.processors.format_exc_info) @@ -60,6 +67,10 @@ def configure_logging(production: bool = True, log_level: str = "INFO") -> None: handler = logging.StreamHandler() handler.setFormatter(formatter) root_logger = logging.getLogger() + for existing_handler in root_logger.handlers: + if isinstance(existing_handler, logging.StreamHandler): + root_logger.removeHandler(existing_handler) + root_logger.addHandler(handler) root_logger.setLevel(log_level) diff --git a/sync/examples/infrahub_to_peering-manager/peeringmanager/__init__.py b/backend/infrahub/menu/__init__.py similarity index 100% rename from sync/examples/infrahub_to_peering-manager/peeringmanager/__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..499428a35b --- /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 = "BuiltinOther" diff --git a/backend/infrahub/menu/generator.py b/backend/infrahub/menu/generator.py new file mode 100644 index 0000000000..49459797fc --- /dev/null +++ b/backend/infrahub/menu/generator.py @@ -0,0 +1,149 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from infrahub.core import registry +from infrahub.core.protocols import CoreMenuItem +from infrahub.log import get_logger +from infrahub.permissions.constants import AssignedPermissions +from infrahub.permissions.local_backend import LocalPermissionBackend + +from .constants import FULL_DEFAULT_MENU +from .models import MenuDict, MenuItemDict + +if TYPE_CHECKING: + from infrahub.auth import AccountSession + from infrahub.core.branch import Branch + from infrahub.database import InfrahubDatabase + +log = get_logger() + + +def get_full_name(obj: CoreMenuItem) -> str: + return f"{obj.namespace.value}{obj.name.value}" + + +async def generate_restricted_menu( + db: InfrahubDatabase, branch: Branch, menu_items: list[CoreMenuItem], account: AccountSession | None = None +) -> MenuDict: + menu = await generate_menu(db=db, branch=branch, menu_items=menu_items) + + permissions = AssignedPermissions(global_permissions=[], object_permissions=[]) + perm_backend = LocalPermissionBackend() + + if account: + permissions = await perm_backend.load_permissions(db=db, account_session=account, branch=branch) + + for item in menu.data.values(): + has_permission: bool | None = None + for permission in item.get_global_permissions(): + has_permission = perm_backend.resolve_global_permission( + permissions=permissions["global_permissions"], permission_to_check=permission + ) + if has_permission: + has_permission = True + elif has_permission is None: + has_permission = False + + if has_permission is False: + item.hidden = True + + return menu + + +# pylint: disable=too-many-branches,too-many-statements +async def generate_menu(db: InfrahubDatabase, branch: Branch, menu_items: list[CoreMenuItem]) -> MenuDict: + structure = MenuDict() + full_schema = registry.schema.get_full(branch=branch, duplicate=False) + + already_processed = [] + + # Process the parent first + for item in menu_items: + full_name = get_full_name(item) + parent1 = await item.parent.get_peer(db=db, peer_type=CoreMenuItem) + if parent1: + continue + structure.data[full_name] = MenuItemDict.from_node(obj=item) + already_processed.append(full_name) + + # Process the children + havent_been_processed: list[str] = [] + for item in menu_items: + full_name = get_full_name(item) + if full_name in already_processed: + continue + + parent2 = await item.parent.get_peer(db=db, peer_type=CoreMenuItem) + if not parent2: + havent_been_processed.append(full_name) + continue + + parent_full_name = get_full_name(parent2) + 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=full_name, + parent_item=parent_full_name, + ) + + items_to_add = {schema.kind: False for schema in full_schema.values() if schema.include_in_menu is True} + + nbr_remaining_items_last_round = len(items_to_add.values()) + nbr_remaining_items = len([value for value in items_to_add.values() if value is False]) + while not all(items_to_add.values()): + for item_name, already_done in items_to_add.items(): + if already_done: + continue + + schema = full_schema[item_name] + menu_item = MenuItemDict.from_schema(model=schema) + already_in_schema = bool(structure.find_item(name=menu_item.identifier)) + if already_in_schema: + items_to_add[item_name] = True + continue + + if not schema.menu_placement: + first_element = MenuItemDict.from_schema(model=schema) + first_element.identifier = f"{first_element.identifier}Sub" + first_element.order_weight = 1 + menu_item.children[first_element.identifier] = first_element + structure.data[menu_item.identifier] = menu_item + items_to_add[item_name] = True + elif menu_placement := structure.find_item(name=schema.menu_placement): + menu_placement.children[menu_item.identifier] = menu_item + items_to_add[item_name] = True + continue + + nbr_remaining_items = len([value for value in items_to_add.values() if value is False]) + if nbr_remaining_items_last_round == nbr_remaining_items: + break + nbr_remaining_items_last_round = nbr_remaining_items + + # ---------------------------------------------------------------------------- + # Assign the remaining items for which we couldn't find the menu_placement to the default menu + # ---------------------------------------------------------------------------- + default_menu = structure.find_item(name=FULL_DEFAULT_MENU) + if not default_menu: + raise ValueError("Unable to locate the default menu item") + + for item_name, already_done in items_to_add.items(): + if already_done: + continue + schema = full_schema[item_name] + menu_item = MenuItemDict.from_schema(model=schema) + 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 + items_to_add[item_name] = True + + return structure diff --git a/backend/infrahub/menu/menu.py b/backend/infrahub/menu/menu.py new file mode 100644 index 0000000000..d44bddb3bd --- /dev/null +++ b/backend/infrahub/menu/menu.py @@ -0,0 +1,387 @@ +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, + icon="mdi:cube-outline", + section=MenuSection.OBJECT, + order_weight=10000, + children=[ + MenuItemDefinition( + namespace="Builtin", + name="Tag", + label="Tags", + kind=InfrahubKind.TAG, + protected=True, + icon=_extract_node_icon(infrahub_schema.get(InfrahubKind.TAG)), + section=MenuSection.OBJECT, + order_weight=10000, + ) + ], + ), + MenuItemDefinition( + namespace="Builtin", + name="IPAM", + label="IPAM", + protected=True, + section=MenuSection.OBJECT, + icon="mdi:ip-network", + order_weight=9500, + children=[ + MenuItemDefinition( + namespace="Builtin", + name="IPPrefix", + label="IP Prefixes", + kind=InfrahubKind.IPPREFIX, + path="/ipam/prefixes", + icon=_extract_node_icon(infrahub_schema.get(InfrahubKind.IPPREFIX)), + protected=True, + section=MenuSection.INTERNAL, + order_weight=1000, + ), + MenuItemDefinition( + namespace="Builtin", + name="IPAddress", + label="IP Addresses", + kind=InfrahubKind.IPPREFIX, + path="/ipam/addresses?ipam-tab=ip-details", + icon=_extract_node_icon(infrahub_schema.get(InfrahubKind.IPADDRESS)), + protected=True, + section=MenuSection.INTERNAL, + order_weight=2000, + ), + MenuItemDefinition( + namespace="Builtin", + name="Namespaces", + label="Namespaces", + kind=InfrahubKind.IPNAMESPACE, + icon=_extract_node_icon(infrahub_schema.get(InfrahubKind.IPNAMESPACE)), + protected=True, + section=MenuSection.INTERNAL, + order_weight=3000, + ), + ], + ), + 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=1000, + ), + MenuItemDefinition( + namespace="Builtin", + name="ObjectManagement", + label="Object Management", + icon="mdi:cube-outline", + protected=True, + section=MenuSection.INTERNAL, + order_weight=1500, + 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", + icon="mdi:source-branch", + protected=True, + section=MenuSection.INTERNAL, + order_weight=2000, + children=[ + MenuItemDefinition( + namespace="Builtin", + name="Branches", + label="Branches", + path="/branches", + icon="mdi:layers-triple", + protected=True, + section=MenuSection.INTERNAL, + order_weight=1000, + ), + 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", + icon="mdi:nas", + protected=True, + section=MenuSection.INTERNAL, + order_weight=2500, + 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="Git 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="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="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="Deployment", + label="Deployment", + icon="mdi:rocket-launch", + protected=True, + section=MenuSection.INTERNAL, + order_weight=3000, + children=[ + MenuItemDefinition( + namespace="Builtin", + name="ArtifactMenu", + label="Artifact", + protected=True, + section=MenuSection.INTERNAL, + order_weight=1000, + children=[ + MenuItemDefinition( + namespace="Builtin", + name="Artifact", + label="Artifact", + kind=InfrahubKind.ARTIFACT, + icon=_extract_node_icon(infrahub_schema.get(InfrahubKind.ARTIFACT)), + protected=True, + section=MenuSection.INTERNAL, + order_weight=1000, + ), + MenuItemDefinition( + namespace="Builtin", + name="ArtifactDefinition", + label="Artifact Definition", + kind=InfrahubKind.ARTIFACTDEFINITION, + icon=_extract_node_icon(infrahub_schema.get(InfrahubKind.ARTIFACTDEFINITION)), + protected=True, + section=MenuSection.INTERNAL, + order_weight=1000, + ), + ], + ), + MenuItemDefinition( + namespace="Builtin", + name="GeneratorMenu", + label="Generator", + protected=True, + section=MenuSection.INTERNAL, + order_weight=1000, + children=[ + MenuItemDefinition( + namespace="Builtin", + name="GeneratorInstance", + label="Generator Instance", + kind=InfrahubKind.GENERATORINSTANCE, + icon=_extract_node_icon(infrahub_schema.get(InfrahubKind.GENERATORINSTANCE)), + protected=True, + section=MenuSection.INTERNAL, + order_weight=1000, + ), + MenuItemDefinition( + namespace="Builtin", + name="GeneratorDefinition", + label="Generator Definition", + kind=InfrahubKind.GENERATORDEFINITION, + icon=_extract_node_icon(infrahub_schema.get(InfrahubKind.GENERATORDEFINITION)), + protected=True, + section=MenuSection.INTERNAL, + order_weight=2000, + ), + ], + ), + MenuItemDefinition( + namespace="Builtin", + name="Transformation", + label="Transformation", + kind=InfrahubKind.TRANSFORM, + icon=_extract_node_icon(infrahub_schema.get(InfrahubKind.TRANSFORM)), + protected=True, + section=MenuSection.INTERNAL, + order_weight=3000, + ), + ], + ), + MenuItemDefinition( + namespace="Builtin", + name="Integration", + label="Integrations", + icon="mdi:connection", + protected=True, + section=MenuSection.INTERNAL, + order_weight=3500, + children=[ + 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, + ), + ], + ), + ], + ), + MenuItemDefinition( + namespace="Builtin", + name="Admin", + label="Admin", + icon="mdi:settings-outline", + protected=True, + section=MenuSection.INTERNAL, + order_weight=10000, + permissions=["global:super_admin:allow_all"], + children=[ + MenuItemDefinition( + namespace="Builtin", + name="RoleManagement", + label="Users & Permissions", + path="/role-management", + icon=_extract_node_icon(infrahub_schema.get(InfrahubKind.BASEPERMISSION)), + protected=True, + section=MenuSection.INTERNAL, + order_weight=1000, + ), + MenuItemDefinition( + namespace="Builtin", + name="Menu", + label="Menu", + kind=InfrahubKind.MENU, + icon="mdi:menu", + protected=True, + section=MenuSection.INTERNAL, + order_weight=2500, + ), + ], + ), +] diff --git a/backend/infrahub/menu/models.py b/backend/infrahub/menu/models.py new file mode 100644 index 0000000000..ff944c04cd --- /dev/null +++ b/backend/infrahub/menu/models.py @@ -0,0 +1,176 @@ +from __future__ import annotations + +from dataclasses import dataclass, field +from typing import TYPE_CHECKING + +from pydantic import BaseModel, Field +from typing_extensions import Self + +from infrahub.core.account import GlobalPermission +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 value in self.data.values() if value.section == section and value.hidden is False + ] + 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") + label: 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 + permissions: list[str] = Field(default_factory=list) + + @classmethod + def from_node(cls, obj: CoreMenuItem) -> Self: + return cls( + identifier=get_full_name(obj), + label=obj.label.value or "", + icon=obj.icon.value or "", + order_weight=obj.order_weight.value, + path=obj.path.value or "", + kind=obj.kind.value or "", + section=obj.section.value, + permissions=obj.required_permissions.value or [], + ) + + @classmethod + def from_schema(cls, model: NodeSchema | GenericSchema | ProfileSchema) -> Self: + return cls( + identifier=get_full_name(model), + label=model.label or model.kind, + path=f"/objects/{model.kind}", + icon=model.icon or "", + kind=model.kind, + ) + + +class MenuItemDict(MenuItem): + hidden: bool = False + 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() if child.hidden is False] + data["children"] = sorted(unsorted_children, key=lambda d: d.order_weight) + return MenuItemList(**data) + + def get_global_permissions(self) -> list[GlobalPermission]: + permissions: list[GlobalPermission] = [] + for permission in self.permissions: + if not permission.startswith("global"): + continue + permissions.append(GlobalPermission.from_string(input=permission)) + return permissions + + +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 + permissions: list[str] = Field(default_factory=list) + 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, + required_permissions=self.permissions, + ) + 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/menu/utils.py b/backend/infrahub/menu/utils.py new file mode 100644 index 0000000000..da00d11bac --- /dev/null +++ b/backend/infrahub/menu/utils.py @@ -0,0 +1,12 @@ +from infrahub.core.protocols import CoreMenuItem +from infrahub.database import InfrahubDatabase + +from .models import MenuItemDefinition + + +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) diff --git a/backend/infrahub/message_bus/messages/__init__.py b/backend/infrahub/message_bus/messages/__init__.py index fdf3bbe5aa..7c4c9159e6 100644 --- a/backend/infrahub/message_bus/messages/__init__.py +++ b/backend/infrahub/message_bus/messages/__init__.py @@ -13,7 +13,6 @@ from .event_schema_update import EventSchemaUpdate from .event_worker_newprimaryapi import EventWorkerNewPrimaryAPI from .finalize_validator_execution import FinalizeValidatorExecution -from .git_branch_create import GitBranchCreate from .git_diff_namesonly import GitDiffNamesOnly, GitDiffNamesOnlyResponse from .git_file_get import GitFileGet, GitFileGetResponse from .git_repository_add import GitRepositoryAdd @@ -31,16 +30,9 @@ from .refresh_registry_branches import RefreshRegistryBranches from .refresh_registry_rebasedbranch import RefreshRegistryRebasedBranch from .refresh_webhook_configuration import RefreshWebhookConfiguration -from .request_artifact_generate import RequestArtifactGenerate from .request_artifactdefinition_check import RequestArtifactDefinitionCheck -from .request_artifactdefinition_generate import RequestArtifactDefinitionGenerate -from .request_diff_refresh import RequestDiffRefresh -from .request_diff_update import RequestDiffUpdate -from .request_generator_run import RequestGeneratorRun from .request_generatordefinition_check import RequestGeneratorDefinitionCheck from .request_generatordefinition_run import RequestGeneratorDefinitionRun -from .request_git_createbranch import RequestGitCreateBranch -from .request_git_sync import RequestGitSync from .request_graphqlquerygroup_update import RequestGraphQLQueryGroupUpdate from .request_proposed_change_cancel import RequestProposedChangeCancel from .request_proposedchange_pipeline import RequestProposedChangePipeline @@ -49,13 +41,7 @@ from .schema_migration_path import SchemaMigrationPath, SchemaMigrationPathResponse from .schema_validator_path import SchemaValidatorPath, SchemaValidatorPathResponse from .send_echo_request import SendEchoRequest, SendEchoRequestResponse -from .send_telemetry_push import SendTelemetryPush -from .send_webhook_event import SendWebhookEvent -from .transform_jinja_template import TransformJinjaTemplate, TransformJinjaTemplateResponse -from .transform_python_data import TransformPythonData, TransformPythonDataResponse -from .trigger_artifact_definition_generate import TriggerArtifactDefinitionGenerate from .trigger_generatordefinition_run import TriggerGeneratorDefinitionRun -from .trigger_ipam_reconciliation import TriggerIpamReconciliation from .trigger_proposed_change_cancel import TriggerProposedChangeCancel from .trigger_webhook_actions import TriggerWebhookActions @@ -73,7 +59,6 @@ "event.schema.update": EventSchemaUpdate, "event.worker.new_primary_api": EventWorkerNewPrimaryAPI, "finalize.validator.execution": FinalizeValidatorExecution, - "git.branch.create": GitBranchCreate, "git.diff.names_only": GitDiffNamesOnly, "git.file.get": GitFileGet, "git.repository.add": GitRepositoryAdd, @@ -87,16 +72,9 @@ "refresh.registry.branches": RefreshRegistryBranches, "refresh.registry.rebased_branch": RefreshRegistryRebasedBranch, "refresh.webhook.configuration": RefreshWebhookConfiguration, - "request.artifact.generate": RequestArtifactGenerate, "request.artifact_definition.check": RequestArtifactDefinitionCheck, - "request.artifact_definition.generate": RequestArtifactDefinitionGenerate, - "request.diff.update": RequestDiffUpdate, - "request.diff.refresh": RequestDiffRefresh, - "request.generator.run": RequestGeneratorRun, "request.generator_definition.check": RequestGeneratorDefinitionCheck, "request.generator_definition.run": RequestGeneratorDefinitionRun, - "request.git.create_branch": RequestGitCreateBranch, - "request.git.sync": RequestGitSync, "request.graphql_query_group.update": RequestGraphQLQueryGroupUpdate, "request.proposed_change.cancel": RequestProposedChangeCancel, "request.proposed_change.data_integrity": RequestProposedChangeDataIntegrity, @@ -109,20 +87,12 @@ "request.repository.checks": RequestRepositoryChecks, "request.repository.user_checks": RequestRepositoryUserChecks, "send.echo.request": SendEchoRequest, - "send.webhook.event": SendWebhookEvent, - "send.telemetry.push": SendTelemetryPush, - "transform.jinja.template": TransformJinjaTemplate, - "transform.python.data": TransformPythonData, - "trigger.artifact_definition.generate": TriggerArtifactDefinitionGenerate, "trigger.generator_definition.run": TriggerGeneratorDefinitionRun, - "trigger.ipam.reconciliation": TriggerIpamReconciliation, "trigger.proposed_change.cancel": TriggerProposedChangeCancel, "trigger.webhook.actions": TriggerWebhookActions, } RESPONSE_MAP: dict[str, type[InfrahubResponse]] = { - "transform.jinja.template": TransformJinjaTemplateResponse, - "transform.python.data": TransformPythonDataResponse, "git.diff.names_only": GitDiffNamesOnlyResponse, "git.file.get": GitFileGetResponse, "send.echo.request": SendEchoRequestResponse, diff --git a/backend/infrahub/message_bus/messages/event_branch_merge.py b/backend/infrahub/message_bus/messages/event_branch_merge.py index c438340b8c..b71d584d63 100644 --- a/backend/infrahub/message_bus/messages/event_branch_merge.py +++ b/backend/infrahub/message_bus/messages/event_branch_merge.py @@ -1,6 +1,5 @@ from pydantic import Field -from infrahub.core.ipam.model import IpamNodeDetails from infrahub.message_bus import InfrahubMessage @@ -9,4 +8,3 @@ class EventBranchMerge(InfrahubMessage): source_branch: str = Field(..., description="The source branch") target_branch: str = Field(..., description="The target branch") - ipam_node_details: list[IpamNodeDetails] = Field(default_factory=list, description="Details for changed IP nodes") diff --git a/backend/infrahub/message_bus/messages/event_branch_rebased.py b/backend/infrahub/message_bus/messages/event_branch_rebased.py index 04335ace44..604d9d9bf6 100644 --- a/backend/infrahub/message_bus/messages/event_branch_rebased.py +++ b/backend/infrahub/message_bus/messages/event_branch_rebased.py @@ -1,6 +1,5 @@ from pydantic import Field -from infrahub.core.ipam.model import IpamNodeDetails from infrahub.message_bus import InfrahubMessage @@ -8,4 +7,3 @@ class EventBranchRebased(InfrahubMessage): """Sent when a branch has been rebased.""" branch: str = Field(..., description="The branch that was rebased") - ipam_node_details: list[IpamNodeDetails] = Field(default_factory=list, description="Details for changed IP nodes") diff --git a/backend/infrahub/message_bus/messages/git_branch_create.py b/backend/infrahub/message_bus/messages/git_branch_create.py deleted file mode 100644 index 88119493d5..0000000000 --- a/backend/infrahub/message_bus/messages/git_branch_create.py +++ /dev/null @@ -1,12 +0,0 @@ -from pydantic import Field - -from infrahub.message_bus import InfrahubMessage - - -class GitBranchCreate(InfrahubMessage): - """Create a branch in a Git repository.""" - - branch: str = Field(..., description="Name of the branch to create") - branch_id: str = Field(..., description="The unique ID of the branch") - repository_id: str = Field(..., description="The unique ID of the Repository") - repository_name: str = Field(..., description="The name of the Repository") diff --git a/backend/infrahub/message_bus/messages/request_artifactdefinition_generate.py b/backend/infrahub/message_bus/messages/request_artifactdefinition_generate.py deleted file mode 100644 index 6658826d45..0000000000 --- a/backend/infrahub/message_bus/messages/request_artifactdefinition_generate.py +++ /dev/null @@ -1,16 +0,0 @@ -from typing import List - -from pydantic import Field - -from infrahub.message_bus import InfrahubMessage - - -class RequestArtifactDefinitionGenerate(InfrahubMessage): - """Sent to trigger the generation of artifacts for a given branch.""" - - artifact_definition: str = Field(..., description="The unique ID of the Artifact Definition") - branch: str = Field(..., description="The branch to target") - limit: List[str] = Field( - default_factory=list, - description="List of targets to limit the scope of the generation, if populated only the included artifacts will be regenerated", - ) diff --git a/backend/infrahub/message_bus/messages/request_git_createbranch.py b/backend/infrahub/message_bus/messages/request_git_createbranch.py deleted file mode 100644 index 1ef3f7362f..0000000000 --- a/backend/infrahub/message_bus/messages/request_git_createbranch.py +++ /dev/null @@ -1,10 +0,0 @@ -from pydantic import Field - -from infrahub.message_bus import InfrahubMessage - - -class RequestGitCreateBranch(InfrahubMessage): - """Sent to trigger the creation of a branch in git repositories.""" - - branch: str = Field(..., description="The branch to target") - branch_id: str = Field(..., description="The unique ID of the branch") diff --git a/backend/infrahub/message_bus/messages/request_git_sync.py b/backend/infrahub/message_bus/messages/request_git_sync.py deleted file mode 100644 index ae31c665db..0000000000 --- a/backend/infrahub/message_bus/messages/request_git_sync.py +++ /dev/null @@ -1,5 +0,0 @@ -from infrahub.message_bus import InfrahubMessage - - -class RequestGitSync(InfrahubMessage): - """Request remote repositories to be synced.""" 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_telemetry_push.py b/backend/infrahub/message_bus/messages/send_telemetry_push.py deleted file mode 100644 index 0f2b148780..0000000000 --- a/backend/infrahub/message_bus/messages/send_telemetry_push.py +++ /dev/null @@ -1,5 +0,0 @@ -from infrahub.message_bus import InfrahubMessage - - -class SendTelemetryPush(InfrahubMessage): - """Push usage telemetry.""" diff --git a/backend/infrahub/message_bus/messages/transform_jinja_template.py b/backend/infrahub/message_bus/messages/transform_jinja_template.py deleted file mode 100644 index 96cea8da17..0000000000 --- a/backend/infrahub/message_bus/messages/transform_jinja_template.py +++ /dev/null @@ -1,28 +0,0 @@ -from typing import Optional - -from pydantic import Field - -from infrahub.message_bus import InfrahubMessage, InfrahubResponse, InfrahubResponseData - -ROUTING_KEY = "transform.jinja.template" - - -class TransformJinjaTemplate(InfrahubMessage): - """Sent to trigger the checks for a repository to be executed.""" - - repository_id: str = Field(..., description="The unique ID of the Repository") - repository_name: str = Field(..., description="The name of the repository") - repository_kind: str = Field(..., description="The kind of the repository") - data: dict = Field(..., description="Input data for the template") - branch: str = Field(..., description="The branch to target") - template_location: str = Field(..., description="Location of the template within the repository") - commit: str = Field(..., description="The commit id to use when rendering the template") - - -class TransformJinjaTemplateResponseData(InfrahubResponseData): - rendered_template: Optional[str] = Field(None, description="Rendered template in string format") - - -class TransformJinjaTemplateResponse(InfrahubResponse): - routing_key: str = ROUTING_KEY - data: TransformJinjaTemplateResponseData diff --git a/backend/infrahub/message_bus/messages/transform_python_data.py b/backend/infrahub/message_bus/messages/transform_python_data.py deleted file mode 100644 index 6412c163dd..0000000000 --- a/backend/infrahub/message_bus/messages/transform_python_data.py +++ /dev/null @@ -1,26 +0,0 @@ -from pydantic import Field - -from infrahub.message_bus import InfrahubMessage, InfrahubResponse, InfrahubResponseData - -ROUTING_KEY = "transform.python.data" - - -class TransformPythonData(InfrahubMessage): - """Sent to run a Python transform.""" - - repository_id: str = Field(..., description="The unique ID of the Repository") - repository_name: str = Field(..., description="The name of the repository") - repository_kind: str = Field(..., description="The kind of the repository") - data: dict = Field(..., description="Input data for the template") - branch: str = Field(..., description="The branch to target") - transform_location: str = Field(..., description="Location of the transform within the repository") - commit: str = Field(..., description="The commit id to use when rendering the template") - - -class TransformPythonDataResponseData(InfrahubResponseData): - transformed_data: dict = Field(..., description="The data output of the transformation") - - -class TransformPythonDataResponse(InfrahubResponse): - routing_key: str = ROUTING_KEY - data: TransformPythonDataResponseData diff --git a/backend/infrahub/message_bus/messages/trigger_artifact_definition_generate.py b/backend/infrahub/message_bus/messages/trigger_artifact_definition_generate.py deleted file mode 100644 index 14f4790840..0000000000 --- a/backend/infrahub/message_bus/messages/trigger_artifact_definition_generate.py +++ /dev/null @@ -1,9 +0,0 @@ -from pydantic import Field - -from infrahub.message_bus import InfrahubMessage - - -class TriggerArtifactDefinitionGenerate(InfrahubMessage): - """Sent after a branch has been merged to start the regeneration of artifacts""" - - branch: str = Field(..., description="The impacted branch") diff --git a/backend/infrahub/message_bus/messages/trigger_ipam_reconciliation.py b/backend/infrahub/message_bus/messages/trigger_ipam_reconciliation.py deleted file mode 100644 index af2cfd9368..0000000000 --- a/backend/infrahub/message_bus/messages/trigger_ipam_reconciliation.py +++ /dev/null @@ -1,11 +0,0 @@ -from pydantic import Field - -from infrahub.core.ipam.model import IpamNodeDetails -from infrahub.message_bus import InfrahubMessage - - -class TriggerIpamReconciliation(InfrahubMessage): - """Sent after a branch has been merged/rebased to reconcile changed IP Prefix and Address nodes""" - - branch: str = Field(..., description="The updated branch") - ipam_node_details: list[IpamNodeDetails] = Field(..., description="Details for changed IP nodes") diff --git a/backend/infrahub/message_bus/operations/__init__.py b/backend/infrahub/message_bus/operations/__init__.py index 91a159879d..28fefe17e9 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 ( @@ -12,7 +13,6 @@ requests, schema, send, - transform, trigger, ) from infrahub.message_bus.types import MessageTTL @@ -33,7 +33,6 @@ "event.schema.update": event.schema.update, "event.worker.new_primary_api": event.worker.new_primary_api, "finalize.validator.execution": finalize.validator.execution, - "git.branch.create": git.branch.create, "git.diff.names_only": git.diff.names_only, "git.file.get": git.file.get, "git.repository.add": git.repository.add, @@ -45,17 +44,10 @@ "refresh.registry.branches": refresh.registry.branches, "refresh.registry.rebased_branch": refresh.registry.rebased_branch, "refresh.webhook.configuration": refresh.webhook.configuration, - "request.diff.refresh": requests.diff.refresh, - "request.diff.update": requests.diff.update, - "request.generator.run": requests.generator.run, "request.generator_definition.check": requests.generator_definition.check, "request.generator_definition.run": requests.generator_definition.run, - "request.git.create_branch": requests.git.create_branch, - "request.git.sync": requests.git.sync, "request.graphql_query_group.update": requests.graphql_query_group.update, - "request.artifact.generate": requests.artifact.generate, "request.artifact_definition.check": requests.artifact_definition.check, - "request.artifact_definition.generate": requests.artifact_definition.generate, "request.proposed_change.cancel": requests.proposed_change.cancel, "request.proposed_change.data_integrity": requests.proposed_change.data_integrity, "request.proposed_change.pipeline": requests.proposed_change.pipeline, @@ -67,26 +59,25 @@ "request.repository.checks": requests.repository.checks, "request.repository.user_checks": requests.repository.user_checks, "send.echo.request": send.echo.request, - "send.webhook.event": send.webhook.event, - "send.telemetry.push": send.telemetry.push, "schema.migration.path": schema.migration.path, "schema.validator.path": schema.validator.path, - "transform.jinja.template": transform.jinja.template, - "transform.python.data": transform.python.data, - "trigger.artifact_definition.generate": trigger.artifact_definition.generate, "trigger.generator_definition.run": trigger.generator_definition.run, - "trigger.ipam.reconciliation": trigger.ipam.reconciliation, "trigger.proposed_change.cancel": trigger.proposed_change.cancel, "trigger.webhook.actions": trigger.webhook.actions, } -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..27e9efa2a4 100644 --- a/backend/infrahub/message_bus/operations/check/generator.py +++ b/backend/infrahub/message_bus/operations/check/generator.py @@ -1,8 +1,9 @@ import os -from infrahub_sdk import InfrahubNode from infrahub_sdk.exceptions import ModuleImportError +from infrahub_sdk.node import InfrahubNode 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..f8666139ff 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 infrahub_sdk.uuidt 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..976c9d70b0 100644 --- a/backend/infrahub/message_bus/operations/event/branch.py +++ b/backend/infrahub/message_bus/operations/event/branch.py @@ -1,28 +1,42 @@ 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.models import RequestDiffRefresh, RequestDiffUpdate from infrahub.core.diff.repository.repository import DiffRepository from infrahub.dependencies.registry import get_component_registry from infrahub.log import get_logger from infrahub.message_bus import InfrahubMessage, messages from infrahub.services import InfrahubServices +from infrahub.workflows.catalogue import ( + GIT_REPOSITORIES_CREATE_BRANCH, + REQUEST_DIFF_REFRESH, + REQUEST_DIFF_UPDATE, + TRIGGER_ARTIFACT_DEFINITION_GENERATE, +) log = get_logger() +@flow(name="event-branch-create") async def create(message: messages.EventBranchCreate, service: InfrahubServices) -> None: log.info("run_message", branch=message.branch) events: List[InfrahubMessage] = [messages.RefreshRegistryBranches()] if message.sync_with_git: - events.append(messages.RequestGitCreateBranch(branch=message.branch, branch_id=message.branch_id)) + await service.workflow.submit_workflow( + workflow=GIT_REPOSITORIES_CREATE_BRANCH, + parameters={"branch": message.branch, "branch_id": message.branch_id}, + ) for event in events: event.assign_meta(parent=message) 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,13 +50,12 @@ 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) events: List[InfrahubMessage] = [ messages.RefreshRegistryBranches(), - messages.TriggerIpamReconciliation(branch=message.target_branch, ipam_node_details=message.ipam_node_details), - messages.TriggerArtifactDefinitionGenerate(branch=message.target_branch), messages.TriggerGeneratorDefinitionRun(branch=message.target_branch), ] component_registry = get_component_registry() @@ -51,29 +64,34 @@ async def merge(message: messages.EventBranchMerge, service: InfrahubServices) - # send diff update requests for every branch-tracking diff branch_diff_roots = await diff_repository.get_empty_roots(base_branch_names=[message.target_branch]) + await service.workflow.submit_workflow( + workflow=TRIGGER_ARTIFACT_DEFINITION_GENERATE, + parameters={"branch": message.target_branch}, + ) + for diff_root in branch_diff_roots: if ( diff_root.base_branch_name != diff_root.diff_branch_name and diff_root.tracking_id and isinstance(diff_root.tracking_id, BranchTrackingId) ): - events.append(messages.RequestDiffUpdate(branch_name=diff_root.diff_branch_name)) + request_diff_update_model = RequestDiffUpdate(branch_name=diff_root.diff_branch_name) + await service.workflow.submit_workflow( + workflow=REQUEST_DIFF_UPDATE, parameters={"model": request_diff_update_model} + ) for event in events: event.assign_meta(parent=message) 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) events: List[InfrahubMessage] = [ messages.RefreshRegistryRebasedBranch(branch=message.branch), ] - if message.ipam_node_details: - events.append( - messages.TriggerIpamReconciliation(branch=message.branch, ipam_node_details=message.ipam_node_details), - ) # for every diff that touches the rebased branch, recalculate it component_registry = get_component_registry() @@ -83,7 +101,12 @@ async def rebased(message: messages.EventBranchRebased, service: InfrahubService for diff_root in diff_roots_to_refresh: if diff_root.base_branch_name != diff_root.diff_branch_name: - events.append(messages.RequestDiffRefresh(branch_name=diff_root.diff_branch_name, diff_id=diff_root.uuid)) + request_diff_refresh_model = RequestDiffRefresh( + branch_name=diff_root.diff_branch_name, diff_id=diff_root.uuid + ) + await service.workflow.submit_workflow( + workflow=REQUEST_DIFF_REFRESH, parameters={"model": request_diff_refresh_model} + ) for event in events: event.assign_meta(parent=message) 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/__init__.py b/backend/infrahub/message_bus/operations/git/__init__.py index 776717bdcc..4eb4925baa 100644 --- a/backend/infrahub/message_bus/operations/git/__init__.py +++ b/backend/infrahub/message_bus/operations/git/__init__.py @@ -1,3 +1,3 @@ -from . import branch, diff, file, repository +from . import diff, file, repository -__all__ = ["branch", "diff", "file", "repository"] +__all__ = ["diff", "file", "repository"] diff --git a/backend/infrahub/message_bus/operations/git/branch.py b/backend/infrahub/message_bus/operations/git/branch.py deleted file mode 100644 index 2c2fc76afa..0000000000 --- a/backend/infrahub/message_bus/operations/git/branch.py +++ /dev/null @@ -1,14 +0,0 @@ -from infrahub import lock -from infrahub.git.repository import InfrahubRepository -from infrahub.log import get_logger -from infrahub.message_bus import messages -from infrahub.services import InfrahubServices - -log = get_logger() - - -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) - async with lock.registry.get(name=message.repository_name, namespace="repository"): - await repo.create_branch_in_git(branch_name=message.branch, branch_id=message.branch_id) 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..e2e8752d14 100644 --- a/backend/infrahub/message_bus/operations/refresh/webhook.py +++ b/backend/infrahub/message_bus/operations/refresh/webhook.py @@ -1,14 +1,20 @@ 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, ) -> None: + if not service._client: + service.log.error("Client hasn't been initialized, can't refresh webhook") + return + service.log.debug("Refreshing webhook configuration") standard_webhooks = await service.client.all(kind=InfrahubKind.STANDARDWEBHOOK) custom_webhooks = await service.client.all(kind=InfrahubKind.CUSTOMWEBHOOK) diff --git a/backend/infrahub/message_bus/operations/requests/__init__.py b/backend/infrahub/message_bus/operations/requests/__init__.py index 1ad4a69d96..52eb1ebe8b 100644 --- a/backend/infrahub/message_bus/operations/requests/__init__.py +++ b/backend/infrahub/message_bus/operations/requests/__init__.py @@ -1,22 +1,14 @@ from . import ( - artifact, artifact_definition, - diff, - generator, generator_definition, - git, graphql_query_group, proposed_change, repository, ) __all__ = [ - "artifact", "artifact_definition", - "diff", - "generator", "generator_definition", - "git", "graphql_query_group", "proposed_change", "repository", diff --git a/backend/infrahub/message_bus/operations/requests/artifact.py b/backend/infrahub/message_bus/operations/requests/artifact.py deleted file mode 100644 index 6764978b76..0000000000 --- a/backend/infrahub/message_bus/operations/requests/artifact.py +++ /dev/null @@ -1,35 +0,0 @@ -from infrahub.git.repository import get_initialized_repo -from infrahub.log import get_logger -from infrahub.message_bus import messages -from infrahub.services import InfrahubServices -from infrahub.tasks.artifact import define_artifact - -log = get_logger() - - -async def generate(message: messages.RequestArtifactGenerate, service: InfrahubServices): - log.debug("Generating artifact", message=message) - - repo = await get_initialized_repo( - repository_id=message.repository_id, - name=message.repository_name, - service=service, - repository_kind=message.repository_kind, - ) - - artifact = await define_artifact(message=message, service=service) - - try: - result = await repo.render_artifact(artifact=artifact, message=message) - log.debug( - "Generated artifact", - name=message.artifact_name, - changed=result.changed, - checksum=result.checksum, - artifact_id=result.artifact_id, - storage_id=result.storage_id, - ) - except Exception as exc: # pylint: disable=broad-except - log.exception("Failed to generate artifact", error=exc) - artifact.status.value = "Error" - await artifact.save() diff --git a/backend/infrahub/message_bus/operations/requests/artifact_definition.py b/backend/infrahub/message_bus/operations/requests/artifact_definition.py index 2020d6f9d1..a0fad05a8d 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 infrahub_sdk.uuidt 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,85 +136,6 @@ async def check(message: messages.RequestArtifactDefinitionCheck, service: Infra await service.send(message=event) -async def generate(message: messages.RequestArtifactDefinitionGenerate, service: InfrahubServices) -> None: - log.info( - "Received request to generate artifacts for an artifact_definition", - branch=message.branch, - artifact_definition=message.artifact_definition, - limit=message.limit, - ) - artifact_definition = await service.client.get( - kind=InfrahubKind.ARTIFACTDEFINITION, id=message.artifact_definition, branch=message.branch - ) - - await artifact_definition.targets.fetch() - group = artifact_definition.targets.peer - await group.members.fetch() - - existing_artifacts = await service.client.filters( - kind=InfrahubKind.ARTIFACT, - definition__ids=[message.artifact_definition], - include=["object"], - branch=message.branch, - ) - artifacts_by_member = {} - for artifact in existing_artifacts: - artifacts_by_member[artifact.object.peer.id] = artifact.id - - await artifact_definition.transformation.fetch() - transformation_repository = artifact_definition.transformation.peer.repository - - await transformation_repository.fetch() - - transform = artifact_definition.transformation.peer - await transform.query.fetch() - query = transform.query.peer - repository = transformation_repository.peer - branch = await service.client.branch.get(branch_name=message.branch) - if branch.sync_with_git: - repository = await service.client.get( - kind=InfrahubKind.GENERICREPOSITORY, id=repository.id, branch=message.branch, fragment=True - ) - transform_location = "" - - if transform.typename == InfrahubKind.TRANSFORMJINJA2: - transform_location = transform.template_path.value - elif transform.typename == InfrahubKind.TRANSFORMPYTHON: - transform_location = f"{transform.file_path.value}::{transform.class_name.value}" - - events = [] - for relationship in group.members.peers: - member = relationship.peer - artifact_id = artifacts_by_member.get(member.id) - if message.limit and artifact_id not in message.limit: - continue - - events.append( - messages.RequestArtifactGenerate( - artifact_name=artifact_definition.name.value, - artifact_id=artifact_id, - artifact_definition=message.artifact_definition, - commit=repository.commit.value, - content_type=artifact_definition.content_type.value, - transform_type=transform.typename, - transform_location=transform_location, - repository_id=repository.id, - repository_name=repository.name.value, - repository_kind=repository.get_kind(), - branch_name=message.branch, - query=query.name.value, - variables=member.extract(params=artifact_definition.parameters.value), - target_id=member.id, - target_name=member.display_label, - timeout=transform.timeout.value, - ) - ) - - for event in events: - event.assign_meta(parent=message) - await service.send(message=event) - - def _render_artifact(artifact_id: Optional[str], managed_branch: bool, impacted_artifacts: list[str]) -> bool: """Returns a boolean to indicate if an artifact should be generated or not. Will return true if: diff --git a/backend/infrahub/message_bus/operations/requests/generator_definition.py b/backend/infrahub/message_bus/operations/requests/generator_definition.py index 92c03e839e..28acfcd27b 100644 --- a/backend/infrahub/message_bus/operations/requests/generator_definition.py +++ b/backend/infrahub/message_bus/operations/requests/generator_definition.py @@ -1,14 +1,18 @@ from typing import Optional -from infrahub_sdk import UUIDT +from infrahub_sdk.uuidt import UUIDT +from prefect import flow from infrahub.core.constants import InfrahubKind, ValidatorConclusion, ValidatorState from infrahub.core.timestamp import Timestamp +from infrahub.generators.models import RequestGeneratorRun from infrahub.message_bus import InfrahubMessage, Meta, messages from infrahub.message_bus.types import KVTTL from infrahub.services import InfrahubServices +from infrahub.workflows.catalogue import REQUEST_GENERATOR_RUN +@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 +131,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", @@ -137,7 +142,6 @@ async def run(message: messages.RequestGeneratorDefinitionRun, service: Infrahub branch=message.branch, generator_definition=message.generator_definition.definition_id, ) - events: list[InfrahubMessage] = [] group = await service.client.get( kind=InfrahubKind.GENERICGROUP, @@ -165,30 +169,25 @@ async def run(message: messages.RequestGeneratorDefinitionRun, service: Infrahub for relationship in group.members.peers: member = relationship.peer generator_instance = instance_by_member.get(member.id) - events.append( - messages.RequestGeneratorRun( - generator_definition=message.generator_definition, - commit=repository.commit.value, - generator_instance=generator_instance, - repository_id=repository.id, - repository_name=repository.name.value, - repository_kind=repository.typename, - branch_name=message.branch, - query=message.generator_definition.query_name, - variables=member.extract(params=message.generator_definition.parameters), - target_id=member.id, - target_name=member.display_label, - ) + model = RequestGeneratorRun( + generator_definition=message.generator_definition, + commit=repository.commit.value, + generator_instance=generator_instance, + repository_id=repository.id, + repository_name=repository.name.value, + repository_kind=repository.typename, + branch_name=message.branch, + query=message.generator_definition.query_name, + variables=member.extract(params=message.generator_definition.parameters), + target_id=member.id, + target_name=member.display_label, ) + await service.workflow.submit_workflow(workflow=REQUEST_GENERATOR_RUN, parameters={"model": model}) await task_report.info( event=f"Generator triggered for {len(group.members.peers)} members in {group.name.value}." ) - for event in events: - event.assign_meta(parent=message) - await service.send(message=event) - def _run_generator(instance_id: Optional[str], managed_branch: bool, impacted_instances: list[str]) -> bool: """Returns a boolean to indicate if a generator instance needs to be executed diff --git a/backend/infrahub/message_bus/operations/requests/git.py b/backend/infrahub/message_bus/operations/requests/git.py deleted file mode 100644 index f388bf09d5..0000000000 --- a/backend/infrahub/message_bus/operations/requests/git.py +++ /dev/null @@ -1,36 +0,0 @@ -from typing import List - -from infrahub.core.constants import InfrahubKind -from infrahub.git.actions import sync_remote_repositories -from infrahub.log import get_logger -from infrahub.message_bus import messages -from infrahub.services import InfrahubServices - -log = get_logger() - - -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") - repositories = await service.client.filters(kind=InfrahubKind.REPOSITORY) - events: List[messages.GitBranchCreate] = [] - for repository in repositories: - events.append( - messages.GitBranchCreate( - branch=message.branch, - branch_id=message.branch_id, - repository_name=repository.name.value, - repository_id=repository.id, - ) - ) - for event in events: - event.assign_meta(parent=message) - await service.send(message=event) - - -async def sync( - message: messages.RequestGitSync, # pylint: disable=unused-argument - service: InfrahubServices, -) -> None: - """Sync remote repositories.""" - await sync_remote_repositories(service) 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..678231b4cc 100644 --- a/backend/infrahub/message_bus/operations/requests/graphql_query_group.py +++ b/backend/infrahub/message_bus/operations/requests/graphql_query_group.py @@ -1,7 +1,9 @@ from typing import List -from infrahub_sdk import InfrahubClient, InfrahubNode +from infrahub_sdk import InfrahubClient +from infrahub_sdk.node import 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 +13,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 +37,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 89d0e44784..174fa26a3f 100644 --- a/backend/infrahub/message_bus/operations/requests/proposed_change.py +++ b/backend/infrahub/message_bus/operations/requests/proposed_change.py @@ -8,12 +8,15 @@ 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 from infrahub.core.constants import CheckType, InfrahubKind, ProposedChangeState, RepositoryInternalStatus from infrahub.core.diff.coordinator import DiffCoordinator -from infrahub.core.diff.model.diff import SchemaConflict +from infrahub.core.diff.model.diff import DiffElementType, SchemaConflict +from infrahub.core.diff.model.path import NodeDiffFieldSummary from infrahub.core.integrity.object_conflict.conflict_recorder import ObjectConflictValidatorRecorder from infrahub.core.registry import registry from infrahub.core.validators.checker import schema_validators_checker @@ -30,14 +33,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 +71,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 +79,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 +101,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, @@ -227,6 +230,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 @@ -294,6 +298,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, @@ -334,6 +339,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, @@ -397,6 +403,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, @@ -563,6 +570,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, @@ -887,5 +895,22 @@ async def _populate_subscribers(branch_diff: ProposedChangeBranchDiff, service: async def _get_proposed_change_schema_integrity_constraints( message: messages.RequestProposedChangeSchemaIntegrity, schema: SchemaBranch ) -> list[SchemaUpdateConstraintInfo]: + node_diff_field_summary_map: dict[str, NodeDiffFieldSummary] = {} + for node_diff in message.branch_diff.diff_summary: + node_kind = node_diff["kind"] + if node_kind not in node_diff_field_summary_map: + node_diff_field_summary_map[node_kind] = NodeDiffFieldSummary(kind=node_kind) + field_summary = node_diff_field_summary_map[node_kind] + for element in node_diff["elements"]: + element_name = element["name"] + element_type = element["element_type"] + if element_type.lower() in ( + DiffElementType.RELATIONSHIP_MANY.value.lower(), + DiffElementType.RELATIONSHIP_ONE.value.lower(), + ): + field_summary.relationship_names.add(element_name) + elif element_type.lower() in (DiffElementType.ATTRIBUTE.value.lower(),): + field_summary.attribute_names.add(element_name) + determiner = ConstraintValidatorDeterminer(schema_branch=schema) - return await determiner.get_constraints(node_diffs=message.branch_diff.diff_summary) + return await determiner.get_constraints(node_diffs=list(node_diff_field_summary_map.values())) diff --git a/backend/infrahub/message_bus/operations/requests/repository.py b/backend/infrahub/message_bus/operations/requests/repository.py index 872196a2f8..5438247789 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 infrahub_sdk.uuidt 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/__init__.py b/backend/infrahub/message_bus/operations/send/__init__.py index fb6b746f43..93293e24e3 100644 --- a/backend/infrahub/message_bus/operations/send/__init__.py +++ b/backend/infrahub/message_bus/operations/send/__init__.py @@ -1,3 +1,3 @@ -from . import echo, telemetry, webhook +from . import echo -__all__ = ["echo", "telemetry", "webhook"] +__all__ = ["echo"] 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/webhook.py b/backend/infrahub/message_bus/operations/send/webhook.py deleted file mode 100644 index 5b1df4cc76..0000000000 --- a/backend/infrahub/message_bus/operations/send/webhook.py +++ /dev/null @@ -1,35 +0,0 @@ -import ujson - -from infrahub.exceptions import NodeNotFoundError -from infrahub.message_bus import messages -from infrahub.services import InfrahubServices -from infrahub.webhook import CustomWebhook, StandardWebhook, TransformWebhook, Webhook - - -async def event(message: messages.SendWebhookEvent, service: InfrahubServices) -> None: - async with service.task_report( - related_node=message.webhook_id, - title="Webhook", - ) as task_report: - webhook_definition = await service.cache.get(key=f"webhook:active:{message.webhook_id}") - if not webhook_definition: - service.log.warning("Webhook not found", webhook_id=message.webhook_id) - raise NodeNotFoundError( - node_type="Webhook", identifier=message.webhook_id, message="The requested Webhook was not found" - ) - - webhook_data = ujson.loads(webhook_definition) - payload = {"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() - await task_report.finalise( - title=webhook.webhook_type, - logs={"message": "Successfully sent webhook", "severity": "INFO"}, - ) diff --git a/backend/infrahub/message_bus/operations/transform/__init__.py b/backend/infrahub/message_bus/operations/transform/__init__.py deleted file mode 100644 index 7b7cda3e60..0000000000 --- a/backend/infrahub/message_bus/operations/transform/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from . import jinja, python - -__all__ = ["jinja", "python"] diff --git a/backend/infrahub/message_bus/operations/transform/jinja.py b/backend/infrahub/message_bus/operations/transform/jinja.py deleted file mode 100644 index a861ea5946..0000000000 --- a/backend/infrahub/message_bus/operations/transform/jinja.py +++ /dev/null @@ -1,30 +0,0 @@ -from infrahub.git.repository import get_initialized_repo -from infrahub.log import get_logger -from infrahub.message_bus.messages.transform_jinja_template import ( - TransformJinjaTemplate, - TransformJinjaTemplateResponse, - TransformJinjaTemplateResponseData, -) -from infrahub.services import InfrahubServices - -log = get_logger() - - -async def template(message: TransformJinjaTemplate, service: InfrahubServices) -> None: - log.info(f"Received request to render a Jinja template on branch={message.branch}") - - 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} - ) - if message.reply_requested: - response = TransformJinjaTemplateResponse( - data=TransformJinjaTemplateResponseData(rendered_template=rendered_template), - ) - await service.reply(message=response, initiator=message) diff --git a/backend/infrahub/message_bus/operations/transform/python.py b/backend/infrahub/message_bus/operations/transform/python.py deleted file mode 100644 index a93a33c1f9..0000000000 --- a/backend/infrahub/message_bus/operations/transform/python.py +++ /dev/null @@ -1,38 +0,0 @@ -from infrahub.git.repository import get_initialized_repo -from infrahub.log import get_logger -from infrahub.message_bus import messages -from infrahub.message_bus.messages.transform_python_data import ( - TransformPythonDataResponse, - TransformPythonDataResponseData, -) -from infrahub.services import InfrahubServices - -log = get_logger() - - -async def data(message: messages.TransformPythonData, service: InfrahubServices) -> None: - log.info( - "Received request to transform Python data", - repository_name=message.repository_name, - transform=message.transform_location, - branch=message.branch, - ) - - repo = await get_initialized_repo( - repository_id=message.repository_id, - name=message.repository_name, - service=service, - repository_kind=message.repository_kind, - ) - - transformed_data = await repo.execute_python_transform( - branch_name=message.branch, - commit=message.commit, - location=message.transform_location, - data=message.data, - client=service.client, - ) - - if message.reply_requested: - response = TransformPythonDataResponse(data=TransformPythonDataResponseData(transformed_data=transformed_data)) - await service.reply(message=response, initiator=message) diff --git a/backend/infrahub/message_bus/operations/trigger/__init__.py b/backend/infrahub/message_bus/operations/trigger/__init__.py index 5d71093f84..f9c9de4ead 100644 --- a/backend/infrahub/message_bus/operations/trigger/__init__.py +++ b/backend/infrahub/message_bus/operations/trigger/__init__.py @@ -1,3 +1,3 @@ -from . import artifact_definition, generator_definition, ipam, proposed_change, webhook +from . import generator_definition, proposed_change, webhook -__all__ = ["artifact_definition", "generator_definition", "ipam", "proposed_change", "webhook"] +__all__ = ["generator_definition", "proposed_change", "webhook"] diff --git a/backend/infrahub/message_bus/operations/trigger/artifact_definition.py b/backend/infrahub/message_bus/operations/trigger/artifact_definition.py deleted file mode 100644 index 27013e25ff..0000000000 --- a/backend/infrahub/message_bus/operations/trigger/artifact_definition.py +++ /dev/null @@ -1,20 +0,0 @@ -from infrahub.core.constants import InfrahubKind -from infrahub.log import get_logger -from infrahub.message_bus import messages -from infrahub.services import InfrahubServices - -log = get_logger() - - -async def generate(message: messages.TriggerArtifactDefinitionGenerate, service: InfrahubServices) -> None: - artifact_definitions = await service.client.all( - kind=InfrahubKind.ARTIFACTDEFINITION, branch=message.branch, include=["id"] - ) - - events = [ - messages.RequestArtifactDefinitionGenerate(branch=message.branch, artifact_definition=artifact_definition.id) - for artifact_definition in artifact_definitions - ] - for event in events: - event.assign_meta(parent=message) - await service.send(message=event) 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 deleted file mode 100644 index 49dba8bf78..0000000000 --- a/backend/infrahub/message_bus/operations/trigger/ipam.py +++ /dev/null @@ -1,26 +0,0 @@ -import ipaddress - -from infrahub.core import registry -from infrahub.core.ipam.reconciler import IpamReconciler -from infrahub.log import get_logger -from infrahub.message_bus import messages -from infrahub.services import InfrahubServices - -log = get_logger() - - -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) - else: - ip_value = ipaddress.ip_network(ipam_node_details.ip_value) - await ipam_reconciler.reconcile( - ip_value=ip_value, - namespace=ipam_node_details.namespace_id, - node_uuid=ipam_node_details.node_uuid, - is_delete=ipam_node_details.is_delete, - ) 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..866cb053c3 100644 --- a/backend/infrahub/message_bus/operations/trigger/webhook.py +++ b/backend/infrahub/message_bus/operations/trigger/webhook.py @@ -1,19 +1,22 @@ from typing import List +from prefect import flow + from infrahub.message_bus import InfrahubMessage, messages +from infrahub.send.models import SendWebhookData from infrahub.services import InfrahubServices +from infrahub.workflows.catalogue import WEBHOOK_SEND +@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] = [] for webhook in webhooks: webhook_id = webhook.split(":")[-1] - events.append( - messages.SendWebhookEvent( - webhook_id=webhook_id, event_type=message.event_type, event_data=message.event_data - ) - ) + model = SendWebhookData(webhook_id=webhook_id, event_type=message.event_type, event_data=message.event_data) + await service.workflow.submit_workflow(workflow=WEBHOOK_SEND, parameters={"model": model}) + for event in events: event.assign_meta(parent=message) await service.send(message=event) 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..cdde5796ff --- /dev/null +++ b/backend/infrahub/permissions/backend.py @@ -0,0 +1,32 @@ +from __future__ import annotations + +from abc import ABC, abstractmethod +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from infrahub.auth import AccountSession + from infrahub.core.account import GlobalPermission, ObjectPermission + from infrahub.core.branch import Branch + from infrahub.database import InfrahubDatabase + from infrahub.permissions.constants import AssignedPermissions, PermissionDecisionFlag + + +class PermissionBackend(ABC): + @abstractmethod + async def load_permissions( + self, db: InfrahubDatabase, account_session: AccountSession, branch: Branch + ) -> AssignedPermissions: ... + + @abstractmethod + def report_object_permission( + self, permissions: list[ObjectPermission], namespace: str, name: str, action: str + ) -> PermissionDecisionFlag: ... + + @abstractmethod + async def has_permission( + self, + db: InfrahubDatabase, + account_session: AccountSession, + permission: GlobalPermission | ObjectPermission, + branch: Branch, + ) -> bool: ... diff --git a/backend/infrahub/permissions/constants.py b/backend/infrahub/permissions/constants.py new file mode 100644 index 0000000000..c4cb88477a --- /dev/null +++ b/backend/infrahub/permissions/constants.py @@ -0,0 +1,28 @@ +from __future__ import annotations + +from enum import IntFlag, StrEnum, auto +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] + + +class PermissionDecisionFlag(IntFlag): + DENY = 1 + ALLOW_DEFAULT = 2 + ALLOW_OTHER = 4 + ALLOW_ALL = ALLOW_DEFAULT | ALLOW_OTHER + + +class BranchRelativePermissionDecision(StrEnum): + """This enum is only used to communicate a permission decision relative to a branch.""" + + DENY = auto() + ALLOW = auto() + ALLOW_DEFAULT = auto() + ALLOW_OTHER = auto() diff --git a/backend/infrahub/permissions/local_backend.py b/backend/infrahub/permissions/local_backend.py new file mode 100644 index 0000000000..19b50a5dc3 --- /dev/null +++ b/backend/infrahub/permissions/local_backend.py @@ -0,0 +1,135 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from infrahub import config +from infrahub.core.account import GlobalPermission, ObjectPermission, fetch_permissions, fetch_role_permissions +from infrahub.core.constants import GlobalPermissions, PermissionDecision +from infrahub.core.manager import NodeManager +from infrahub.core.protocols import CoreAccountRole +from infrahub.permissions.constants import PermissionDecisionFlag + +from .backend import PermissionBackend + +if TYPE_CHECKING: + from infrahub.auth import AccountSession + 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.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 + if not permission.decision & PermissionDecisionFlag.ALLOW_ALL: + specificity += 1 + return specificity + + def report_object_permission( + self, permissions: list[ObjectPermission], namespace: str, name: str, action: str + ) -> PermissionDecisionFlag: + """Given a set of permissions, return the permission decision for a given kind and action.""" + highest_specificity: int = -1 + combined_decision = PermissionDecisionFlag.DENY + + for permission in permissions: + if ( + permission.namespace in [namespace, *self.wildcard_values] + and permission.name in [name, *self.wildcard_values] + and permission.action in [action, *self.wildcard_actions] + ): + permission_decision = PermissionDecisionFlag(value=permission.decision) + # 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: + combined_decision = permission_decision + highest_specificity = specificity + elif specificity == highest_specificity and permission_decision != PermissionDecisionFlag.DENY: + combined_decision |= permission_decision + + return combined_decision + + def resolve_object_permission( + self, permissions: list[ObjectPermission], permission_to_check: ObjectPermission + ) -> bool: + """Compute the permissions and check if the one provided is allowed.""" + required_decision = PermissionDecisionFlag(value=permission_to_check.decision) + combined_decision = self.report_object_permission( + permissions=permissions, + namespace=permission_to_check.namespace, + name=permission_to_check.name, + action=permission_to_check.action, + ) + + return combined_decision & required_decision == required_decision + + def resolve_global_permission( + self, permissions: list[GlobalPermission], permission_to_check: GlobalPermission + ) -> bool: + grant_permission = False + + for permission in permissions: + if permission.action == permission_to_check.action: + # Early exit on deny as deny preempt allow + if permission.decision == PermissionDecisionFlag.DENY: + return False + grant_permission = True + + return grant_permission + + async def load_permissions( + self, db: InfrahubDatabase, account_session: AccountSession, branch: Branch + ) -> AssignedPermissions: + if not account_session.authenticated: + anonymous_permissions: AssignedPermissions = {"global_permissions": [], "object_permissions": []} + if not config.SETTINGS.main.allow_anonymous_access: + return anonymous_permissions + + role = await NodeManager.get_one_by_hfid( + db=db, kind=CoreAccountRole, hfid=[config.SETTINGS.main.anonymous_access_role] + ) + if role: + anonymous_permissions = await fetch_role_permissions(db=db, role_id=role.id, branch=branch) + + return anonymous_permissions + + return await fetch_permissions(db=db, account_id=account_session.account_id, branch=branch) + + async def has_permission( + self, + db: InfrahubDatabase, + account_session: AccountSession, + permission: GlobalPermission | ObjectPermission, + branch: Branch, + ) -> bool: + granted_permissions = await self.load_permissions(db=db, account_session=account_session, branch=branch) + is_super_admin = self.resolve_global_permission( + permissions=granted_permissions["global_permissions"], + permission_to_check=GlobalPermission( + action=GlobalPermissions.SUPER_ADMIN, decision=PermissionDecision.ALLOW_ALL + ), + ) + + if isinstance(permission, GlobalPermission): + return ( + self.resolve_global_permission( + permissions=granted_permissions["global_permissions"], permission_to_check=permission + ) + or is_super_admin + ) + + return ( + self.resolve_object_permission( + permissions=granted_permissions["object_permissions"], permission_to_check=permission + ) + or is_super_admin + ) diff --git a/backend/infrahub/permissions/report.py b/backend/infrahub/permissions/report.py new file mode 100644 index 0000000000..2790653a49 --- /dev/null +++ b/backend/infrahub/permissions/report.py @@ -0,0 +1,119 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from infrahub.core import registry +from infrahub.core.account import GlobalPermission +from infrahub.core.constants import GLOBAL_BRANCH_NAME, GlobalPermissions, PermissionDecision +from infrahub.permissions.constants import AssignedPermissions, BranchRelativePermissionDecision, PermissionDecisionFlag +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.backend import PermissionBackend + from infrahub.permissions.types import KindPermissions + + +def get_permission_report( + backend: PermissionBackend, + permissions: AssignedPermissions, + branch: Branch, + node: MainSchemaTypes, + action: str, + is_super_admin: bool = False, + can_edit_default_branch: bool = False, # pylint: disable=unused-argument +) -> BranchRelativePermissionDecision: + is_default_branch = branch.name in (GLOBAL_BRANCH_NAME, registry.default_branch) + + if is_super_admin: + return BranchRelativePermissionDecision.ALLOW + + decision = backend.report_object_permission( + permissions=permissions["object_permissions"], namespace=node.namespace, name=node.name, action=action + ) + + # What do we do if edit default branch global permission is set? + # if can_edit_default_branch: + # decision |= PermissionDecisionFlag.ALLOW_DEFAULT + + if ( + decision == PermissionDecisionFlag.ALLOW_ALL + or (decision & PermissionDecisionFlag.ALLOW_DEFAULT and is_default_branch) + or (decision & PermissionDecisionFlag.ALLOW_OTHER and not is_default_branch) + ): + return BranchRelativePermissionDecision.ALLOW + if decision & PermissionDecisionFlag.ALLOW_DEFAULT: + return BranchRelativePermissionDecision.ALLOW_DEFAULT + if decision & PermissionDecisionFlag.ALLOW_OTHER: + return BranchRelativePermissionDecision.ALLOW_OTHER + + return BranchRelativePermissionDecision.DENY + + +async def report_schema_permissions( + db: InfrahubDatabase, schemas: list[MainSchemaTypes], account_session: AccountSession, branch: Branch +) -> list[KindPermissions]: + perm_backend = LocalPermissionBackend() + permissions = await perm_backend.load_permissions(db=db, account_session=account_session, branch=branch) + + is_super_admin = perm_backend.resolve_global_permission( + permissions=permissions["global_permissions"], + permission_to_check=GlobalPermission( + action=GlobalPermissions.SUPER_ADMIN.value, decision=PermissionDecision.ALLOW_ALL.value + ), + ) + can_edit_default_branch = perm_backend.resolve_global_permission( + permissions=permissions["global_permissions"], + permission_to_check=GlobalPermission( + action=GlobalPermissions.EDIT_DEFAULT_BRANCH.value, decision=PermissionDecision.ALLOW_ALL.value + ), + ) + + permission_objects: list[KindPermissions] = [] + for node in schemas: + permission_objects.append( + { + "kind": node.kind, + "create": get_permission_report( + backend=perm_backend, + permissions=permissions, + branch=branch, + node=node, + action="create", + is_super_admin=is_super_admin, + can_edit_default_branch=can_edit_default_branch, + ), + "delete": get_permission_report( + backend=perm_backend, + permissions=permissions, + branch=branch, + node=node, + action="delete", + is_super_admin=is_super_admin, + can_edit_default_branch=can_edit_default_branch, + ), + "update": get_permission_report( + backend=perm_backend, + permissions=permissions, + branch=branch, + node=node, + action="update", + is_super_admin=is_super_admin, + can_edit_default_branch=can_edit_default_branch, + ), + "view": get_permission_report( + backend=perm_backend, + permissions=permissions, + branch=branch, + node=node, + action="view", + is_super_admin=is_super_admin, + can_edit_default_branch=can_edit_default_branch, + ), + } + ) + + return permission_objects diff --git a/backend/infrahub/permissions/types.py b/backend/infrahub/permissions/types.py new file mode 100644 index 0000000000..5c088874b7 --- /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.permissions.constants import BranchRelativePermissionDecision + + +class KindPermissions(TypedDict): + kind: str + create: BranchRelativePermissionDecision + delete: BranchRelativePermissionDecision + update: BranchRelativePermissionDecision + view: BranchRelativePermissionDecision 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/sync/examples/ipfabric_to_infrahub/infrahub/__init__.py b/backend/infrahub/send/__init__.py similarity index 100% rename from sync/examples/ipfabric_to_infrahub/infrahub/__init__.py rename to backend/infrahub/send/__init__.py diff --git a/backend/infrahub/message_bus/messages/send_webhook_event.py b/backend/infrahub/send/models.py similarity index 69% rename from backend/infrahub/message_bus/messages/send_webhook_event.py rename to backend/infrahub/send/models.py index 9ee4284ae0..945d6d5eed 100644 --- a/backend/infrahub/message_bus/messages/send_webhook_event.py +++ b/backend/infrahub/send/models.py @@ -1,9 +1,7 @@ -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") diff --git a/backend/infrahub/send/webhook.py b/backend/infrahub/send/webhook.py new file mode 100644 index 0000000000..3c66ace76b --- /dev/null +++ b/backend/infrahub/send/webhook.py @@ -0,0 +1,37 @@ +from typing import Any + +import ujson +from prefect import flow +from prefect.logging import get_run_logger + +from infrahub.exceptions import NodeNotFoundError +from infrahub.send.models import SendWebhookData +from infrahub.services import services +from infrahub.webhook import CustomWebhook, StandardWebhook, TransformWebhook, Webhook + + +@flow(name="event-send-webhook") +async def send_webhook(model: SendWebhookData) -> None: + service = services.service + log = get_run_logger() + + webhook_definition = await service.cache.get(key=f"webhook:active:{model.webhook_id}") + if not webhook_definition: + log.warning("Webhook not found") + raise NodeNotFoundError( + node_type="Webhook", identifier=model.webhook_id, message="The requested Webhook was not found" + ) + + webhook_data = ujson.loads(webhook_definition) + payload: dict[str, Any] = {"event_type": model.event_type, "data": model.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/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..ba4adffc0f 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() @@ -45,6 +55,9 @@ def client(self) -> InfrahubClient: return self._client + def set_client(self, client: InfrahubClient) -> None: + self._client = client + @property def database(self) -> InfrahubDatabase: if not self._database: @@ -90,10 +103,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..d2e3927acb 100644 --- a/backend/infrahub/services/adapters/message_bus/local.py +++ b/backend/infrahub/services/adapters/message_bus/local.py @@ -4,7 +4,7 @@ from typing import TYPE_CHECKING, Optional, TypeVar import ujson -from infrahub_sdk import UUIDT +from infrahub_sdk.uuidt import UUIDT from infrahub.dependencies.registry import build_component_registry from infrahub.message_bus import InfrahubMessage, Meta @@ -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/nats.py b/backend/infrahub/services/adapters/message_bus/nats.py index f8604e725f..485ae74976 100644 --- a/backend/infrahub/services/adapters/message_bus/nats.py +++ b/backend/infrahub/services/adapters/message_bus/nats.py @@ -6,7 +6,7 @@ import nats import ujson -from infrahub_sdk import UUIDT +from infrahub_sdk.uuidt import UUIDT from opentelemetry import context, propagate, trace from opentelemetry.instrumentation.utils import is_instrumentation_enabled diff --git a/backend/infrahub/services/adapters/message_bus/rabbitmq.py b/backend/infrahub/services/adapters/message_bus/rabbitmq.py index 2ba4421792..37806c6c7d 100644 --- a/backend/infrahub/services/adapters/message_bus/rabbitmq.py +++ b/backend/infrahub/services/adapters/message_bus/rabbitmq.py @@ -6,7 +6,7 @@ import aio_pika import opentelemetry.instrumentation.aio_pika.span_builder import ujson -from infrahub_sdk import UUIDT +from infrahub_sdk.uuidt import UUIDT from opentelemetry.instrumentation.aio_pika import AioPikaInstrumentor from opentelemetry.semconv.trace import SpanAttributes @@ -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..4cc14e16e9 --- /dev/null +++ b/backend/infrahub/services/adapters/workflow/__init__.py @@ -0,0 +1,52 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, Callable, ParamSpec, TypeVar, overload + +if TYPE_CHECKING: + from infrahub.services import InfrahubServices + from infrahub.workflows.models import WorkflowDefinition, WorkflowInfo + +Return = TypeVar("Return") +Params = ParamSpec("Params") + +FuncType = Callable[Params, Return] + + +class InfrahubWorkflow: + async def initialize(self, service: InfrahubServices) -> None: + """Initialize the Workflow engine""" + + @overload + async def execute_workflow( + self, + workflow: WorkflowDefinition, + expected_return: type[Return], + parameters: dict[str, Any] | None = ..., + tags: list[str] | None = ..., + ) -> Return: ... + + @overload + async def execute_workflow( + self, + workflow: WorkflowDefinition, + expected_return: None = ..., + parameters: dict[str, Any] | None = ..., + tags: list[str] | None = ..., + ) -> Any: ... + + async def execute_workflow( + self, + workflow: WorkflowDefinition, + expected_return: type[Return] | None = None, + parameters: dict[str, Any] | None = None, + tags: list[str] | None = None, + ) -> Any: + raise NotImplementedError() + + async def submit_workflow( + self, + workflow: WorkflowDefinition, + parameters: dict[str, Any] | None = None, + tags: list[str] | None = None, + ) -> WorkflowInfo: + 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..f3d7fc8fa0 --- /dev/null +++ b/backend/infrahub/services/adapters/workflow/local.py @@ -0,0 +1,27 @@ +import uuid +from typing import Any + +from infrahub.workflows.models import WorkflowDefinition, WorkflowInfo + +from . import InfrahubWorkflow, Return + + +class WorkflowLocalExecution(InfrahubWorkflow): + async def execute_workflow( + self, + workflow: WorkflowDefinition, + expected_return: type[Return] | None = None, + parameters: dict[str, Any] | None = None, + tags: list[str] | None = None, + ) -> Any: + fn = workflow.get_function() + return await fn(**parameters or {}) + + async def submit_workflow( + self, + workflow: WorkflowDefinition, + parameters: dict[str, Any] | None = None, + tags: list[str] | None = None, + ) -> WorkflowInfo: + await self.execute_workflow(workflow=workflow, parameters=parameters) + return WorkflowInfo(id=uuid.uuid4()) diff --git a/backend/infrahub/services/adapters/workflow/worker.py b/backend/infrahub/services/adapters/workflow/worker.py new file mode 100644 index 0000000000..6946d9d217 --- /dev/null +++ b/backend/infrahub/services/adapters/workflow/worker.py @@ -0,0 +1,67 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any, overload + +from prefect.client.schemas import StateType +from prefect.deployments import run_deployment + +from infrahub.workflows.initialization import setup_task_manager +from infrahub.workflows.models import WorkflowInfo + +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() + + @overload + async def execute_workflow( + self, + workflow: WorkflowDefinition, + expected_return: type[Return], + parameters: dict[str, Any] | None = ..., + tags: list[str] | None = ..., + ) -> Return: ... + + @overload + async def execute_workflow( + self, + workflow: WorkflowDefinition, + expected_return: None = ..., + parameters: dict[str, Any] | None = ..., + tags: list[str] | None = ..., + ) -> Any: ... + + async def execute_workflow( + self, + workflow: WorkflowDefinition, + expected_return: type[Return] | None = None, + parameters: dict[str, Any] | None = None, + tags: list[str] | None = None, + ) -> Any: + response: FlowRun = await run_deployment( + name=workflow.full_name, poll_interval=1, parameters=parameters or {}, tags=tags + ) # 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] + + async def submit_workflow( + self, workflow: WorkflowDefinition, parameters: dict[str, Any] | None = None, tags: list[str] | None = None + ) -> WorkflowInfo: + flow_run = await run_deployment(name=workflow.full_name, timeout=0, parameters=parameters or {}, tags=tags) # type: ignore[return-value, misc] + return WorkflowInfo.from_flow(flow_run=flow_run) 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/services/scheduler.py b/backend/infrahub/services/scheduler.py index a5e1fe0a2e..7094383796 100644 --- a/backend/infrahub/services/scheduler.py +++ b/backend/infrahub/services/scheduler.py @@ -8,7 +8,7 @@ from infrahub import config from infrahub.components import ComponentType from infrahub.tasks.keepalive import refresh_heartbeat -from infrahub.tasks.recurring import push_telemetry, resync_repositories, trigger_branch_refresh +from infrahub.tasks.recurring import trigger_branch_refresh if TYPE_CHECKING: from infrahub.services import InfrahubServices, ServiceFunction @@ -43,23 +43,6 @@ async def initialize(self, service: InfrahubServices) -> None: ] self.schedules.extend(schedules) - if config.SETTINGS.git.sync_interval: - self.schedules.append( - Schedule( - name="resync_repositories", - interval=config.SETTINGS.git.sync_interval, - function=resync_repositories, - ) - ) - if not config.SETTINGS.main.telemetry_optout: - self.schedules.append( - Schedule( - name="push_telemetry", - interval=config.SETTINGS.main.telemetry_interval, - function=push_telemetry, - start_delay=3600, # Start pushing only if running for 1 hour - ) - ) if self.service.component_type == ComponentType.GIT_AGENT: schedules = [ Schedule(name="refresh_components", interval=10, function=refresh_heartbeat), diff --git a/backend/infrahub/tasks/artifact.py b/backend/infrahub/tasks/artifact.py index 1850dc0ffa..7c2ee8c4f6 100644 --- a/backend/infrahub/tasks/artifact.py +++ b/backend/infrahub/tasks/artifact.py @@ -1,15 +1,18 @@ from typing import Union -from infrahub_sdk import InfrahubNode +from infrahub_sdk.node import InfrahubNode +from prefect import task from infrahub import lock from infrahub.core.constants import InfrahubKind +from infrahub.git.models import RequestArtifactGenerate from infrahub.message_bus import messages from infrahub.services import InfrahubServices +@task async def define_artifact( - message: Union[messages.CheckArtifactCreate, messages.RequestArtifactGenerate], service: InfrahubServices + message: Union[messages.CheckArtifactCreate, RequestArtifactGenerate], service: InfrahubServices ) -> InfrahubNode: if message.artifact_id: artifact = await service.client.get( diff --git a/backend/infrahub/tasks/dummy.py b/backend/infrahub/tasks/dummy.py new file mode 100644 index 0000000000..ddf58dc183 --- /dev/null +++ b/backend/infrahub/tasks/dummy.py @@ -0,0 +1,44 @@ +from __future__ import annotations + +from prefect import flow, task +from pydantic import BaseModel + +from infrahub.workflows.models import WorkflowDefinition + + +class DummyInput(BaseModel): + firstname: str + lastname: str + + +class DummyOutput(BaseModel): + full_name: str + + +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", +) + + +@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/tasks/recurring.py b/backend/infrahub/tasks/recurring.py index 40f91cf9f8..644deee2de 100644 --- a/backend/infrahub/tasks/recurring.py +++ b/backend/infrahub/tasks/recurring.py @@ -2,10 +2,6 @@ from typing import TYPE_CHECKING -from infrahub import config -from infrahub.message_bus import messages -from infrahub.worker import WORKER_IDENTITY - from .registry import refresh_branches if TYPE_CHECKING: @@ -18,18 +14,3 @@ async def trigger_branch_refresh(service: InfrahubServices) -> None: await refresh_branches(db=db) await service.component.refresh_schema_hash() - - -async def resync_repositories(service: InfrahubServices) -> None: - if await service.component.is_primary_api(): - service.log.debug(f"Primary identity matches my identity={WORKER_IDENTITY}. Posting sync of repo message.") - message = messages.RequestGitSync() - message.assign_expiration(config.SETTINGS.git.sync_interval) - await service.send(message=message) - - -async def push_telemetry(service: InfrahubServices) -> None: - if await service.component.is_primary_api(): - service.log.debug(f"Primary identity matches my identity={WORKER_IDENTITY}. Pushing usage telemetry.") - message = messages.SendTelemetryPush() - await service.send(message=message) diff --git a/backend/infrahub/message_bus/operations/send/telemetry.py b/backend/infrahub/tasks/telemetry.py similarity index 74% rename from backend/infrahub/message_bus/operations/send/telemetry.py rename to backend/infrahub/tasks/telemetry.py index ad4ccc9997..b481b2214a 100644 --- a/backend/infrahub/message_bus/operations/send/telemetry.py +++ b/backend/infrahub/tasks/telemetry.py @@ -2,23 +2,25 @@ 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.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 +35,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 +46,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 +64,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,14 +94,25 @@ async def gather_anonymous_telemetry_data(service: InfrahubServices) -> dict: return data -async def push( - message: messages.SendTelemetryPush, # pylint: disable=unused-argument - service: InfrahubServices, -) -> None: - service.log.debug("Received telemetry push message...") +@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(name="anonymous-telemetry-push") +async def send_telemetry_push() -> None: + service = services.service + log = get_run_logger() + if config.SETTINGS.main.telemetry_optout: + log.info("Skipping, User opted out of this service.") + return + + log.info(f"Pushing anonymous telemetry data to {config.SETTINGS.main.telemetry_endpoint}...") data = await gather_anonymous_telemetry_data(service=service) - service.log.debug(f"Anonymous usage telemetry gathered in {data['execution_time']} seconds.") + log.info(f"Anonymous usage telemetry gathered in {data['execution_time']} seconds. | {data}") payload = { "kind": TELEMETRY_KIND, @@ -105,9 +121,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/test_data/gen_connected_nodes.py b/backend/infrahub/test_data/gen_connected_nodes.py deleted file mode 100644 index d258f02d84..0000000000 --- a/backend/infrahub/test_data/gen_connected_nodes.py +++ /dev/null @@ -1,84 +0,0 @@ -import random -import uuid - -from infrahub.core import registry -from infrahub.core.constants import InfrahubKind -from infrahub.core.node import Node -from infrahub.log import get_logger - -from .shared import DataGenerator - -log = get_logger() - - -class GenerateConnectedNodes(DataGenerator): - async def load_data(self, nbr_tags: int = 50, nbr_repository: int = 100, nbr_query: int = 1000): - """Generate a large number of GraphQLQuery associated with some Tags and some Repositorie.""" - default_branch = await registry.get_branch(db=self.db) - - if self.progress: - task_tag = self.progress.add_task("Loading TAG", total=nbr_tags) - task_repo = self.progress.add_task("Loading REPOSITORY", total=nbr_repository) - task_query = self.progress.add_task("Loading QUERY", total=nbr_query) - - tags = {} - repository = {} - gqlquery = {} - - tag_schema = registry.schema.get_node_schema(name=InfrahubKind.TAG, branch=default_branch) - repository_schema = registry.schema.get_node_schema(name=InfrahubKind.REPOSITORY, branch=default_branch) - gqlquery_schema = registry.schema.get_node_schema(name=InfrahubKind.GRAPHQLQUERY, branch=default_branch) - - # ------------------------------------------------------------------------------------- - # TAG - # ------------------------------------------------------------------------------------- - batch = self.create_batch() - for _ in range(nbr_tags): - short_id = str(uuid.uuid4())[:8] - tag_name = f"tag-{short_id}" - obj = await Node.init(db=self.db, schema=tag_schema, branch=default_branch) - await obj.new(db=self.db, name=tag_name) - batch.add(task=self.save_obj, obj=obj) - tags[tag_name] = obj - - async for _ in batch.execute(): - if self.progress: - self.progress.advance(task_tag) - - # ------------------------------------------------------------------------------------- - # REPOSITORY - # ------------------------------------------------------------------------------------- - batch = self.create_batch() - for _ in range(nbr_repository): - short_id = str(uuid.uuid4())[:8] - repo_name = f"repository-{short_id}" - obj = await Node.init(db=self.db, schema=repository_schema, branch=default_branch) - random_tags = [tags[tag] for tag in random.choices(list(tags.keys()), k=5)] - await obj.new(db=self.db, name=repo_name, location=f"git://{repo_name}", tags=random_tags) - batch.add(task=self.save_obj, obj=obj) - repository[repo_name] = obj - - async for _ in batch.execute(): - if self.progress: - self.progress.advance(task_repo) - - # ------------------------------------------------------------------------------------- - # GRAPHQL_QUERY - # ------------------------------------------------------------------------------------- - batch = self.create_batch() - for _ in range(nbr_query): - short_id = str(uuid.uuid4())[:8] - - random_tags = [tags[tag] for tag in random.choices(list(tags.keys()), k=5)] - random_repo = repository[random.choice(list(repository.keys()))] - - name = f"query-{nbr_query:04}-{short_id}" - query_str = "query CoreQuery%s { tag { name { value }}}" % f"{nbr_query:04}" - obj = await Node.init(db=self.db, schema=gqlquery_schema, branch=default_branch) - await obj.new(db=self.db, name=name, query=query_str, tags=random_tags, repository=random_repo) - batch.add(task=self.save_obj, obj=obj, db=self.db) - gqlquery[name] = obj - - async for _ in batch.execute(): - if self.progress: - self.progress.advance(task_query) diff --git a/backend/infrahub/test_data/gen_isolated_node.py b/backend/infrahub/test_data/gen_isolated_node.py deleted file mode 100644 index 8dcf06360a..0000000000 --- a/backend/infrahub/test_data/gen_isolated_node.py +++ /dev/null @@ -1,62 +0,0 @@ -import uuid - -from infrahub.core import registry -from infrahub.core.constants import InfrahubKind -from infrahub.core.node import Node -from infrahub.log import get_logger - -from .shared import DataGenerator - -log = get_logger() - - -class GenerateIsolatedNodes(DataGenerator): - async def load_data( - self, - nbr_tags: int = 100, - nbr_repository: int = 100, - ): - """Generate a large number of Tags and Repositories""" - default_branch = await registry.get_branch(db=self.db) - - if self.progress: - task_tag = self.progress.add_task("Loading TAG", total=nbr_tags) - task_repo = self.progress.add_task("Loading REPOSITORY", total=nbr_repository) - - tags = {} - repository = {} - - tag_schema = registry.schema.get_node_schema(name=InfrahubKind.TAG, branch=default_branch) - repository_schema = registry.schema.get_node_schema(name=InfrahubKind.REPOSITORY, branch=default_branch) - - # ------------------------------------------------------------------------------------- - # TAG - # ------------------------------------------------------------------------------------- - batch = self.create_batch() - for _ in range(nbr_tags): - short_id = str(uuid.uuid4())[:8] - tag_name = f"tag-{short_id}" - obj = await Node.init(db=self.db, schema=tag_schema, branch=default_branch) - await obj.new(db=self.db, name=tag_name) - batch.add(task=self.save_obj, obj=obj) - tags[tag_name] = obj - - async for _ in batch.execute(): - if self.progress: - self.progress.advance(task_tag) - - # ------------------------------------------------------------------------------------- - # REPOSITORY - # ------------------------------------------------------------------------------------- - batch = self.create_batch() - for _ in range(nbr_repository): - short_id = str(uuid.uuid4())[:8] - repo_name = f"repository-{short_id}" - obj = await Node.init(db=self.db, schema=repository_schema, branch=default_branch) - await obj.new(db=self.db, name=repo_name, location=f"git://{repo_name}") - batch.add(task=self.save_obj, obj=obj) - repository[repo_name] = obj - - async for _ in batch.execute(): - if self.progress: - self.progress.advance(task_repo) diff --git a/backend/infrahub/test_data/gen_node_profile_node.py b/backend/infrahub/test_data/gen_node_profile_node.py deleted file mode 100644 index e094d46aae..0000000000 --- a/backend/infrahub/test_data/gen_node_profile_node.py +++ /dev/null @@ -1,55 +0,0 @@ -import random -import uuid - -from infrahub.core import registry -from infrahub.core.node import Node -from infrahub.log import get_logger - -from .shared import DataGenerator - -log = get_logger() - - -class ProfileAttribute(DataGenerator): - async def load_data( - self, - nbr_person: int = 100, - ): - """Generate a large number of Tags and Repositories""" - default_branch = await registry.get_branch(db=self.db) - - if self.progress: - task_person = self.progress.add_task("Loading PERSON", total=nbr_person) - - persons = {} - - person_profile_schema = registry.schema.get(name="ProfileTestPerson", branch=default_branch) - profile1 = await Node.init(db=self.db, schema=person_profile_schema, branch=default_branch) - await profile1.new(db=self.db, profile_name="profile1", profile_priority=1000, height=180) - await profile1.save(db=self.db) - profile2 = await Node.init(db=self.db, schema=person_profile_schema, branch=default_branch) - await profile2.new(db=self.db, profile_name="profile2", profile_priority=1200, height=150) - await profile2.save(db=self.db) - - person_schema = registry.schema.get_node_schema(name="TestPerson", branch=default_branch) - # ------------------------------------------------------------------------------------- - # TAG - # ------------------------------------------------------------------------------------- - batch = self.create_batch() - for _ in range(nbr_person): - short_id = str(uuid.uuid4())[:8] - rand_height = random.randrange(200) - name = f"nbr_person-{short_id}" - obj = await Node.init(db=self.db, schema=person_schema, branch=default_branch) - - profile = profile2 - if rand_height % 2 == 0: - profile = profile1 - - await obj.new(db=self.db, name=name, profiles=[profile]) - batch.add(task=self.save_obj, obj=obj) - persons[name] = obj - - async for _ in batch.execute(): - if self.progress: - self.progress.advance(task_person) diff --git a/backend/infrahub/test_data/shared.py b/backend/infrahub/test_data/shared.py deleted file mode 100644 index 9421263500..0000000000 --- a/backend/infrahub/test_data/shared.py +++ /dev/null @@ -1,67 +0,0 @@ -import asyncio -from dataclasses import dataclass -from typing import Any, Awaitable, Callable, Optional - -from infrahub_sdk.batch import BatchTask, InfrahubBatch -from rich.progress import Progress - -from infrahub.core.node import Node -from infrahub.database import InfrahubDatabase - - -@dataclass -class CallbackTask: - name: str - task: Callable[[Any], Awaitable[Any]] - args: tuple[Any, ...] - kwargs: dict[str, Any] - - -class DataGeneratorBatch(InfrahubBatch): - def __init__( - self, - callbacks: Optional[list[CallbackTask]] = None, - callback_frequency: int = 10, - semaphore: Optional[asyncio.Semaphore] = None, - max_concurrent_execution: int = 5, - return_exceptions: bool = False, - ): - super().__init__( - semaphore=semaphore, max_concurrent_execution=max_concurrent_execution, return_exceptions=return_exceptions - ) - self.callbacks: list[CallbackTask] = callbacks or [] - self.callback_frequency = callback_frequency - - def add(self, *args: Any, **kwargs: Any) -> None: - super().add(*args, **kwargs) - - if len(self._tasks) % self.callback_frequency == 0: - for callback in self.callbacks: - self._tasks.append(BatchTask(task=callback.task, args=callback.args, kwargs=callback.kwargs)) - - -class DataGenerator: - def __init__(self, db: InfrahubDatabase, concurrent_execution: int = 2, progress: Optional[Progress] = None): - self.db = db - self.concurrent_execution = concurrent_execution - self.progress = progress - self.callbacks: list[CallbackTask] = [] - - def add_callback(self, *args: Any, callback_name: str, **kwargs: Any) -> None: - self.callbacks.append(CallbackTask(name=callback_name, task=self.execute_db_task, args=args, kwargs=kwargs)) - - async def execute_db_task(self, task: Callable[[Any], Awaitable[Any]], **kwargs: Any) -> Any: - async with self.db.start_session() as dbs: - return await task(db=dbs, **kwargs) - - async def save_obj(self, obj: Node) -> Node: - async with self.db.start_session() as dbs: - async with dbs.start_transaction() as dbt: - await obj.save(db=dbt) - - return obj - - def create_batch(self) -> DataGeneratorBatch: - return DataGeneratorBatch( - max_concurrent_execution=self.concurrent_execution, return_exceptions=True, callbacks=self.callbacks - ) diff --git a/backend/infrahub/trace.py b/backend/infrahub/trace.py index c332380b61..973dfaef72 100644 --- a/backend/infrahub/trace.py +++ b/backend/infrahub/trace.py @@ -55,7 +55,11 @@ def add_span_exception(exception: Exception) -> None: def create_tracer_provider( - service: str, version: str, exporter_type: str, exporter_endpoint: str = None, exporter_protocol: str = None + service: str, + version: str, + exporter_type: str, + exporter_endpoint: str | None = None, + exporter_protocol: str | None = None, ) -> TracerProvider: # Create a BatchSpanProcessor exporter based on the type if exporter_type == "console": @@ -91,7 +95,11 @@ def create_tracer_provider( def configure_trace( - service: str, version: str, exporter_type: str, exporter_endpoint: str | None = None, exporter_protocol: str = None + service: str, + version: str, + exporter_type: str, + exporter_endpoint: str | None = None, + exporter_protocol: str | None = None, ) -> None: # Create a trace provider with the exporter tracer_provider = create_tracer_provider( diff --git a/sync/examples/ipfabric_to_infrahub/ipfabricsync/__init__.py b/backend/infrahub/transformations/__init__.py similarity index 100% rename from sync/examples/ipfabric_to_infrahub/ipfabricsync/__init__.py rename to backend/infrahub/transformations/__init__.py diff --git a/backend/infrahub/transformations/models.py b/backend/infrahub/transformations/models.py new file mode 100644 index 0000000000..25886dc8c2 --- /dev/null +++ b/backend/infrahub/transformations/models.py @@ -0,0 +1,25 @@ +from pydantic import BaseModel, Field + + +class TransformPythonData(BaseModel): + """Sent to run a Python transform.""" + + repository_id: str = Field(..., description="The unique ID of the Repository") + repository_name: str = Field(..., description="The name of the repository") + repository_kind: str = Field(..., description="The kind of the repository") + data: dict = Field(..., description="Input data for the template") + branch: str = Field(..., description="The branch to target") + transform_location: str = Field(..., description="Location of the transform within the repository") + commit: str = Field(..., description="The commit id to use when rendering the template") + + +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") + repository_name: str = Field(..., description="The name of the repository") + repository_kind: str = Field(..., description="The kind of the repository") + data: dict = Field(..., description="Input data for the template") + branch: str = Field(..., description="The branch to target") + template_location: str = Field(..., description="Location of the template within the repository") + commit: str = Field(..., description="The commit id to use when rendering the template") diff --git a/backend/infrahub/transformations/tasks.py b/backend/infrahub/transformations/tasks.py new file mode 100644 index 0000000000..a0ef7899ac --- /dev/null +++ b/backend/infrahub/transformations/tasks.py @@ -0,0 +1,54 @@ +from typing import Any + +from prefect import flow + +from infrahub.git.repository import get_initialized_repo +from infrahub.log import get_logger +from infrahub.services import services +from infrahub.workflows.utils import add_branch_tag + +from .models import TransformJinjaTemplateData, TransformPythonData + +log = get_logger() + + +@flow(name="transform-render-python") +async def transform_python(message: TransformPythonData) -> Any: + service = services.service + await add_branch_tag(branch_name=message.branch) + + repo = await get_initialized_repo( + repository_id=message.repository_id, + name=message.repository_name, + service=service, + repository_kind=message.repository_kind, + ) + + transformed_data = await repo.execute_python_transform( + branch_name=message.branch, + commit=message.commit, + location=message.transform_location, + data=message.data, + client=service.client, + ) + + return transformed_data + + +@flow(name="transform-render-jinja2") +async def transform_render_jinja2_template(message: TransformJinjaTemplateData) -> str: + service = services.service + await add_branch_tag(branch_name=message.branch) + + 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/types.py b/backend/infrahub/types.py index 9ee6ef4006..5387a71db9 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 @@ -365,6 +365,9 @@ class Any(InfrahubDataType): ATTRIBUTE_KIND_LABELS = list(ATTRIBUTE_TYPES.keys()) +# Data types supporting large values, which can therefore not be indexed in neo4j. +LARGE_ATTRIBUTE_TYPES = [TextArea, JSON] + def get_attribute_type(kind: str = "Default") -> type[InfrahubDataType]: """Return an InfrahubDataType object for a given kind 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/librenms_to_infrahub/infrahub/__init__.py b/backend/infrahub/workers/__init__.py similarity index 100% rename from sync/examples/librenms_to_infrahub/infrahub/__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..2ffe2ca11d --- /dev/null +++ b/backend/infrahub/workers/infrahub_async.py @@ -0,0 +1,202 @@ +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 __version__ as infrahub_version +from infrahub import config +from infrahub.components import ComponentType +from infrahub.core import registry +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 import InfrahubCache +from infrahub.services.adapters.cache.nats import NATSCache +from infrahub.services.adapters.cache.redis import RedisCache +from infrahub.services.adapters.message_bus import InfrahubMessageBus +from infrahub.services.adapters.message_bus.nats import NATSMessageBus +from infrahub.services.adapters.message_bus.rabbitmq import RabbitMQMessageBus +from infrahub.services.adapters.workflow import InfrahubWorkflow +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 + +WORKER_QUERY_SECONDS = "2" +WORKER_PERSIST_RESULT = "true" +WORKER_DEFAULT_RESULT_STORAGE_BLOCK = f"redisstoragecontainer/{TASK_RESULT_STORAGE_NAME}" + + +class InfrahubWorkerAsyncConfiguration(BaseJobConfiguration): + env: dict[str, str | None] = { + "PREFECT_WORKER_QUERY_SECONDS": WORKER_QUERY_SECONDS, + "PREFECT_RESULTS_PERSIST_BY_DEFAULT": WORKER_PERSIST_RESULT, + "PREFECT_DEFAULT_RESULT_STORAGE_BLOCK": WORKER_DEFAULT_RESULT_STORAGE_BLOCK, + } + labels: dict[str, str] = { + "infrahub.app/version": infrahub_version, + } + + +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, + client: InfrahubClient | None = None, + metric_port: int | None = None, + **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) + + if not config.SETTINGS.settings: + config_file = os.environ.get("INFRAHUB_CONFIG", "infrahub.toml") + config.load_and_exit(config_file_name=config_file) + + # Start metric endpoint + if metric_port is None or metric_port != 0: + metric_port = metric_port or int(os.environ.get("INFRAHUB_METRICS_PORT", 8000)) + self._logger.info(f"Starting metric endpoint on port {metric_port}") + start_http_server(metric_port) + + await super().setup(**kwargs) + + 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: WORKER_DEFAULT_RESULT_STORAGE_BLOCK, + } + ) + ) + + client = await self._init_infrahub_client(client=client) + service = await self._init_services(client=client) + + if not registry.schema_has_been_initialized(): + 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") + + return InfrahubWorkerAsyncResult( + status_code=0, + identifier=str(flow_run.id), + ) + + async def _init_infrahub_client(self, client: InfrahubClient | None = None) -> InfrahubClient: + if not client: + 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) + + return client + + async def _init_database(self) -> InfrahubDatabase: + return InfrahubDatabase(driver=await get_db(retry=1)) + + async def _init_workflow(self) -> InfrahubWorkflow: + return config.OVERRIDE.workflow or ( + WorkflowWorkerExecution() + if config.SETTINGS.workflow.driver == config.WorkflowDriver.WORKER + else WorkflowLocalExecution() + ) + + async def _init_message_bus(self) -> InfrahubMessageBus: + return config.OVERRIDE.message_bus or ( + NATSMessageBus() if config.SETTINGS.broker.driver == config.BrokerDriver.NATS else RabbitMQMessageBus() + ) + + async def _init_cache(self) -> InfrahubCache: + return config.OVERRIDE.cache or ( + NATSCache() if config.SETTINGS.cache.driver == config.CacheDriver.NATS else RedisCache() + ) + + async def _init_services(self, client: InfrahubClient) -> InfrahubServices: + database = await self._init_database() + workflow = await self._init_workflow() + message_bus = await self._init_message_bus() + cache = await self._init_cache() + + 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() + + return service diff --git a/sync/examples/librenms_to_infrahub/librenms/__init__.py b/backend/infrahub/workflows/__init__.py similarity index 100% rename from sync/examples/librenms_to_infrahub/librenms/__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..856f89a5f4 --- /dev/null +++ b/backend/infrahub/workflows/catalogue.py @@ -0,0 +1,163 @@ +from infrahub.core.constants import BranchSupportType + +from .constants import WorkflowTag, 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="event-send-webhook", + type=WorkflowType.USER, + module="infrahub.send.webhook", + function="send_webhook", +) + +TRANSFORM_JINJA2_RENDER = WorkflowDefinition( + name="transform_render_jinja2_template", + type=WorkflowType.USER, + module="infrahub.transformations.tasks", + function="transform_render_jinja2_template", + branch_support=BranchSupportType.AWARE, +) + +TRANSFORM_PYTHON_RENDER = WorkflowDefinition( + name="transform_render_python", + type=WorkflowType.USER, + module="infrahub.transformations.tasks", + function="transform_python", + branch_support=BranchSupportType.AWARE, +) + +ANONYMOUS_TELEMETRY_SEND = WorkflowDefinition( + name="anonymous_telemetry_send", + type=WorkflowType.INTERNAL, + cron="0 2 * * *", + module="infrahub.tasks.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", + branch_support=BranchSupportType.AWARE, + tags=[WorkflowTag.DATABASE_CHANGE], +) + +SCHEMA_VALIDATE_MIGRATION = WorkflowDefinition( + name="schema_validate_migrations", + type=WorkflowType.INTERNAL, + module="infrahub.core.validators.tasks", + function="schema_validate_migrations", + branch_support=BranchSupportType.AWARE, +) + +TRIGGER_ARTIFACT_DEFINITION_GENERATE = WorkflowDefinition( + name="artifact-definition-generate", + type=WorkflowType.INTERNAL, + module="infrahub.git.tasks", + function="generate_artifact_definition", +) + +IPAM_RECONCILIATION = WorkflowDefinition( + name="ipam_reconciliation", + type=WorkflowType.INTERNAL, + module="infrahub.core.ipam.tasks", + function="ipam_reconciliation", + branch_support=BranchSupportType.AWARE, + tags=[WorkflowTag.DATABASE_CHANGE], +) + +REQUEST_GENERATOR_RUN = WorkflowDefinition( + name="generator-run", + type=WorkflowType.INTERNAL, + module="infrahub.generators.tasks", + function="run_generator", +) + +REQUEST_ARTIFACT_GENERATE = WorkflowDefinition( + name="artifact-generate", + type=WorkflowType.INTERNAL, + module="infrahub.git.tasks", + function="generate_artifact", +) + +REQUEST_ARTIFACT_DEFINITION_GENERATE = WorkflowDefinition( + name="artifact-definition-generate", + type=WorkflowType.INTERNAL, + module="infrahub.git.tasks", + function="generate_request_artifact_definition", +) + +REQUEST_DIFF_UPDATE = WorkflowDefinition( + name="diff-update", + type=WorkflowType.INTERNAL, + module="infrahub.core.diff.tasks", + function="update_diff", +) + +REQUEST_DIFF_REFRESH = WorkflowDefinition( + name="diff-refresh", + type=WorkflowType.INTERNAL, + module="infrahub.core.diff.tasks", + function="refresh_diff", +) + +GIT_REPOSITORIES_SYNC = WorkflowDefinition( + name="git_repositories_sync", + type=WorkflowType.INTERNAL, + cron="* * * * *", + module="infrahub.git.tasks", + function="sync_remote_repositories", +) + +GIT_REPOSITORIES_CREATE_BRANCH = WorkflowDefinition( + name="git_repositories_create_branch", + type=WorkflowType.INTERNAL, + module="infrahub.git.tasks", + function="create_branch", + branch_support=BranchSupportType.AWARE, + tags=[WorkflowTag.DATABASE_CHANGE], +) +BRANCH_REBASE = WorkflowDefinition( + name="branch-rebase", + type=WorkflowType.INTERNAL, + module="infrahub.core.branch.tasks", + function="rebase_branch", + branch_support=BranchSupportType.AWARE, + tags=[WorkflowTag.DATABASE_CHANGE], +) + +BRANCH_MERGE = WorkflowDefinition( + name="branch-merge", + type=WorkflowType.INTERNAL, + module="infrahub.core.branch.tasks", + function="merge_branch", + branch_support=BranchSupportType.AWARE, + tags=[WorkflowTag.DATABASE_CHANGE], +) + +worker_pools = [INFRAHUB_WORKER_POOL] + +workflows = [ + WEBHOOK_SEND, + TRANSFORM_JINJA2_RENDER, + TRANSFORM_PYTHON_RENDER, + ANONYMOUS_TELEMETRY_SEND, + SCHEMA_APPLY_MIGRATION, + SCHEMA_VALIDATE_MIGRATION, + TRIGGER_ARTIFACT_DEFINITION_GENERATE, + IPAM_RECONCILIATION, + GIT_REPOSITORIES_SYNC, + GIT_REPOSITORIES_CREATE_BRANCH, + REQUEST_ARTIFACT_GENERATE, + BRANCH_REBASE, + BRANCH_MERGE, + REQUEST_ARTIFACT_DEFINITION_GENERATE, + REQUEST_GENERATOR_RUN, + REQUEST_DIFF_UPDATE, + REQUEST_DIFF_REFRESH, +] diff --git a/backend/infrahub/workflows/constants.py b/backend/infrahub/workflows/constants.py new file mode 100644 index 0000000000..1597b2a1cb --- /dev/null +++ b/backend/infrahub/workflows/constants.py @@ -0,0 +1,21 @@ +from infrahub.utils import InfrahubStringEnum + + +class WorkflowType(InfrahubStringEnum): + INTERNAL = "internal" + USER = "user" + + +TAG_NAMESPACE = "infrahub.app" + + +class WorkflowTag(InfrahubStringEnum): + BRANCH = "branch/{identifier}" + WORKFLOWTYPE = "workflow-type/{identifier}" + DATABASE_CHANGE = "database-change" + + def render(self, identifier: str | None = None) -> str: + if identifier is None: + return f"{TAG_NAMESPACE}/{self.value}" + rendered_value = str(self.value).format(identifier=identifier) + return f"{TAG_NAMESPACE}/{rendered_value}" diff --git a/backend/infrahub/workflows/initialization.py b/backend/infrahub/workflows/initialization.py new file mode 100644 index 0000000000..11ac5a33ad --- /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, overwrite=True) + 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..cde6e63320 --- /dev/null +++ b/backend/infrahub/workflows/models.py @@ -0,0 +1,79 @@ +import importlib +from typing import Any, Awaitable, Callable, TypeVar +from uuid import UUID + +from prefect.client.orchestration import PrefectClient +from prefect.client.schemas.actions import DeploymentScheduleCreate +from prefect.client.schemas.objects import FlowRun +from prefect.client.schemas.schedules import CronSchedule +from pydantic import BaseModel, Field +from typing_extensions import Self + +from infrahub import __version__ +from infrahub.core.constants import BranchSupportType + +from .constants import WorkflowTag, WorkflowType + +TASK_RESULT_STORAGE_NAME = "infrahub-storage" + +WorkflowReturn = TypeVar("WorkflowReturn") + + +class WorkerPoolDefinition(BaseModel): + name: str + worker_type: str + description: str = "" + + +class WorkflowInfo(BaseModel): + id: UUID + info: FlowRun | None = None + + @classmethod + def from_flow(cls, flow_run: FlowRun) -> Self: + return cls(id=flow_run.id, info=flow_run) + + +class WorkflowDefinition(BaseModel): + name: str + type: WorkflowType = WorkflowType.INTERNAL + module: str + function: str + cron: str | None = None + branch_support: BranchSupportType = BranchSupportType.AGNOSTIC + tags: list[WorkflowTag] = Field(default_factory=list) + + @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, "tags": self.get_tags()} + if self.type == WorkflowType.INTERNAL: + payload["version"] = __version__ + if self.cron: + payload["schedules"] = [DeploymentScheduleCreate(schedule=CronSchedule(cron=self.cron))] + + return payload + + def get_tags(self) -> list[str]: + tags: list[str] = [WorkflowTag.WORKFLOWTYPE.render(identifier=self.type.value)] + tags += [tag.render() for tag in self.tags] + return tags + + 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/infrahub/workflows/utils.py b/backend/infrahub/workflows/utils.py new file mode 100644 index 0000000000..b32e0676fc --- /dev/null +++ b/backend/infrahub/workflows/utils.py @@ -0,0 +1,13 @@ +from prefect import get_client, task +from prefect.runtime import flow_run + +from .constants import WorkflowTag + + +@task(name="add-branch-tag") +async def add_branch_tag(branch_name: str) -> None: + client = get_client(sync_client=False) + current_flow_run_id = flow_run.id + current_tags: list[str] = flow_run.tags + new_tags = current_tags + [WorkflowTag.BRANCH.render(identifier=branch_name)] + await client.update_flow_run(current_flow_run_id, tags=list(new_tags)) 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..d806572173 100644 --- a/backend/tests/adapters/message_bus.py +++ b/backend/tests/adapters/message_bus.py @@ -2,7 +2,7 @@ from typing import Optional, TypeVar import ujson -from infrahub_sdk import UUIDT +from infrahub_sdk.uuidt import UUIDT from infrahub.components import ComponentType from infrahub.database import InfrahubDatabase @@ -13,12 +13,13 @@ from infrahub.message_bus.types import MessageTTL from infrahub.services import InfrahubServices from infrahub.services.adapters.message_bus import InfrahubMessageBus +from infrahub.services.adapters.workflow import InfrahubWorkflow ResponseClass = TypeVar("ResponseClass") 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,10 +37,10 @@ def seen_routing_keys(self) -> list[str]: class BusSimulator(InfrahubMessageBus): - def __init__(self, database: Optional[InfrahubDatabase] = None): + def __init__(self, database: InfrahubDatabase | None = None, workflow: InfrahubWorkflow | None = None) -> None: self.messages: list[InfrahubMessage] = [] self.messages_per_routing_key: dict[str, list[InfrahubMessage]] = {} - self.service: InfrahubServices = InfrahubServices(database=database, message_bus=self) + self.service: InfrahubServices = InfrahubServices(database=database, message_bus=self, workflow=workflow) self.replies: dict[str, list[InfrahubMessage]] = defaultdict(list) build_component_registry() @@ -50,7 +51,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_get_menu.py b/backend/tests/benchmark/test_get_menu.py index c4aea5013b..c4c944300b 100644 --- a/backend/tests/benchmark/test_get_menu.py +++ b/backend/tests/benchmark/test_get_menu.py @@ -1,6 +1,14 @@ +import pytest + from infrahub.api.menu import get_menu +from infrahub.core.initialization import create_default_menu from infrahub.database import InfrahubDatabase -def test_get_menu(aio_benchmark, db: InfrahubDatabase, default_branch, register_core_models_schema): - aio_benchmark(get_menu, branch=default_branch) +@pytest.fixture +async def init_menu(db: InfrahubDatabase, default_branch, register_core_models_schema): + await create_default_menu(db=db) + + +def test_get_menu(aio_benchmark, db: InfrahubDatabase, default_branch, register_core_models_schema, init_menu): + aio_benchmark(get_menu, db=db, branch=default_branch, account_session=None) diff --git a/backend/tests/benchmark/test_graphql_query.py b/backend/tests/benchmark/test_graphql_query.py index e2d3d41c8f..c8353c9238 100644 --- a/backend/tests/benchmark/test_graphql_query.py +++ b/backend/tests/benchmark/test_graphql_query.py @@ -15,11 +15,12 @@ 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.test_data.dataset04 import load_data +from infrahub.graphql.initialization import prepare_graphql_params +from tests.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..3b86ed5b6e 100644 --- a/backend/tests/conftest.py +++ b/backend/tests/conftest.py @@ -2,15 +2,24 @@ 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 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 +32,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 @@ -33,9 +43,20 @@ from infrahub.services.adapters.message_bus import InfrahubMessageBus from tests.adapters.log import FakeLogger from tests.adapters.message_bus import BusRecorder, BusSimulator +from tests.helpers.constants import ( + INFRAHUB_USE_TEST_CONTAINERS, + NEO4J_IMAGE, + PORT_BOLT_NEO4J, + PORT_CLIENT_RABBITMQ, + PORT_HTTP_NEO4J, + PORT_HTTP_RABBITMQ, + PORT_MEMGRAPH, + PORT_NATS, + PORT_PREFECT, + PORT_REDIS, +) +from tests.helpers.utils import get_exposed_port, start_neo4j_container -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") @@ -70,8 +91,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 +164,225 @@ 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 + + +@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 = start_neo4j_container(NEO4J_IMAGE) + request.addfinalizer(container.stop) + + return { + PORT_BOLT_NEO4J: get_exposed_port(container, PORT_BOLT_NEO4J), + PORT_HTTP_NEO4J: get_exposed_port(container, PORT_HTTP_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) + ) + + container.start() + wait_for_logs(container, "Server startup complete;") # wait_container_is_ready does not seem to be enough + request.addfinalizer(container.stop) + + 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) + + container.start() + wait_for_logs(container, "Ready to accept connections tcp") # wait_container_is_ready does not seem to be enough + request.addfinalizer(container.stop) + + 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) + ) + + 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(container.stop) + + 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) + + container.start() + wait_for_logs(container, "Server is ready") # wait_container_is_ready does not seem to be enough + request.addfinalizer(container.stop) + + 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 + config.SETTINGS.workflow.driver = config.WorkflowDriver.LOCAL - 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 +391,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/repos/car-dealership/initial__main/.infrahub.yml b/backend/tests/fixtures/repos/car-dealership/initial__main/.infrahub.yml index 043cf7e1d9..bda3fcde29 100644 --- a/backend/tests/fixtures/repos/car-dealership/initial__main/.infrahub.yml +++ b/backend/tests/fixtures/repos/car-dealership/initial__main/.infrahub.yml @@ -17,6 +17,11 @@ jinja2_transforms: query: "person_with_cars" template_path: "templates/person_with_cars.j2" +python_transforms: + - name: PersonWithCarsTransform + class_name: PersonWithCarsTransform + file_path: "transforms/person_with_cars_transform.py" + artifact_definitions: - name: "Ownership report" artifact_name: "car-owner" diff --git a/sync/examples/nautobot-v1_to_infrahub/infrahub/__init__.py b/backend/tests/fixtures/repos/car-dealership/initial__main/transforms/__init__.py similarity index 100% rename from sync/examples/nautobot-v1_to_infrahub/infrahub/__init__.py rename to backend/tests/fixtures/repos/car-dealership/initial__main/transforms/__init__.py diff --git a/backend/tests/fixtures/repos/car-dealership/initial__main/transforms/person_with_cars_transform.py b/backend/tests/fixtures/repos/car-dealership/initial__main/transforms/person_with_cars_transform.py new file mode 100644 index 0000000000..f340988383 --- /dev/null +++ b/backend/tests/fixtures/repos/car-dealership/initial__main/transforms/person_with_cars_transform.py @@ -0,0 +1,10 @@ +from typing import Any + +from infrahub_sdk.transforms import InfrahubTransform + + +class PersonWithCarsTransform(InfrahubTransform): + query = "person_with_cars" + + async def transform(self, data: dict[str, Any]) -> dict[str, Any]: + return {"name": data["TestingPerson"]["edges"][0]["node"]["name"]["value"]} 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/constants.py b/backend/tests/helpers/constants.py new file mode 100644 index 0000000000..5f61e866db --- /dev/null +++ b/backend/tests/helpers/constants.py @@ -0,0 +1,16 @@ +import os + +from infrahub_sdk.utils import str_to_bool + +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_HTTP_NEO4J = 7474 +PORT_BOLT_NEO4J = 7687 +PORT_MEMGRAPH = 7687 +PORT_PREFECT = 4200 +NEO4J_COMMUNITY_IMAGE = "neo4j:5.20.0-community" +NEO4J_ENTERPRISE_IMAGE = "neo4j:5.20.0-enterprise" +NEO4J_IMAGE = os.getenv("NEO4J_DOCKER_IMAGE", NEO4J_ENTERPRISE_IMAGE) diff --git a/backend/tests/helpers/graphql.py b/backend/tests/helpers/graphql.py index fa769af2e0..13be523a9f 100644 --- a/backend/tests/helpers/graphql.py +++ b/backend/tests/helpers/graphql.py @@ -1,11 +1,11 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Optional +from typing import TYPE_CHECKING, Any 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: @@ -16,10 +16,10 @@ async def graphql_mutation( query: str, db: InfrahubDatabase, - branch: Optional[Branch] = None, - variables: Optional[dict[str, Any]] = None, - service: Optional[InfrahubServices] = None, - account_session: Optional[AccountSession] = None, + branch: Branch | None = None, + variables: dict[str, Any] | None = None, + service: InfrahubServices | None = None, + account_session: AccountSession | None = None, ) -> ExecutionResult: branch = branch or await Branch.get_by_name(name="main", db=db) service = service or services.service @@ -45,8 +45,8 @@ async def graphql_query( query: str, db: InfrahubDatabase, branch: Branch, - variables: Optional[dict[str, Any]] = None, - service: Optional[InfrahubServices] = None, + variables: dict[str, Any] | None = None, + service: InfrahubServices | None = None, ) -> ExecutionResult: service = service or services.service diff --git a/sync/examples/nautobot-v1_to_infrahub/nautobot/__init__.py b/backend/tests/helpers/query_benchmark/__init__.py similarity index 100% rename from sync/examples/nautobot-v1_to_infrahub/nautobot/__init__.py rename to backend/tests/helpers/query_benchmark/__init__.py diff --git a/backend/tests/helpers/query_benchmark/car_person_generators.py b/backend/tests/helpers/query_benchmark/car_person_generators.py new file mode 100644 index 0000000000..7719b8e42b --- /dev/null +++ b/backend/tests/helpers/query_benchmark/car_person_generators.py @@ -0,0 +1,186 @@ +import random +import uuid +from typing import Optional, Tuple + +from infrahub.core import registry +from infrahub.core.node import Node +from tests.helpers.query_benchmark.data_generator import DataGenerator +from tests.helpers.query_benchmark.db_query_profiler import InfrahubDatabaseProfiler + + +class CarGenerator(DataGenerator): + async def load_data(self, nb_elements: int) -> None: + await self.load_cars(nb_elements) + + async def load_cars(self, nb_cars: int, persons: Optional[dict[str, Node]] = None) -> dict[str, Node]: + """ + Load cars and return a mapping car_name -> car_node. + If 'persons' is specified, each car created is linked to a person. + """ + + default_branch = await registry.get_branch(db=self.db) + car_schema = registry.schema.get_node_schema(name="TestCar", branch=default_branch) + + cars = {} + for _ in range(nb_cars): + short_id = str(uuid.uuid4())[:8] + car_name = f"car-{short_id}" + car_node = await Node.init(db=self.db, schema=car_schema, branch=default_branch) + if persons is not None: + random_person = random.choice([persons[person_name] for person_name in persons]) + await car_node.new(db=self.db, name=car_name, nbr_seats=4, owner=random_person) + else: + await car_node.new(db=self.db, name=car_name, nbr_seats=4) + + async with self.db.start_session(): + await car_node.save(db=self.db) + + cars[car_name] = car_node + + return cars + + +class PersonGenerator(DataGenerator): + async def load_data(self, nb_elements: int) -> None: + await self.load_persons(nb_persons=nb_elements) + + async def load_persons( + self, + nb_persons: int, + cars: Optional[dict[str, Node]] = None, + ) -> dict[str, Node]: + """ + Load persons and return a mapping person_name -> person_node. + If 'cars' is specified, each person created is linked to a few random cars. + """ + + default_branch = await registry.get_branch(db=self.db) + person_schema = registry.schema.get_node_schema(name="TestPerson", branch=default_branch) + + persons_names_to_nodes = {} + for _ in range(nb_persons): + short_id = str(uuid.uuid4())[:8] + person_name = f"person-{short_id}" + person_node = await Node.init(db=self.db, schema=person_schema, branch=default_branch) + + if cars is not None: + random_cars = [cars[car_name] for car_name in random.choices(list(cars.keys()), k=5)] + await person_node.new(db=self.db, name=person_name, cars=random_cars) + else: + await person_node.new(db=self.db, name=person_name) + + async with self.db.start_session(): + await person_node.save(db=self.db) + + persons_names_to_nodes[person_name] = person_node + + return persons_names_to_nodes + + +class PersonFromExistingCarGenerator(PersonGenerator): + cars: Optional[dict[str, Node]] # mapping of existing cars names -> node + nb_cars: int + + def __init__(self, db: InfrahubDatabaseProfiler, nb_cars: int) -> None: + super().__init__(db) + self.nb_cars = nb_cars + self.cars = None + + async def init(self) -> None: + """Load cars, that will be later connected to generated persons.""" + self.cars = await CarGenerator(self.db).load_cars(nb_cars=self.nb_cars) + + async def load_data(self, nb_elements: int) -> None: + assert self.cars is not None, "'init' method should be called before 'load_data'" + await self.load_persons(nb_persons=nb_elements, cars=self.cars) + + +class CarFromExistingPersonGenerator(CarGenerator): + persons: Optional[dict[str, Node]] # mapping of existing cars names -> node + nb_persons: int + + def __init__(self, db: InfrahubDatabaseProfiler, nb_persons: int) -> None: + super().__init__(db) + self.nb_persons = nb_persons + self.persons = None + + async def init(self) -> None: + """Load persons, that will be later connected to generated cars.""" + self.persons = await PersonGenerator(self.db).load_persons(nb_persons=self.nb_persons) + + async def load_data(self, nb_elements: int) -> None: + assert self.persons is not None, "'init' method should be called before 'load_data'" + await self.load_cars(nb_cars=nb_elements, persons=self.persons) + + +class CarGeneratorWithOwnerHavingUniqueCar(CarGenerator): + persons: list[Tuple[str, Node]] # mapping of existing cars names -> node + nb_persons: int + nb_cars_loaded: int + + def __init__(self, db: InfrahubDatabaseProfiler, nb_persons: int) -> None: + super().__init__(db) + self.nb_persons = nb_persons + self.persons = [] + self.nb_cars_loaded = 0 + + async def init(self) -> None: + """Load persons, that will be later connected to generated cars.""" + persons = await PersonGenerator(self.db).load_persons(nb_persons=self.nb_persons) + self.persons = list(persons.items()) + + async def load_data(self, nb_elements: int) -> None: + """ + Generate cars with an owner, in a way that an owner can't have multiple cars. + Also generate distinct nb_seats per car. + """ + + default_branch = await registry.get_branch(db=self.db) + car_schema = registry.schema.get_node_schema(name="TestCar", branch=default_branch) + + for i in range(nb_elements): + short_id = str(uuid.uuid4())[:8] + car_name = f"car-{short_id}" + car_node = await Node.init(db=self.db, schema=car_schema, branch=default_branch) + + await car_node.new( + db=self.db, + name=car_name, + nbr_seats=self.nb_cars_loaded + i, + owner=self.persons[self.nb_cars_loaded + i][1], + ) + + async with self.db.start_session(): + await car_node.save(db=self.db) + + self.nb_cars_loaded += nb_elements + + +class CarAndPersonIsolatedGenerator(DataGenerator): + def __init__(self, db: InfrahubDatabaseProfiler) -> None: + super().__init__(db) + self.car_generator: CarGenerator = CarGenerator(db) + self.person_generator: PersonGenerator = PersonGenerator(db) + + async def load_data(self, nb_elements: int) -> None: + """ + Load not connected cars and persons. Note that 'nb_elements' cars plus 'nb_elements' persons are loaded. + """ + + await self.car_generator.load_cars(nb_cars=nb_elements) + await self.person_generator.load_persons(nb_persons=nb_elements) + + +class CarAndPersonConnectedGenerator(DataGenerator): + def __init__(self, db: InfrahubDatabaseProfiler) -> None: + super().__init__(db) + self.car_generator: CarGenerator = CarGenerator(db) + self.person_generator: PersonGenerator = PersonGenerator(db) + + async def load_data(self, nb_elements: int) -> None: + """ + Load connected cars and persons. Note that 'nb_elements' cars plus 'nb_elements' persons are loaded. + """ + + persons = await self.person_generator.load_persons(nb_persons=nb_elements) + await self.car_generator.load_cars(nb_cars=nb_elements, persons=persons) diff --git a/backend/tests/helpers/query_benchmark/data_generator.py b/backend/tests/helpers/query_benchmark/data_generator.py new file mode 100644 index 0000000000..413d20e5af --- /dev/null +++ b/backend/tests/helpers/query_benchmark/data_generator.py @@ -0,0 +1,82 @@ +from abc import abstractmethod +from pathlib import Path +from typing import Callable, Optional + +from rich.console import Console +from rich.progress import Progress + +from tests.helpers.query_benchmark.db_query_profiler import ( + GraphProfileGenerator, + InfrahubDatabaseProfiler, +) + + +class DataGenerator: + """ + Abstract class responsible for loading data into a given database. + """ + + def __init__(self, db: InfrahubDatabaseProfiler) -> None: + self.db = db + + async def init(self) -> None: + """ + Any previous step before loading and profiling data should be implemented here. + """ + + @abstractmethod + async def load_data(self, nb_elements: int) -> None: + raise NotImplementedError("Abstract method") + + +async def load_data_and_profile( + data_generator: DataGenerator, + nb_elements: int, + func_call: Callable, + profile_frequency: int, + graphs_output_location: Path, + test_label: str, + graph_generator: GraphProfileGenerator, + memory_profiling_rate: Optional[int] = None, +) -> None: + """ + Loads data using the provided data generator, profiles the execution at specified loading intervals, + and generate profiling graphs. + + Args: + data_generator (DataGenerator): Object responsible for loading data. + nb_elements (int): The number of elements to generate by DataGenerator. + func_call (FuncCall): Contains function to profile and its arguments. + profile_frequency (int): The frequency, in terms of number of elements, at which function to profile will be executed and profiled. + graphs_output_location (Path): Path to the directory where profiling graphs will be saved. + test_label (str): A label or identifier for the test, used to name or organize the outputs. + memory_profiling_rate (int): Indicates at which rate memory should be profiled. Frequency is related to number of function execution times, + not to the number of elements as for 'profile_frequency'. For instance, memory_profiling_rate=25 + means memory is profiled every 25 times a function is executed/profiled. + """ + + await data_generator.init() + + q, r = divmod(nb_elements, profile_frequency) + nb_elem_per_batch = [profile_frequency] * q + ([r] if r else []) + + db_profiling_queries = data_generator.db + + with Progress(console=Console(force_terminal=True)) as progress: # Need force_terminal to display with pytest + task = progress.add_task( + f"Loading elements from {data_generator.__class__.__name__}", total=len(nb_elem_per_batch) + ) + + for i, nb_elem_to_load in enumerate(nb_elem_per_batch): + await data_generator.load_data(nb_elements=nb_elem_to_load) + db_profiling_queries.increase_nb_elements_loaded(nb_elem_to_load) + profile_memory = i % memory_profiling_rate == 0 if memory_profiling_rate is not None else False + with db_profiling_queries.profile(profile_memory): + await func_call() + progress.advance(task) + + # Remove first measurements as queries when there is no data seem always extreme + measurements = [m for m in db_profiling_queries.measurements if m.nb_elements_loaded != 0] + graph_generator.create_graphs( + measurements=measurements, output_location=graphs_output_location, label=test_label + ) diff --git a/backend/tests/helpers/query_benchmark/db_query_profiler.py b/backend/tests/helpers/query_benchmark/db_query_profiler.py new file mode 100644 index 0000000000..a8bb07b516 --- /dev/null +++ b/backend/tests/helpers/query_benchmark/db_query_profiler.py @@ -0,0 +1,175 @@ +import time +from dataclasses import dataclass +from pathlib import Path +from types import TracebackType +from typing import Any, List, Optional, Self, Type + +import matplotlib.pyplot as plt +import pandas as pd +from neo4j import Record + +from infrahub.config import SETTINGS + +# pylint: skip-file +from infrahub.database import InfrahubDatabase +from infrahub.database.constants import Neo4jRuntime +from infrahub.log import get_logger +from tests.helpers.constants import NEO4J_ENTERPRISE_IMAGE + +log = get_logger() + + +@dataclass +class BenchmarkConfig: + neo4j_image: str = NEO4J_ENTERPRISE_IMAGE + neo4j_runtime: Neo4jRuntime = Neo4jRuntime.DEFAULT + load_db_indexes: bool = False + + def __str__(self) -> str: + return f"{self.neo4j_image=} ; runtime: {self.neo4j_runtime} ; indexes: {self.load_db_indexes}" + + +@dataclass +class QueryMeasurement: + duration: float + query_name: str + start_time: float + nb_elements_loaded: Optional[int] = None + memory: Optional[float] = None + + +class GraphProfileGenerator: + def build_df_from_measuremenst(self, measurements: list[QueryMeasurement]) -> pd.DataFrame: + data = {} + for item in QueryMeasurement.__dataclass_fields__.keys(): + data[item] = [getattr(m, item) for m in measurements] + + return pd.DataFrame(data) + + def create_graphs(self, measurements: List[QueryMeasurement], output_location: Path, label: str) -> None: + df = self.build_df_from_measuremenst(measurements) + query_names = set(df["query_name"].tolist()) + + if not output_location.exists(): + output_location.mkdir(parents=True) + + for query_name in query_names: + self.create_duration_graph(df=df, query_name=query_name, label=label, output_dir=output_location) + # self.create_memory_graph(query_name=query_name, label=label, output_dir=output_location) + + def create_duration_graph(self, df: pd.DataFrame, query_name: str, label: str, output_dir: Path) -> None: + metric = "duration" + + name = f"{query_name}_{metric}" + plt.figure(name) + + df_query = df[(df["query_name"] == query_name) & (df["memory"].isna())].sort_values( + by="start_time", ascending=True + ) + x = df_query["nb_elements_loaded"].values + y = df_query[metric].values * 1000 + plt.plot(x, y, label=label) + + plt.legend(bbox_to_anchor=(1.04, 1), borderaxespad=0) + + plt.ylabel("msec", fontsize=15) + plt.title(f"Query - {query_name} | {metric}", fontsize=20) + plt.grid() + + file_name = f"{name}.png" + plt.savefig(str(output_dir / file_name), bbox_inches="tight") + + +class InfrahubDatabaseProfiler(InfrahubDatabase): + profiling_enabled: bool + profile_memory: bool + measurements: List[QueryMeasurement] + nb_elements_loaded: int + + def __init__( + self, + profiling_enabled: bool = False, + profile_memory: bool = False, + measurements: Optional[List[QueryMeasurement]] = None, + nb_elements_loaded: int = 0, + **kwargs: Any, + ) -> None: # todo args in constructor only because of __class__ pattern + super().__init__(**kwargs) + self.profiling_enabled = profiling_enabled + self.profile_memory = profile_memory + self.measurements = measurements if measurements is not None else [] + self.nb_elements_loaded = nb_elements_loaded + # Note that any attribute added here should be added to get_context method. + + def get_context(self) -> dict[str, Any]: + ctx = super().get_context() + ctx["profiling_enabled"] = self.profiling_enabled + ctx["profile_memory"] = self.profile_memory + ctx["measurements"] = self.measurements + ctx["nb_elements_loaded"] = self.nb_elements_loaded + return ctx + + async def execute_query_with_metadata( + self, + query: str, + params: dict[str, Any] | None = None, + name: str = "undefined", + context: dict[str, str] | None = None, + ) -> tuple[list[Record], dict[str, Any]]: + if not self.profiling_enabled: + # Profiling might be disabled to avoid capturing queries while loading data + return await super().execute_query_with_metadata(query, params, name) + + # We don't want to memory profile all queries + if self.profile_memory and name in self.queries_names_to_config: + # Following call to super().execute_query_with_metadata() will use this value to set PROFILE option + self.queries_names_to_config[name].profile_memory = True + profile_memory = True + else: + profile_memory = False + + assert profile_memory is False, "Do not profile memory for now" + + # Do the query and measure duration + time_start = time.time() + response, metadata = await super().execute_query_with_metadata(query, params, name) + duration_time = time.time() - time_start + + assert len(response) < SETTINGS.database.query_size_limit // 2, "make sure data return is small" + + measurement = QueryMeasurement( + duration=duration_time, + memory=metadata["profile"]["args"]["GlobalMemory"] if profile_memory else None, + query_name=str(name), + start_time=time_start, + nb_elements_loaded=self.nb_elements_loaded, + ) + self.measurements.append(measurement) + + return response, metadata + + def profile(self, profile_memory: bool) -> Self: + """ + This method allows to enable profiling of a InfrahubDatabaseProfiler instance + through a context manager with this syntax: + + `with db.profile(profile_memory=...): + # run code to profile + ` + """ + + self.profile_memory = profile_memory + return self + + def __enter__(self) -> None: + self.profiling_enabled = True + self.profile_memory = self.profile_memory + + def __exit__( + self, exc_type: Optional[Type[BaseException]], exc_val: Optional[BaseException], exc_tb: Optional[TracebackType] + ) -> None: + self.profiling_enabled = False + self.profile_memory = False + + def increase_nb_elements_loaded(self, nb_elements_loaded: int) -> None: + self.nb_elements_loaded += nb_elements_loaded diff --git a/backend/tests/helpers/schema/__init__.py b/backend/tests/helpers/schema/__init__.py index 0096e224d3..a066165800 100644 --- a/backend/tests/helpers/schema/__init__.py +++ b/backend/tests/helpers/schema/__init__.py @@ -9,6 +9,7 @@ from .manufacturer import MANUFACTURER from .person import PERSON from .ticket import TICKET +from .widget import WIDGET if TYPE_CHECKING: from infrahub.database import InfrahubDatabase @@ -17,14 +18,16 @@ CAR_SCHEMA = SchemaRoot(nodes=[CAR, MANUFACTURER, PERSON]) -async def load_schema(db: InfrahubDatabase, schema: SchemaRoot) -> None: +async def load_schema(db: InfrahubDatabase, schema: SchemaRoot, branch_name: str | None = None) -> None: default_branch_name = registry.default_branch - branch_schema = registry.schema.get_schema_branch(name=default_branch_name) + branch_schema = registry.schema.get_schema_branch(name=branch_name or default_branch_name) tmp_schema = branch_schema.duplicate() tmp_schema.load_schema(schema=schema) tmp_schema.process() - await registry.schema.update_schema_branch(schema=tmp_schema, db=db, branch=default_branch_name, update_db=True) + await registry.schema.update_schema_branch( + schema=tmp_schema, db=db, branch=branch_name or default_branch_name, update_db=True + ) -__all__ = ["CAR", "MANUFACTURER", "PERSON", "CAR_SCHEMA", "TICKET"] +__all__ = ["CAR", "CAR_SCHEMA", "MANUFACTURER", "PERSON", "TICKET", "WIDGET"] diff --git a/backend/tests/helpers/schema/widget.py b/backend/tests/helpers/schema/widget.py new file mode 100644 index 0000000000..82c2180ec5 --- /dev/null +++ b/backend/tests/helpers/schema/widget.py @@ -0,0 +1,16 @@ +from infrahub.core.schema import AttributeSchema, NodeSchema + +WIDGET = NodeSchema( + name="Widget", + namespace="Testing", + label="Widget", + default_filter="name__value", + order_by=["name__value"], + attributes=[ + AttributeSchema(name="name", kind="Text", optional=False), + AttributeSchema(name="description", kind="Text", optional=True), + AttributeSchema(name="height", kind="Number", optional=True), + AttributeSchema(name="weight", kind="Number", optional=True), + ], + inherit_from=["LineageOwner", "LineageSource"], +) diff --git a/backend/tests/helpers/test_app.py b/backend/tests/helpers/test_app.py index cb6cfe66b5..6dc9a6339e 100644 --- a/backend/tests/helpers/test_app.py +++ b/backend/tests/helpers/test_app.py @@ -2,7 +2,8 @@ from typing import Generator import pytest -from infrahub_sdk import UUIDT, Config, InfrahubClient +from infrahub_sdk import Config, InfrahubClient +from infrahub_sdk.uuidt import UUIDT from infrahub import config from infrahub.core import registry @@ -12,13 +13,18 @@ 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 import services +from infrahub.services.adapters.workflow.local import WorkflowLocalExecution from tests.adapters.message_bus import BusSimulator from .test_client import InfrahubTestClient @@ -54,12 +60,20 @@ def api_token(self) -> str: @pytest.fixture(scope="class") def bus_simulator(self, db: InfrahubDatabase) -> Generator[BusSimulator, None, None]: - bus = BusSimulator(database=db) + bus = BusSimulator(database=db, workflow=WorkflowLocalExecution()) original = config.OVERRIDE.message_bus config.OVERRIDE.message_bus = bus 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,9 +94,11 @@ 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: + # This call emits an ERROR because it calls registry-webhook-config-refresh flow within a local worker + # while services.service.client is not set. There might be a design issue here: a client is needed while + # the app is being initialized. await app_initialization(app) return InfrahubTestClient(app=app) @@ -98,17 +114,27 @@ async def client( bus_simulator.service._client = sdk_client - return sdk_client + # Some tests rely on infrahub worker which runs locally during testing. Thus, code supposed to run + # on worker rely on server's `services.service`, which is not initialized with a client, + # instead of the worker one. Thus, we temporarily set `services.service.client` + # here to mock worker's `services.service`. + assert isinstance( + services.service.workflow, WorkflowLocalExecution + ), "These tests are currently meant to run with a local worker" + original_service_client = services.service._client + services.service.set_client(sdk_client) + yield sdk_client + services.service.set_client(original_service_client) @pytest.fixture(scope="class") 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]) + # This call emits a warning related to the fact database index manager has not been initialized. 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/helpers/test_worker.py b/backend/tests/helpers/test_worker.py new file mode 100644 index 0000000000..7f019480f5 --- /dev/null +++ b/backend/tests/helpers/test_worker.py @@ -0,0 +1,141 @@ +import asyncio +from contextlib import ExitStack +from typing import Any, Generator +from uuid import UUID + +import pytest +from infrahub_sdk import InfrahubClient +from prefect import settings as prefect_settings +from prefect.client.orchestration import PrefectClient +from prefect.client.schemas.actions import WorkPoolCreate +from prefect.client.schemas.filters import WorkPoolFilter, WorkPoolFilterId +from prefect.client.schemas.objects import FlowRun, StateType, WorkPool +from prefect.workers.base import BaseWorkerResult +from testcontainers.core.container import DockerContainer +from testcontainers.core.waiting_utils import wait_for_logs + +from infrahub.tasks.dummy import DUMMY_FLOW, DUMMY_FLOW_BROKEN +from infrahub.workers.infrahub_async import ( + InfrahubWorkerAsync, +) +from infrahub.workflows.catalogue import INFRAHUB_WORKER_POOL +from infrahub.workflows.initialization import setup_blocks +from infrahub.workflows.models import WorkerPoolDefinition +from tests.helpers.constants import ( + INFRAHUB_USE_TEST_CONTAINERS, + PORT_PREFECT, +) +from tests.helpers.test_app import TestInfrahubApp +from tests.helpers.utils import get_exposed_port + + +class TestWorkerInfrahubAsync(TestInfrahubApp): + @classmethod + async def wait_for_flow( + cls, client: PrefectClient, work_pool_id: UUID, interval: int = 1, timeout: int = 10 + ) -> FlowRun: + while timeout: + flows = await client.read_flow_runs( + work_pool_filter=WorkPoolFilter(id=WorkPoolFilterId(any_=[work_pool_id])) + ) + + scheduled_flows = [flow for flow in flows if flow.state_type in [StateType.SCHEDULED]] + if scheduled_flows: + return scheduled_flows[0] + + timeout = -interval + await asyncio.sleep(interval) + + raise TimeoutError + + @classmethod + async def worker_run_flow( + cls, worker: InfrahubWorkerAsync, client: PrefectClient, flow: FlowRun + ) -> BaseWorkerResult: + assert flow.deployment_id + deployment = await client.read_deployment(deployment_id=flow.deployment_id) + flow_config = await worker._get_configuration(flow_run=flow, deployment=deployment) + + return await worker.run( + flow_run=flow, + configuration=flow_config, + ) + + @pytest.fixture(scope="class") + def prefect_container( + self, request: pytest.FixtureRequest, load_settings_before_session: Any + ) -> dict[int, int] | None: + 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() -> None: + 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="class") + def prefect_server( + self, prefect_container: dict[int, int] | None, reload_settings_before_each_module: Any + ) -> 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="class") + async def prefect_client(self, prefect_server: str) -> PrefectClient: + return PrefectClient(api=prefect_server) + + @pytest.fixture(scope="class") + async def work_pool(self, prefect_client: PrefectClient) -> WorkPool: + wp = WorkPoolCreate( + name=INFRAHUB_WORKER_POOL.name, + type=InfrahubWorkerAsync.type, + description=INFRAHUB_WORKER_POOL.name, + ) + return await prefect_client.create_work_pool(work_pool=wp, overwrite=True) + + @pytest.fixture(scope="class") + async def block_storage(self, redis: dict[int, int] | None, prefect_client: PrefectClient) -> None: + await setup_blocks() + + @pytest.fixture(scope="class") + async def dummy_flows_deployment(self, work_pool: WorkerPoolDefinition, prefect_client: PrefectClient) -> None: + for flow in [DUMMY_FLOW, DUMMY_FLOW_BROKEN]: + await flow.save(client=prefect_client, work_pool=INFRAHUB_WORKER_POOL) + + @pytest.fixture(scope="class") + async def prefect_worker( + self, client: InfrahubClient, block_storage: Any, prefect_client: PrefectClient, work_pool: WorkPool + ) -> InfrahubWorkerAsync: + worker = InfrahubWorkerAsync(work_pool_name=work_pool.name) + + await worker.setup(client=client, metric_port=0) + await worker.sync_with_backend() + + # Validate that the worker has properly registered with the server + active_workers = await prefect_client.read_workers_for_work_pool(work_pool_name=work_pool.name) + assert active_workers[0].name == worker.name + + return worker diff --git a/backend/tests/helpers/utils.py b/backend/tests/helpers/utils.py new file mode 100644 index 0000000000..c5ba03e6af --- /dev/null +++ b/backend/tests/helpers/utils.py @@ -0,0 +1,31 @@ +from testcontainers.core.container import DockerContainer +from testcontainers.core.waiting_utils import wait_for_logs + +from tests.helpers.constants import PORT_BOLT_NEO4J, PORT_HTTP_NEO4J + + +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)) + + +def start_neo4j_container(neo4j_image: str) -> DockerContainer: + container = ( + DockerContainer(image=neo4j_image) + .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) + .with_exposed_ports(PORT_HTTP_NEO4J) + ) + + container.start() + wait_for_logs(container, "Started.") # wait_container_is_ready does not seem to be enough + return container diff --git a/backend/tests/integration/conftest.py b/backend/tests/integration/conftest.py index b7dec10a9e..258cabb819 100644 --- a/backend/tests/integration/conftest.py +++ b/backend/tests/integration/conftest.py @@ -1,12 +1,15 @@ 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 infrahub_sdk.uuidt import UUIDT +from prefect.testing.utilities import prefect_test_harness +from pytest import TempPathFactory +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 +17,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 +27,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 +42,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 +90,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 +102,29 @@ 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(scope="session") +def git_sources_dir(tmp_path_factory: TempPathFactory) -> Path: + return tmp_path_factory.mktemp("sources") + + +@pytest.fixture(scope="session") +def git_repos_dir(tmp_path_factory: TempPathFactory) -> Path: + repos_dir = tmp_path_factory.mktemp("repositories") + config.SETTINGS.git.repositories_directory = str(repos_dir) + return repos_dir + + +@pytest.fixture(scope="session") +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(scope="session") +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/diff/test_diff_incremental_addition.py b/backend/tests/integration/diff/test_diff_incremental_addition.py index b5acbc221a..6a6b471156 100644 --- a/backend/tests/integration/diff/test_diff_incremental_addition.py +++ b/backend/tests/integration/diff/test_diff_incremental_addition.py @@ -340,7 +340,7 @@ async def test_add_new_peer_on_main( full_diff = await diff_coordinator.create_or_update_arbitrary_timeframe_diff( base_branch=default_branch, diff_branch=diff_branch, - from_time=Timestamp(diff_branch.created_at), + from_time=Timestamp(diff_branch.branched_from), to_time=Timestamp(), name=str(uuid4), ) @@ -413,7 +413,7 @@ async def test_update_previous_owner_protected_on_branch( full_diff = await diff_coordinator.create_or_update_arbitrary_timeframe_diff( base_branch=default_branch, diff_branch=diff_branch, - from_time=Timestamp(diff_branch.created_at), + from_time=Timestamp(diff_branch.branched_from), to_time=Timestamp(), name=str(uuid4), ) @@ -492,7 +492,7 @@ async def test_remove_previous_owner_on_branch( full_diff = await diff_coordinator.create_or_update_arbitrary_timeframe_diff( base_branch=default_branch, diff_branch=diff_branch, - from_time=Timestamp(diff_branch.created_at), + from_time=Timestamp(diff_branch.branched_from), to_time=Timestamp(), name=str(uuid4), ) @@ -562,7 +562,7 @@ async def test_remove_previous_owner_on_main_again( full_diff = await diff_coordinator.create_or_update_arbitrary_timeframe_diff( base_branch=default_branch, diff_branch=diff_branch, - from_time=Timestamp(diff_branch.created_at), + from_time=Timestamp(diff_branch.branched_from), to_time=Timestamp(), name=str(uuid4), ) diff --git a/backend/tests/integration/diff/test_diff_merge.py b/backend/tests/integration/diff/test_diff_merge.py new file mode 100644 index 0000000000..44422e3249 --- /dev/null +++ b/backend/tests/integration/diff/test_diff_merge.py @@ -0,0 +1,132 @@ +import pytest +from infrahub_sdk import InfrahubClient + +from infrahub.core.branch import Branch +from infrahub.core.diff.coordinator import DiffCoordinator +from infrahub.core.diff.merger.merger import DiffMerger +from infrahub.core.diff.model.path import ConflictSelection +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.dependencies.registry import get_component_registry +from infrahub.services.adapters.cache.redis import RedisCache +from tests.adapters.message_bus import BusSimulator +from tests.constants import TestKind +from tests.helpers.schema import CAR_SCHEMA, load_schema +from tests.helpers.test_app import TestInfrahubApp + +BRANCH_NAME = "this-branch" +PERSON_KIND = "TestingPerson" + + +class TestDiffMerge(TestInfrahubApp): + @pytest.fixture(scope="class") + async def initial_dataset( + self, + db: InfrahubDatabase, + default_branch, + client: InfrahubClient, + bus_simulator: BusSimulator, + ) -> dict[str, Node]: + await load_schema(db, schema=CAR_SCHEMA) + doc_brown = await Node.init(schema=TestKind.PERSON, db=db) + await doc_brown.new(db=db, name="Doc Brown", height=175) + await doc_brown.save(db=db) + marty = await Node.init(schema=TestKind.PERSON, db=db) + await marty.new(db=db, name="Marty McFly", height=155) + await marty.save(db=db) + biff = await Node.init(schema=TestKind.PERSON, db=db) + await biff.new(db=db, name="Biff... something", height=177) + await biff.save(db=db) + dmc = await Node.init(schema=TestKind.MANUFACTURER, db=db) + await dmc.new(db=db, name="DMC") + await dmc.save(db=db) + delorean = await Node.init(schema=TestKind.CAR, db=db) + await delorean.new( + db=db, + name="Delorean", + color="Silver", + description="time-travelling coupe", + owner=doc_brown, + manufacturer=dmc, + ) + await delorean.save(db=db) + + bus_simulator.service.cache = RedisCache() + + return { + "doc_brown": doc_brown, + "marty": marty, + "biff": biff, + "dmc": dmc, + "delorean": delorean, + } + + @pytest.fixture(scope="class") + async def diff_branch(self, db: InfrahubDatabase, initial_dataset) -> Branch: + return await create_branch(db=db, branch_name=BRANCH_NAME) + + @pytest.fixture(scope="class") + async def diff_repository(self, db: InfrahubDatabase, default_branch: Branch) -> DiffRepository: + component_registry = get_component_registry() + return await component_registry.get_component(DiffRepository, db=db, branch=default_branch) + + @pytest.fixture(scope="class") + async def diff_coordinator(self, db: InfrahubDatabase, default_branch: Branch) -> DiffCoordinator: + component_registry = get_component_registry() + return await component_registry.get_component(DiffCoordinator, db=db, branch=default_branch) + + async def _get_diff_merger(self, db: InfrahubDatabase, diff_branch: Branch) -> DiffMerger: + component_registry = get_component_registry() + return await component_registry.get_component(DiffMerger, db=db, branch=diff_branch) + + @pytest.fixture(scope="class") + async def data_01_update_owner_conflict_select_base( + self, + db: InfrahubDatabase, + initial_dataset, + default_branch: Branch, + diff_branch: Branch, + ) -> None: + delorean_id = initial_dataset["delorean"].get_id() + marty_id = initial_dataset["marty"].get_id() + biff_id = initial_dataset["biff"].get_id() + + delorean_main = await NodeManager.get_one(db=db, branch=default_branch, id=delorean_id) + await delorean_main.owner.update(db=db, data=marty_id) + await delorean_main.save(db=db) + + delorean_branch = await NodeManager.get_one(db=db, branch=diff_branch, id=delorean_id) + await delorean_branch.owner.update(db=db, data=biff_id) + await delorean_branch.save(db=db) + + async def test_select_cardinality_one_resolution_and_merge( + self, + db: InfrahubDatabase, + initial_dataset, + data_01_update_owner_conflict_select_base, + default_branch: Branch, + diff_branch: Branch, + diff_coordinator: DiffCoordinator, + diff_repository: DiffRepository, + ): + delorean_id = initial_dataset["delorean"].get_id() + marty_id = initial_dataset["marty"].get_id() + + enriched_diff = await diff_coordinator.update_branch_diff(base_branch=default_branch, diff_branch=diff_branch) + conflicts_map = enriched_diff.get_all_conflicts() + assert len(conflicts_map) == 1 + owner_conflict = list(conflicts_map.values())[0] + await diff_repository.update_conflict_by_id( + conflict_id=owner_conflict.uuid, selection=ConflictSelection.BASE_BRANCH + ) + right_now = Timestamp() + diff_merger = await self._get_diff_merger(db=db, diff_branch=diff_branch) + await diff_merger.merge_graph(at=right_now) + + delorean_main = await NodeManager.get_one(db=db, branch=default_branch, id=delorean_id) + owner_peer = await delorean_main.owner.get_peer(db=db) + assert owner_peer.get_id() == marty_id diff --git a/backend/tests/integration/diff/test_diff_rebase.py b/backend/tests/integration/diff/test_diff_rebase.py index 312c7a365e..411e1c65b1 100644 --- a/backend/tests/integration/diff/test_diff_rebase.py +++ b/backend/tests/integration/diff/test_diff_rebase.py @@ -7,7 +7,7 @@ from infrahub import config, lock from infrahub.core.constants import DiffAction, InfrahubKind from infrahub.core.constants.database import DatabaseEdgeType -from infrahub.core.diff.model.path import BranchTrackingId, ConflictSelection +from infrahub.core.diff.model.path import BranchTrackingId from infrahub.core.diff.repository.repository import DiffRepository from infrahub.core.initialization import create_branch from infrahub.core.manager import NodeManager @@ -297,6 +297,7 @@ async def test_merge_causes_diff_update( cyberdyne_id = initial_dataset["cyberdyne"].id omnicorp_id = initial_dataset["omnicorp"].id before_merge = Timestamp() + result = await client.execute_graphql(query=BRANCH_MERGE, variables={"branch": branch_1.name}) assert result["BranchMerge"]["ok"] @@ -370,41 +371,22 @@ async def test_merge_causes_diff_update( assert prop_diff.conflict is None async def test_resolve_conflict( - self, client: InfrahubClient, branch_2: Branch, diff_repository: DiffRepository, initial_dataset + self, + db: InfrahubDatabase, + branch_2: Branch, + initial_dataset, ): + kara_id = initial_dataset["kara"].id + jesko_id = initial_dataset["jesko"].id cyberdyne_id = initial_dataset["cyberdyne"].id - branch_2_diff = await diff_repository.get_one( - diff_branch_name=branch_2.name, tracking_id=BranchTrackingId(name=branch_2.name) - ) - conflicts = branch_2_diff.get_all_conflicts() - attr_conflict = None - peer_conflict = None - for conflict in conflicts: - if conflict.base_branch_value == "branch-1-description": - attr_conflict = conflict - elif conflict.base_branch_value == cyberdyne_id: - peer_conflict = conflict - assert attr_conflict - assert peer_conflict - - result = await client.execute_graphql( - query=CONFLICT_SELECTION_QUERY, - variables={"conflict_id": attr_conflict.uuid, "selected_branch": ConflictSelection.DIFF_BRANCH.name}, - ) - assert result["ResolveDiffConflict"]["ok"] - result = await client.execute_graphql( - query=CONFLICT_SELECTION_QUERY, - variables={"conflict_id": peer_conflict.uuid, "selected_branch": ConflictSelection.BASE_BRANCH.name}, - ) - assert result["ResolveDiffConflict"]["ok"] - branch_2_diff = await diff_repository.get_one( - diff_branch_name=branch_2.name, tracking_id=BranchTrackingId(name=branch_2.name) - ) - updated_conflicts = branch_2_diff.get_all_conflicts() - conflicts_by_id = {c.uuid: c for c in updated_conflicts} - assert conflicts_by_id[attr_conflict.uuid].selected_branch is ConflictSelection.DIFF_BRANCH - assert conflicts_by_id[peer_conflict.uuid].selected_branch is ConflictSelection.BASE_BRANCH + kara_main = await NodeManager.get_one(db=db, id=kara_id) + kara_main.description.value = "branch-2-description" + await kara_main.save(db=db) + + jesko_branch = await NodeManager.get_one(db=db, branch=branch_2, id=jesko_id) + await jesko_branch.manufacturer.update(db=db, data=cyberdyne_id) + await jesko_branch.save(db=db) async def test_rebase_causes_diff_recalculation( self, @@ -414,11 +396,11 @@ async def test_rebase_causes_diff_recalculation( branch_2: Branch, diff_repository: DiffRepository, ): - kara_id = initial_dataset["kara"].id jesko_id = initial_dataset["jesko"].id koenigsegg_id = initial_dataset["koenigsegg"].id - omnicorp_id = initial_dataset["omnicorp"].id + cyberdyne_id = initial_dataset["cyberdyne"].id before_rebase = Timestamp() + result = await client.execute_graphql(query=BRANCH_REBASE, variables={"branch": branch_2.name}) assert result["BranchRebase"]["ok"] @@ -426,26 +408,17 @@ async def test_rebase_causes_diff_recalculation( diff_branch_name=branch_2.name, tracking_id=BranchTrackingId(name=branch_2.name) ) - assert len(branch_2_diff.nodes) == 4 + assert len(branch_2_diff.nodes) == 3 assert branch_2_diff.to_time > before_rebase nodes_by_id = {n.uuid: n for n in branch_2_diff.nodes} - kara_node = nodes_by_id[kara_id] - assert len(kara_node.attributes) == 1 - description_attr = kara_node.attributes.pop() - assert description_attr.name == "description" - assert len(description_attr.properties) == 1 - value_prop = description_attr.properties.pop() - assert value_prop.property_type is DatabaseEdgeType.HAS_VALUE - assert value_prop.previous_value == "branch-1-description" - assert value_prop.new_value == "branch-2-description" - assert value_prop.conflict is None + assert set(nodes_by_id.keys()) == {jesko_id, cyberdyne_id, koenigsegg_id} jesko_node = nodes_by_id[jesko_id] assert len(jesko_node.relationships) == 1 manufacturer_rel = jesko_node.relationships.pop() assert manufacturer_rel.name == "manufacturer" assert len(manufacturer_rel.relationships) == 1 manufacturer_element = manufacturer_rel.relationships.pop() - assert manufacturer_element.peer_id == omnicorp_id + assert manufacturer_element.peer_id == cyberdyne_id assert manufacturer_element.action is DiffAction.UPDATED assert manufacturer_element.conflict is None assert len(manufacturer_element.properties) == 1 @@ -453,9 +426,9 @@ async def test_rebase_causes_diff_recalculation( assert related_prop.property_type is DatabaseEdgeType.IS_RELATED assert related_prop.action is DiffAction.UPDATED assert related_prop.previous_value == koenigsegg_id - assert related_prop.new_value == omnicorp_id + assert related_prop.new_value == cyberdyne_id assert related_prop.conflict is None - for manufacturer_id, expected_action in ((koenigsegg_id, DiffAction.REMOVED), (omnicorp_id, DiffAction.ADDED)): + for manufacturer_id, expected_action in ((koenigsegg_id, DiffAction.REMOVED), (cyberdyne_id, DiffAction.ADDED)): manufacturer_node = nodes_by_id[manufacturer_id] assert len(manufacturer_node.relationships) == 1 cars_rel = manufacturer_node.relationships.pop() 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..71188c1cd6 100644 --- a/backend/tests/integration/git/test_git_repository.py +++ b/backend/tests/integration/git/test_git_repository.py @@ -3,8 +3,10 @@ import pytest import yaml -from infrahub_sdk import Config, InfrahubClient, NodeNotFoundError +from infrahub_sdk import Config, InfrahubClient +from infrahub_sdk.exceptions import 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 +16,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 +46,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 +64,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/git/test_readonly_repository.py b/backend/tests/integration/git/test_readonly_repository.py index 154050a657..b700dbf287 100644 --- a/backend/tests/integration/git/test_readonly_repository.py +++ b/backend/tests/integration/git/test_readonly_repository.py @@ -8,9 +8,10 @@ from infrahub.core.constants import InfrahubKind from infrahub.core.manager import NodeManager from infrahub.core.node import Node +from infrahub.git.models import RequestArtifactDefinitionGenerate from infrahub.lock import InfrahubLockRegistry -from infrahub.message_bus.messages import RequestArtifactDefinitionGenerate -from infrahub.services import InfrahubServices +from infrahub.services import services +from infrahub.workflows.catalogue import REQUEST_ARTIFACT_DEFINITION_GENERATE from tests.constants import TestKind from tests.helpers.file_repo import FileRepo from tests.helpers.schema import CAR_SCHEMA, load_schema @@ -83,12 +84,21 @@ async def test_step01_create_repository( assert repository.commit.value assert check_definition.file_path.value == "checks/car_overview.py" - async def test_step02_validate_generated_artifacts(self, db: InfrahubDatabase, client: InfrahubClient): + async def test_step02_validate_generated_artifacts( + self, + db: InfrahubDatabase, + client: InfrahubClient, + ): artifacts = await client.all(kind=InfrahubKind.ARTIFACT, branch="ro_repository") assert artifacts assert artifacts[0].name.value == "Ownership report" - async def test_step03_merge_branch(self, db: InfrahubDatabase, client: InfrahubClient, helper: TestHelper): + async def test_step03_merge_branch( + self, + db: InfrahubDatabase, + client: InfrahubClient, + helper: TestHelper, + ): await client.branch.merge(branch_name="ro_repository") check_definition: CoreCheckDefinition = await NodeManager.get_one_by_id_or_default_filter( @@ -96,14 +106,12 @@ async def test_step03_merge_branch(self, db: InfrahubDatabase, client: InfrahubC ) assert check_definition.file_path.value == "checks/car_overview.py" - bus_simulator = helper.get_message_bus_simulator() - service = InfrahubServices(client=client, message_bus=bus_simulator) - bus_simulator.service = service - artifact_definitions = await client.all(kind=InfrahubKind.ARTIFACTDEFINITION) + for artifact_definition in artifact_definitions: - await service.send( - message=RequestArtifactDefinitionGenerate(artifact_definition=artifact_definition.id, branch="main") + model = RequestArtifactDefinitionGenerate(artifact_definition=artifact_definition.id, branch="main") + await services.service.workflow.submit_workflow( + REQUEST_ARTIFACT_DEFINITION_GENERATE, parameters={"model": model} ) artifacts = await client.all(kind=InfrahubKind.ARTIFACT) 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_merge_reconcile.py b/backend/tests/integration/ipam/test_ipam_merge_reconcile.py index c6738583ca..59497dd2d0 100644 --- a/backend/tests/integration/ipam/test_ipam_merge_reconcile.py +++ b/backend/tests/integration/ipam/test_ipam_merge_reconcile.py @@ -40,7 +40,12 @@ async def branch_2(self, db: InfrahubDatabase): return await create_branch(db=db, branch_name="delete_prefix") async def test_step01_add_address( - self, db: InfrahubDatabase, initial_dataset, client: InfrahubClient, branch_1, new_address_1 + self, + db: InfrahubDatabase, + initial_dataset, + client: InfrahubClient, + branch_1, + new_address_1, ) -> None: success = await client.branch.merge(branch_name=branch_1.name) assert success is True @@ -51,7 +56,12 @@ async def test_step01_add_address( assert parent_rels[0].peer_id == initial_dataset["net140"].id async def test_step02_add_delete_prefix( - self, db: InfrahubDatabase, initial_dataset, client: InfrahubClient, branch_2, new_address_1 + self, + db: InfrahubDatabase, + initial_dataset, + client: InfrahubClient, + branch_2, + new_address_1, ) -> None: prefix_schema = registry.schema.get_node_schema(name="IpamIPPrefix", branch=branch_2) new_prefix = await Node.init(schema=prefix_schema, db=db, branch=registry.default_branch) diff --git a/backend/tests/integration/ipam/test_ipam_utilization.py b/backend/tests/integration/ipam/test_ipam_utilization.py index 0bdc68a057..9ff0257a27 100644 --- a/backend/tests/integration/ipam/test_ipam_utilization.py +++ b/backend/tests/integration/ipam/test_ipam_utilization.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Union +from typing import TYPE_CHECKING import pytest from graphql import graphql @@ -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: @@ -63,7 +63,7 @@ async def initial_dataset( db: InfrahubDatabase, initialize_registry: None, register_ipam_schema, - ) -> dict[str, Union[Node, list[Node]]]: + ) -> dict[str, Node | list[Node]]: await create_ipam_namespace(db=db) default_ipnamespace = await get_default_ipnamespace(db=db) default_branch = registry.default_branch @@ -119,9 +119,7 @@ async def branch2(self, db: InfrahubDatabase) -> Branch: return await create_branch(db=db, branch_name="branch2") @pytest.fixture(scope="class") - async def step_02_dataset( - self, db: InfrahubDatabase, initial_dataset, branch2 - ) -> dict[str, Union[Node, list[Node]]]: + async def step_02_dataset(self, db: InfrahubDatabase, initial_dataset, branch2) -> dict[str, Node | list[Node]]: prefix_schema = registry.schema.get_node_schema(name="IpamIPPrefix", branch=branch2) address_schema = registry.schema.get_node_schema(name="IpamIPAddress", branch=branch2) container = initial_dataset["container"] diff --git a/backend/tests/integration/ipam/test_proposed_change_reconcile.py b/backend/tests/integration/ipam/test_proposed_change_reconcile.py index aa8d8f3fc6..0aac243a5a 100644 --- a/backend/tests/integration/ipam/test_proposed_change_reconcile.py +++ b/backend/tests/integration/ipam/test_proposed_change_reconcile.py @@ -51,7 +51,12 @@ async def branch_2(self, db: InfrahubDatabase): return await create_branch(db=db, branch_name="delete_prefix") async def test_step01_add_address( - self, db: InfrahubDatabase, initial_dataset, client: InfrahubClient, branch_1, new_address_1 + self, + db: InfrahubDatabase, + initial_dataset, + client: InfrahubClient, + branch_1, + new_address_1, ) -> None: proposed_change_create = await client.create( kind=InfrahubKind.PROPOSEDCHANGE, @@ -67,7 +72,12 @@ async def test_step01_add_address( assert parent_rels[0].peer_id == initial_dataset["net140"].id async def test_step02_add_delete_prefix( - self, db: InfrahubDatabase, initial_dataset, client: InfrahubClient, branch_2, new_address_1 + self, + db: InfrahubDatabase, + initial_dataset, + client: InfrahubClient, + branch_2, + new_address_1, ) -> None: proposed_change_create = await client.create( kind=InfrahubKind.PROPOSEDCHANGE, 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 e8264f91ab..05d8a39841 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/proposed_change/test_proposed_change_repository.py b/backend/tests/integration/proposed_change/test_proposed_change_repository.py index 592deb5168..4bfd6c8cad 100644 --- a/backend/tests/integration/proposed_change/test_proposed_change_repository.py +++ b/backend/tests/integration/proposed_change/test_proposed_change_repository.py @@ -72,7 +72,10 @@ async def initial_dataset( await richard.save(db=db) async def test_create_proposed_change( - self, db: InfrahubDatabase, initial_dataset: None, client: InfrahubClient + self, + db: InfrahubDatabase, + initial_dataset: None, + client: InfrahubClient, ) -> None: proposed_change_create = await client.create( kind=InfrahubKind.PROPOSEDCHANGE, diff --git a/backend/tests/integration/schema_lifecycle/shared.py b/backend/tests/integration/schema_lifecycle/shared.py index ca43eb61de..a5fc2fc843 100644 --- a/backend/tests/integration/schema_lifecycle/shared.py +++ b/backend/tests/integration/schema_lifecycle/shared.py @@ -1,3 +1,4 @@ +import copy from typing import Any, Dict import pytest @@ -34,13 +35,13 @@ def schema_person_02_first_last(self, schema_person_base) -> Dict[str, Any]: """Rename the attribute name to firstname and add a new lastname attribute.""" assert schema_person_base["attributes"][0]["name"] == "name" schema_person_base["attributes"][0]["name"] = "firstname" - schema_person_base["attributes"].append({"name": "lastname", "kind": "Text"}) + schema_person_base["attributes"].append({"name": "lastname", "kind": "Text", "optional": True}) return schema_person_base @pytest.fixture(scope="class") def schema_person_03_no_height(self, schema_person_02_first_last) -> Dict[str, Any]: """Remove the attribute height.""" - person = schema_person_02_first_last + person = copy.deepcopy(schema_person_02_first_last) assert person["attributes"][2]["name"] == "height" person["attributes"][2]["state"] = "absent" return person diff --git a/backend/tests/integration/schema_lifecycle/test_schema_attribute_remove_add.py b/backend/tests/integration/schema_lifecycle/test_schema_attribute_remove_add.py new file mode 100644 index 0000000000..07d14e3e0d --- /dev/null +++ b/backend/tests/integration/schema_lifecycle/test_schema_attribute_remove_add.py @@ -0,0 +1,211 @@ +from typing import Any, Optional + +import pytest +from infrahub_sdk import InfrahubClient + +from infrahub.core import registry +from infrahub.core.branch import Branch +from infrahub.core.node import Node +from infrahub.database import InfrahubDatabase +from infrahub.exceptions import InitializationError + +from ..shared import load_schema +from .shared import ( + CAR_KIND, + MANUFACTURER_KIND_01, + PERSON_KIND, + TAG_KIND, + TestSchemaLifecycleBase, +) + +# pylint: disable=unused-argument + + +class BranchState: + def __init__(self) -> None: + self._branch: Optional[Branch] = None + + @property + def branch(self) -> Branch: + if self._branch: + return self._branch + raise InitializationError + + @branch.setter + def branch(self, value: Branch) -> None: + self._branch = value + + +state = BranchState() + + +# --------------------------------- +# This test was initially written to troubleshoot and fix https://github.com/opsmill/infrahub/issues/4727 +# The issue was primarily happening in Main +# --------------------------------- +class TestSchemaLifecycleAttributeRemoveAddMain(TestSchemaLifecycleBase): + @property + def branch1(self) -> Branch: + return state.branch + + @pytest.fixture(scope="class") + async def initial_dataset(self, db: InfrahubDatabase, initialize_registry, schema_step01): + await load_schema(db=db, schema=schema_step01) + + # Load data in the MAIN branch first + john = await Node.init(schema=PERSON_KIND, db=db) + await john.new(db=db, firstname="John", lastname="Doe", height=175, description="The famous Joe Doe") + await john.save(db=db) + + renault = await Node.init(schema=MANUFACTURER_KIND_01, db=db) + await renault.new( + db=db, name="renault", description="Groupe Renault is a French multinational automobile manufacturer" + ) + await renault.save(db=db) + + megane = await Node.init(schema=CAR_KIND, db=db) + await megane.new( + db=db, name="Megane", description="Renault Megane", color="#c93420", manufacturer=renault, owner=john + ) + await megane.save(db=db) + + clio = await Node.init(schema=CAR_KIND, db=db) + await clio.new( + db=db, name="Clio", description="Renault Clio", color="#ff3420", manufacturer=renault, owner=john + ) + await clio.save(db=db) + + red = await Node.init(schema=TAG_KIND, db=db) + await red.new(db=db, name="red", persons=[john]) + await red.save(db=db) + + objs = { + "john": john.id, + "renault": renault.id, + "megane": megane.id, + "clio": clio.id, + "red": red.id, + } + + return objs + + @pytest.fixture(scope="class") + def schema_step01( + self, schema_car_base, schema_person_02_first_last, schema_manufacturer_base, schema_tag_base + ) -> dict[str, Any]: + return { + "version": "1.0", + "nodes": [schema_person_02_first_last, schema_car_base, schema_manufacturer_base, schema_tag_base], + } + + @pytest.fixture(scope="class") + def schema_step02( + self, schema_car_base, schema_person_03_no_height, schema_manufacturer_base, schema_tag_base + ) -> dict[str, Any]: + return { + "version": "1.0", + "nodes": [schema_person_03_no_height, schema_car_base, schema_manufacturer_base, schema_tag_base], + } + + @pytest.fixture(scope="class") + def schema_step03( + self, schema_car_base, schema_person_02_first_last, schema_manufacturer_base, schema_tag_base + ) -> dict[str, Any]: + return { + "version": "1.0", + "nodes": [ + schema_person_02_first_last, + schema_car_base, + schema_manufacturer_base, + schema_tag_base, + ], + } + + async def test_step01_baseline_backend(self, db: InfrahubDatabase, initial_dataset): + persons = await registry.manager.query(db=db, schema=PERSON_KIND) + assert len(persons) == 1 + + async def test_step02_check_attr_add_rename( + self, db: InfrahubDatabase, client: InfrahubClient, initial_dataset, schema_step02 + ): + success, response = await client.schema.check(schemas=[schema_step02]) + assert success + assert response == { + "diff": { + "added": {}, + "changed": { + "TestingPerson": { + "added": {}, + "changed": { + "attributes": { + "added": {}, + "changed": {}, + "removed": {"height": None}, + }, + }, + "removed": {}, + }, + }, + "removed": {}, + }, + } + + async def test_step02_load(self, db: InfrahubDatabase, client: InfrahubClient, initial_dataset, schema_step02): + response = await client.schema.load(schemas=[schema_step02]) + assert not response.errors + + # Ensure that we can query the nodes with the new schema in BRANCH1 + persons = await registry.manager.query( + db=db, + schema=PERSON_KIND, + filters={"firstname__value": "John"}, # , branch=self.branch1 + ) + assert len(persons) == 1 + john = persons[0] + assert john.firstname.value == "John" # type: ignore[attr-defined] + assert not hasattr(john, "height") + + async def test_step03_check(self, db: InfrahubDatabase, client: InfrahubClient, initial_dataset, schema_step03): + success, response = await client.schema.check(schemas=[schema_step03]) + assert response == { + "diff": { + "added": {}, + "changed": { + "TestingPerson": { + "added": {}, + "changed": { + "attributes": {"added": {"height": None}, "changed": {}, "removed": {}}, + }, + "removed": {}, + }, + }, + "removed": {}, + }, + } + assert success + + async def test_step03_load(self, db: InfrahubDatabase, client: InfrahubClient, initial_dataset, schema_step03): + response = await client.schema.load(schemas=[schema_step03]) + assert not response.errors + + # Modify the value for Height in the database + persons = await registry.manager.query( + db=db, + schema=PERSON_KIND, + filters={"firstname__value": "John"}, + ) + assert len(persons) == 1 + john = persons[0] + assert john.height.value is None + john.height.value = 200 + await john.save(db=db) + + # Validate that the new value has been properly saved + persons2 = await registry.manager.query( + db=db, + schema=PERSON_KIND, + filters={"firstname__value": "John"}, + ) + assert len(persons2) == 1 + john2 = persons2[0] + assert john2.height.value == 200 diff --git a/backend/tests/integration/schema_lifecycle/test_schema_migration_branch.py b/backend/tests/integration/schema_lifecycle/test_schema_migration_branch.py index 3031cbfda7..74d0dad45f 100644 --- a/backend/tests/integration/schema_lifecycle/test_schema_migration_branch.py +++ b/backend/tests/integration/schema_lifecycle/test_schema_migration_branch.py @@ -278,6 +278,9 @@ async def test_step03_check(self, db: InfrahubDatabase, client: InfrahubClient, async def test_step03_load(self, db: InfrahubDatabase, client: InfrahubClient, initial_dataset, schema_step03): manufacturer_schema = registry.schema.get_node_schema(name=MANUFACTURER_KIND_01, branch=self.branch1) + person_schema = registry.schema.get_node_schema(name=PERSON_KIND, branch=self.branch1) + height_attr_schema = person_schema.get_attribute(name="height") + assert height_attr_schema.id # Insert the ID of the attribute name into the schema in order to rename it firstname assert schema_step03["nodes"][2]["name"] == "CarMaker" @@ -295,6 +298,11 @@ async def test_step03_load(self, db: InfrahubDatabase, client: InfrahubClient, i john = persons[0] assert not hasattr(john, "height") + updated_height_attr_schema = await registry.manager.get_one( + db=db, branch=self.branch1.name, id=height_attr_schema.id + ) + assert updated_height_attr_schema is None + manufacturers = await registry.manager.query( db=db, schema=MANUFACTURER_KIND_03, filters={"name__value": "renault"}, branch=self.branch1.name ) @@ -303,11 +311,18 @@ async def test_step03_load(self, db: InfrahubDatabase, client: InfrahubClient, i renault_cars = await renault.cars.get_peers(db=db) # type: ignore[attr-defined] assert len(renault_cars) == 2 - async def test_rebase(self, db: InfrahubDatabase, client: InfrahubClient, initial_dataset): + async def test_rebase(self, db: InfrahubDatabase, client: InfrahubClient, default_branch: Branch, initial_dataset): branch = await client.branch.rebase(branch_name=self.branch1.name) assert branch + person_schema = registry.schema.get_node_schema(name=PERSON_KIND, branch=default_branch) + height_attr_schema = person_schema.get_attribute(name="height") + assert height_attr_schema.id # Validate that all data added to main after the creation of the branch has been migrated properly + updated_height_attr_schema = await registry.manager.get_one( + db=db, branch=self.branch1.name, id=height_attr_schema.id + ) + assert updated_height_attr_schema is None persons = await registry.manager.query( db=db, schema=PERSON_KIND, filters={"firstname__value": "Jane"}, branch=self.branch1.name ) diff --git a/backend/tests/integration/schema_lifecycle/test_schema_missing_menu_placement.py b/backend/tests/integration/schema_lifecycle/test_schema_missing_menu_placement.py deleted file mode 100644 index 076ea39112..0000000000 --- a/backend/tests/integration/schema_lifecycle/test_schema_missing_menu_placement.py +++ /dev/null @@ -1,30 +0,0 @@ -from infrahub_sdk import InfrahubClient - -from .shared import ( - TestSchemaLifecycleBase, -) - - -class TestSchemaMissingMenuPlacement(TestSchemaLifecycleBase): - async def test_schema_missing_menu_placement(self, client: InfrahubClient): - schema = { - "version": "1.0", - "nodes": [ - { - "name": "BNode", - "namespace": "Infra", - "menu_placement": "UnexistingNode", - "label": "BNode", - "display_labels": ["name__value"], - "attributes": [{"name": "name", "kind": "Text", "unique": True}], - } - ], - } - - response = await client.schema.load(schemas=[schema], branch="main") - assert response.schema_updated is False - assert response.errors["errors"][0]["extensions"]["code"] == 422 - assert ( - response.errors["errors"][0]["message"] - == "InfraBNode refers to an invalid menu placement node: UnexistingNode." - ) diff --git a/backend/tests/integration/sdk/test_node_create_constraint.py b/backend/tests/integration/sdk/test_node_create_constraint.py index e04b0b9711..74d9a3a3bf 100644 --- a/backend/tests/integration/sdk/test_node_create_constraint.py +++ b/backend/tests/integration/sdk/test_node_create_constraint.py @@ -293,12 +293,12 @@ async def test_step_03_add_node_failure( async def test_create_repository_with_slash_failure(self, db: InfrahubDatabase, initial_dataset): repo = await Node.init(schema="CoreRepository", db=db) with pytest.raises( - ValidationError, match=re.escape("repo/name must be conform with the regex: '^[^/]*$' at name") + ValidationError, match=re.escape("repo/name must conform with the regex: '^[^/]*$' at name") ): await repo.new(db=db, name="repo/name", location="dummy") repo = await Node.init(schema="CoreReadOnlyRepository", db=db) with pytest.raises( - ValidationError, match=re.escape("repo/name must be conform with the regex: '^[^/]*$' at name") + ValidationError, match=re.escape("repo/name must conform with the regex: '^[^/]*$' at name") ): await repo.new(db=db, name="repo/name", location="dummy") 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..33d0e3d0b1 100644 --- a/backend/tests/integration/services/adapters/message_bus/test_rabbitmq.py +++ b/backend/tests/integration/services/adapters/message_bus/test_rabbitmq.py @@ -4,7 +4,7 @@ from copy import deepcopy from dataclasses import dataclass from functools import partial -from typing import TYPE_CHECKING, Any, Optional +from typing import TYPE_CHECKING, Any from uuid import uuid4 import httpx @@ -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: @@ -109,7 +109,7 @@ async def delete_virtual_host(self) -> None: response = await self._request(method="DELETE", url=f"{self.base_url}/vhosts/{self.settings.virtualhost}") assert response.status_code in {204, 404} - async def _request(self, method: str, url: str, payload: Optional[dict] = None) -> httpx.Response: + async def _request(self, method: str, url: str, payload: dict | None = None) -> httpx.Response: params: dict[str, Any] = {} if payload: params["json"] = payload @@ -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/nautobot-v2_to_infrahub/infrahub/__init__.py b/backend/tests/integration/services/adapters/workflow/__init__.py similarity index 100% rename from sync/examples/nautobot-v2_to_infrahub/infrahub/__init__.py rename to backend/tests/integration/services/adapters/workflow/__init__.py diff --git a/backend/tests/integration/services/adapters/workflow/test_setup.py b/backend/tests/integration/services/adapters/workflow/test_setup.py new file mode 100644 index 0000000000..ba0fe8358a --- /dev/null +++ b/backend/tests/integration/services/adapters/workflow/test_setup.py @@ -0,0 +1,23 @@ +from prefect.client.orchestration import PrefectClient + +from infrahub.workflows.catalogue import INFRAHUB_WORKER_POOL +from infrahub.workflows.initialization import setup_task_manager +from tests.helpers.test_worker import TestWorkerInfrahubAsync + +# @pytest.fixture +# async def prefect_server(redis, prefect): +# await setup_task_manager() + + +class TestTaskManagerSetup(TestWorkerInfrahubAsync): + async def test_setup_task_manager(self, prefect_client: PrefectClient): + await setup_task_manager() + + response = await prefect_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() + + response = await prefect_client.read_work_pool(INFRAHUB_WORKER_POOL.name) + assert response.type == INFRAHUB_WORKER_POOL.worker_type diff --git a/backend/tests/integration/services/adapters/workflow/test_workflow_execution.py b/backend/tests/integration/services/adapters/workflow/test_workflow_execution.py new file mode 100644 index 0000000000..983ae5b996 --- /dev/null +++ b/backend/tests/integration/services/adapters/workflow/test_workflow_execution.py @@ -0,0 +1,76 @@ +import asyncio + +import pytest +from prefect.client.orchestration import PrefectClient +from prefect.client.schemas.objects import WorkPool +from pydantic import ValidationError + +from infrahub.core.branch import Branch +from infrahub.database import InfrahubDatabase +from infrahub.services.adapters.workflow.worker import WorkflowWorkerExecution +from infrahub.tasks.dummy import DUMMY_FLOW, DUMMY_FLOW_BROKEN, DummyInput, DummyOutput +from infrahub.workers.infrahub_async import ( + InfrahubWorkerAsync, +) +from tests.helpers.test_worker import TestWorkerInfrahubAsync + + +class TestWorkflowExecution(TestWorkerInfrahubAsync): + async def test_execute_workflow_success( + self, + db: InfrahubDatabase, + default_branch: Branch, + prefect_client: PrefectClient, + prefect_worker: InfrahubWorkerAsync, + dummy_flows_deployment, + work_pool: WorkPool, + client, + ): + service = WorkflowWorkerExecution() + + task = asyncio.create_task( + service.execute_workflow( + workflow=DUMMY_FLOW, parameters={"data": DummyInput(firstname="John", lastname="Doe")} + ) + ) + + # Wait for the flow to show up in Prefect + flow = await self.wait_for_flow(client=prefect_client, work_pool_id=work_pool.id) + + # Execute the flow + worker_result = await self.worker_run_flow(worker=prefect_worker, client=prefect_client, flow=flow) + assert worker_result.status_code == 0 + + result = await task + assert isinstance(result, DummyOutput) + assert result.full_name == "John, Doe" + + async def test_execute_workflow_failure( + self, + db: InfrahubDatabase, + default_branch: Branch, + prefect_client: PrefectClient, + prefect_worker: InfrahubWorkerAsync, + dummy_flows_deployment, + work_pool: WorkPool, + client, + ): + service = WorkflowWorkerExecution() + + task = asyncio.create_task( + service.execute_workflow( + workflow=DUMMY_FLOW_BROKEN, parameters={"data": DummyInput(firstname="John", lastname="Doe")} + ) + ) + + # Wait for the flow to show up in Prefect + flow = await self.wait_for_flow(client=prefect_client, work_pool_id=work_pool.id) + + # Execute the flow + worker_result = await self.worker_run_flow(worker=prefect_worker, client=prefect_client, flow=flow) + assert worker_result.status_code == 0 + + with pytest.raises(ValidationError) as exc: + await task + + assert "validation error for DummyOutput" in str(exc.value) diff --git a/sync/examples/nautobot-v2_to_infrahub/nautobot/__init__.py b/backend/tests/integration/transform/__init__.py similarity index 100% rename from sync/examples/nautobot-v2_to_infrahub/nautobot/__init__.py rename to backend/tests/integration/transform/__init__.py diff --git a/backend/tests/integration/transform/test_transform.py b/backend/tests/integration/transform/test_transform.py new file mode 100644 index 0000000000..f5c892a371 --- /dev/null +++ b/backend/tests/integration/transform/test_transform.py @@ -0,0 +1,159 @@ +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 infrahub.services import InfrahubServices, services +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 + + +@pytest.fixture +def init_service(): + original = services.service + service = InfrahubServices(client=InfrahubClient()) + services.service = service + yield service + services.service = original + + +class TestTransforms(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(scope="class") + 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(scope="class") + 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" + + async def test_transform_python( + self, db: InfrahubDatabase, client: InfrahubClient, init_service, repo: InfrahubRepository + ): + repositories = await NodeManager.query(db=db, schema=InfrahubKind.REPOSITORY) + queries = await NodeManager.query(db=db, schema=InfrahubKind.GRAPHQLQUERY) + + t2 = await Node.init(db=db, schema=InfrahubKind.TRANSFORMPYTHON) + await t2.new( + db=db, + name="test-python-transform", + query=str(queries[0].id), + repository=str(repositories[0].id), + class_name="PersonWithCarsTransform", + file_path="transforms/person_with_cars_transform.py", + ) + await t2.save(db=db) + + response = await client._get(url=f"{client.address}/api/transform/python/test-python-transform?name=John") + assert response.json() == {"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..62e2c3c372 100644 --- a/backend/tests/integration/user_workflows/test_user_worflow.py +++ b/backend/tests/integration/user_workflows/test_user_worflow.py @@ -6,7 +6,7 @@ from infrahub.core.constants import NULL_VALUE from infrahub.database import InfrahubDatabase from infrahub.server import app -from infrahub.test_data import dataset01 as ds01 +from tests.test_data import dataset01 as ds01 headers = {"Authorization": "Token nelly"} @@ -142,7 +142,7 @@ def __init__(self) -> None: class TestUserWorkflow01: @pytest.fixture(scope="class") - async def client(self): + async def client(self, redis, nats, prefect_test_fixture): client = TestClient(app) return client diff --git a/sync/examples/netbox_to_infrahub/infrahub/__init__.py b/backend/tests/integration/workers/__init__.py similarity index 100% rename from sync/examples/netbox_to_infrahub/infrahub/__init__.py rename to backend/tests/integration/workers/__init__.py diff --git a/backend/tests/integration/workers/test_infrahubasync.py b/backend/tests/integration/workers/test_infrahubasync.py new file mode 100644 index 0000000000..72b65accb2 --- /dev/null +++ b/backend/tests/integration/workers/test_infrahubasync.py @@ -0,0 +1,109 @@ +from typing import TYPE_CHECKING + +import pytest +from prefect.client.orchestration import PrefectClient +from prefect.client.schemas import StateType +from prefect.deployments import run_deployment +from pydantic import ValidationError + +from infrahub import __version__ as infrahub_version +from infrahub.core.branch import Branch +from infrahub.database import InfrahubDatabase +from infrahub.tasks.dummy import DUMMY_FLOW, DUMMY_FLOW_BROKEN, DummyInput, DummyOutput +from infrahub.workers.infrahub_async import ( + WORKER_DEFAULT_RESULT_STORAGE_BLOCK, + WORKER_QUERY_SECONDS, + InfrahubWorkerAsync, +) +from tests.helpers.test_worker import TestWorkerInfrahubAsync + +if TYPE_CHECKING: + from prefect.client.schemas.objects import FlowRun + + +class TestWorker(TestWorkerInfrahubAsync): + async def test_flow_configuration( + self, + db: InfrahubDatabase, + default_branch: Branch, + prefect_client: PrefectClient, + prefect_worker: InfrahubWorkerAsync, + dummy_flows_deployment, + client, + ): + # Schedule the execution of the deployment from the server + flow: FlowRun = await run_deployment( + name=DUMMY_FLOW.full_name, parameters={"data": DummyInput(firstname="John", lastname="Doe")}, timeout=0 + ) # type: ignore[return-value, misc] + + # Prepare the execution of the flow, pull the information about the deployment + assert flow.deployment_id + deployment = await prefect_client.read_deployment(deployment_id=flow.deployment_id) + flow_config = await prefect_worker._get_configuration(flow_run=flow, deployment=deployment) + + assert "PREFECT_WORKER_QUERY_SECONDS" in flow_config.env + assert flow_config.env.get("PREFECT_WORKER_QUERY_SECONDS") == WORKER_QUERY_SECONDS + + assert "PREFECT_DEFAULT_RESULT_STORAGE_BLOCK" in flow_config.env + assert flow_config.env.get("PREFECT_DEFAULT_RESULT_STORAGE_BLOCK") == WORKER_DEFAULT_RESULT_STORAGE_BLOCK + + assert "infrahub.app/version" in flow_config.labels + assert flow_config.labels.get("infrahub.app/version") == infrahub_version + + # delete the flow + await prefect_client.delete_flow_run(flow_run_id=flow.id) + + async def test_successfull_flow( + self, + db: InfrahubDatabase, + default_branch: Branch, + prefect_client: PrefectClient, + prefect_worker: InfrahubWorkerAsync, + dummy_flows_deployment, + client, + ): + # Schedule the execution of the deployment from the server + flow: FlowRun = await run_deployment( + name=DUMMY_FLOW.full_name, parameters={"data": DummyInput(firstname="John", lastname="Doe")}, timeout=0 + ) # type: ignore[return-value, misc] + + result_worker = await self.worker_run_flow(worker=prefect_worker, client=prefect_client, flow=flow) + assert result_worker.status_code == 0 + + # Check the status of the flow in Prefect after the run + flow_after = await prefect_client.read_flow_run(flow_run_id=flow.id) + assert flow_after.state + assert flow_after.state.type == StateType.COMPLETED + + result = await flow_after.state.result(raise_on_failure=True, fetch=True) # type: ignore[call-overload] + assert isinstance(result, DummyOutput) + assert result.full_name == "John, Doe" + + async def test_broken_flow( + self, + db: InfrahubDatabase, + default_branch: Branch, + prefect_client: PrefectClient, + prefect_worker: InfrahubWorkerAsync, + dummy_flows_deployment, + client, + ): + # Schedule the execution of the deployment from the server + flow: FlowRun = await run_deployment( + name=DUMMY_FLOW_BROKEN.full_name, + parameters={"data": DummyInput(firstname="John", lastname="Doe")}, + timeout=0, + ) # type: ignore[return-value, misc] + + result_worker = await self.worker_run_flow(worker=prefect_worker, client=prefect_client, flow=flow) + assert result_worker.status_code == 0 + + # Check the status of the flow in Prefect after the run + flow_after = await prefect_client.read_flow_run(flow_run_id=flow.id) + assert flow_after.state + assert flow_after.state.type == StateType.FAILED + + with pytest.raises(ValidationError) as exc: + await flow_after.state.result(raise_on_failure=True, fetch=True) # type: ignore[call-overload] + + assert "validation error for DummyOutput" in str(exc.value) diff --git a/sync/examples/netbox_to_infrahub/netbox/__init__.py b/backend/tests/query_benchmark/__init__.py similarity index 100% rename from sync/examples/netbox_to_infrahub/netbox/__init__.py rename to backend/tests/query_benchmark/__init__.py diff --git a/backend/tests/query_benchmark/conftest.py b/backend/tests/query_benchmark/conftest.py new file mode 100644 index 0000000000..d8a0d3db58 --- /dev/null +++ b/backend/tests/query_benchmark/conftest.py @@ -0,0 +1,73 @@ +from pathlib import Path +from typing import Any + +import pytest + +from infrahub.core.constants import BranchSupportType +from infrahub.core.schema import SchemaRoot +from tests.helpers.query_benchmark.db_query_profiler import GraphProfileGenerator + +RESULTS_FOLDER = Path(__file__).resolve().parent / "query_performance_results" + + +@pytest.fixture +async def car_person_schema_root() -> SchemaRoot: + schema: dict[str, Any] = { + "nodes": [ + { + "name": "Car", + "namespace": "Test", + "default_filter": "name__value", + "display_labels": ["name__value", "color__value"], + "uniqueness_constraints": [["name__value"]], + "branch": BranchSupportType.AWARE.value, + "attributes": [ + {"name": "name", "kind": "Text", "unique": True}, + {"name": "nbr_seats", "kind": "Number", "optional": True}, + {"name": "color", "kind": "Text", "default_value": "#444444", "max_length": 7, "optional": True}, + {"name": "is_electric", "kind": "Boolean", "optional": True}, + { + "name": "transmission", + "kind": "Text", + "optional": True, + "enum": ["manual", "automatic", "flintstone-feet"], + }, + ], + "relationships": [ + { + "name": "owner", + "label": "Commander of Car", + "peer": "TestPerson", + "cardinality": "one", + }, + ], + }, + { + "name": "Person", + "namespace": "Test", + "default_filter": "name__value", + "display_labels": ["name__value"], + "branch": BranchSupportType.AWARE.value, + "uniqueness_constraints": [["name__value"]], + "attributes": [ + {"name": "name", "kind": "Text", "unique": True}, + {"name": "height", "kind": "Number", "optional": True}, + ], + "relationships": [ + {"name": "cars", "peer": "TestCar", "cardinality": "many"}, + ], + }, + ], + } + + return SchemaRoot(**schema) + + +@pytest.fixture(scope="session") +async def graph_generator() -> GraphProfileGenerator: + """ + Use GraphProfileGenerator as a fixture as it may allow to properly generate graphs from + distinct tests, instead of having each test managing its own display. + """ + + return GraphProfileGenerator() diff --git a/backend/tests/query_benchmark/test_node_unique_attribute_constraint.py b/backend/tests/query_benchmark/test_node_unique_attribute_constraint.py new file mode 100644 index 0000000000..73499b87d3 --- /dev/null +++ b/backend/tests/query_benchmark/test_node_unique_attribute_constraint.py @@ -0,0 +1,173 @@ +import inspect +from pathlib import Path + +import pytest + +from infrahub.core import registry +from infrahub.core.validators.uniqueness.model import ( + NodeUniquenessQueryRequest, + QueryAttributePath, + QueryRelationshipAttributePath, +) +from infrahub.core.validators.uniqueness.query import NodeUniqueAttributeConstraintQuery +from infrahub.database import QueryConfig +from infrahub.database.constants import Neo4jRuntime +from infrahub.log import get_logger +from tests.helpers.constants import NEO4J_COMMUNITY_IMAGE, NEO4J_ENTERPRISE_IMAGE +from tests.helpers.query_benchmark.car_person_generators import ( + CarGeneratorWithOwnerHavingUniqueCar, +) +from tests.helpers.query_benchmark.data_generator import load_data_and_profile +from tests.helpers.query_benchmark.db_query_profiler import BenchmarkConfig, GraphProfileGenerator +from tests.query_benchmark.conftest import RESULTS_FOLDER +from tests.query_benchmark.utils import start_db_and_create_default_branch + +log = get_logger() + +# pytestmark = pytest.mark.skip("Not relevant to test this currently.") + + +async def benchmark_uniqueness_query( + query_request, + car_person_schema_root, + graph_generator: GraphProfileGenerator, + benchmark_config: BenchmarkConfig, + test_params_label: str, + test_name: str, +): + """ + Profile NodeUniqueAttributeConstraintQuery with a given query_request / configuration, using a Car generator. + """ + + # Initialization + queries_names_to_config = { + NodeUniqueAttributeConstraintQuery.name: QueryConfig(neo4j_runtime=benchmark_config.neo4j_runtime) + } + db_profiling_queries, default_branch = await start_db_and_create_default_branch( + neo4j_image=benchmark_config.neo4j_image, + load_indexes=benchmark_config.load_db_indexes, + queries_names_to_config=queries_names_to_config, + ) + registry.schema.register_schema(schema=car_person_schema_root, branch=default_branch.name) + + # Build function to profile + async def init_and_execute(): + # Need this function to avoid loading data between `init` and `execute` methods. + query = await NodeUniqueAttributeConstraintQuery.init( + db=db_profiling_queries, + branch=default_branch, + query_request=query_request, + ) + await query.execute(db=db_profiling_queries) + assert len(query.results) == 0 # supposed to have no violation with CarGeneratorWithOwnerHavingUniqueCar + + nb_cars = 10_000 + cars_generator = CarGeneratorWithOwnerHavingUniqueCar(db=db_profiling_queries, nb_persons=nb_cars) + module_name = Path(__file__).stem + graph_output_location = RESULTS_FOLDER / module_name / test_name + + await load_data_and_profile( + data_generator=cars_generator, + func_call=init_and_execute, + profile_frequency=100, + nb_elements=nb_cars, + graphs_output_location=graph_output_location, + test_label=test_params_label, + graph_generator=graph_generator, + ) + + +@pytest.mark.parametrize( + "query_request", + [ + NodeUniquenessQueryRequest( + kind="TestCar", unique_attribute_paths={QueryAttributePath(attribute_name="name", property_name="value")} + ), + NodeUniquenessQueryRequest( + kind="TestCar", + unique_attribute_paths={ + QueryAttributePath(attribute_name="name", property_name="value"), + QueryAttributePath(attribute_name="nbr_seats", property_name="value"), + }, + ), + NodeUniquenessQueryRequest( + kind="TestCar", + unique_attribute_paths={ + QueryAttributePath(attribute_name="name", property_name="value"), + QueryAttributePath(attribute_name="nbr_seats", property_name="value"), + }, + relationship_attribute_paths={ + QueryRelationshipAttributePath(identifier="testcar__testperson", attribute_name="name") + }, + ), + ], +) +async def test_multiple_constraints(query_request, car_person_schema_root, graph_generator): + benchmark_config = BenchmarkConfig(neo4j_runtime=Neo4jRuntime.DEFAULT, neo4j_image=NEO4J_ENTERPRISE_IMAGE) + await benchmark_uniqueness_query( + query_request=query_request, + car_person_schema_root=car_person_schema_root, + benchmark_config=benchmark_config, + test_params_label=str(query_request), + test_name=inspect.currentframe().f_code.co_name, + graph_generator=graph_generator, + ) + + +@pytest.mark.parametrize( + "benchmark_config", + [ + BenchmarkConfig(neo4j_runtime=Neo4jRuntime.DEFAULT, neo4j_image=NEO4J_COMMUNITY_IMAGE), + BenchmarkConfig(neo4j_runtime=Neo4jRuntime.DEFAULT, neo4j_image=NEO4J_ENTERPRISE_IMAGE), + BenchmarkConfig(neo4j_runtime=Neo4jRuntime.PARALLEL, neo4j_image=NEO4J_ENTERPRISE_IMAGE), + ], +) +async def test_multiple_runtimes(benchmark_config, car_person_schema_root, graph_generator): + query_request = NodeUniquenessQueryRequest( + kind="TestCar", + unique_attribute_paths={ + QueryAttributePath(attribute_name="name", property_name="value"), + QueryAttributePath(attribute_name="nbr_seats", property_name="value"), + }, + relationship_attribute_paths={ + QueryRelationshipAttributePath(identifier="testcar__testperson", attribute_name="name") + }, + ) + + await benchmark_uniqueness_query( + query_request=query_request, + car_person_schema_root=car_person_schema_root, + benchmark_config=benchmark_config, + test_params_label=str(benchmark_config), + test_name=inspect.currentframe().f_code.co_name, + graph_generator=graph_generator, + ) + + +@pytest.mark.parametrize( + "benchmark_config", + [ + BenchmarkConfig(neo4j_runtime=Neo4jRuntime.PARALLEL, neo4j_image=NEO4J_ENTERPRISE_IMAGE, load_db_indexes=False), + # BenchmarkConfig(neo4j_runtime=Neo4jRuntime.PARALLEL, neo4j_image=NEO4J_ENTERPRISE_IMAGE, load_db_indexes=True), + ], +) +async def test_indexes(benchmark_config, car_person_schema_root, graph_generator): + query_request = NodeUniquenessQueryRequest( + kind="TestCar", + unique_attribute_paths={ + QueryAttributePath(attribute_name="name", property_name="value"), + QueryAttributePath(attribute_name="nbr_seats", property_name="value"), + }, + relationship_attribute_paths={ + QueryRelationshipAttributePath(identifier="testcar__testperson", attribute_name="name") + }, + ) + + await benchmark_uniqueness_query( + query_request=query_request, + car_person_schema_root=car_person_schema_root, + benchmark_config=benchmark_config, + test_params_label=str(benchmark_config), + test_name=inspect.currentframe().f_code.co_name, + graph_generator=graph_generator, + ) diff --git a/backend/tests/query_benchmark/utils.py b/backend/tests/query_benchmark/utils.py new file mode 100644 index 0000000000..c13d5951f4 --- /dev/null +++ b/backend/tests/query_benchmark/utils.py @@ -0,0 +1,36 @@ +from typing import Optional, Tuple + +from infrahub import config +from infrahub.core import registry +from infrahub.core.branch import Branch +from infrahub.core.graph.index import node_indexes, rel_indexes +from infrahub.core.initialization import create_default_branch, create_global_branch, create_root_node +from infrahub.core.schema.manager import SchemaManager +from infrahub.database import InfrahubDatabaseMode, QueryConfig, get_db +from tests.helpers.constants import PORT_BOLT_NEO4J +from tests.helpers.query_benchmark.db_query_profiler import InfrahubDatabaseProfiler +from tests.helpers.utils import start_neo4j_container + + +async def start_db_and_create_default_branch( + neo4j_image: str, load_indexes: bool, queries_names_to_config: Optional[dict[str, QueryConfig]] = None +) -> Tuple[InfrahubDatabaseProfiler, Branch]: + # Start database and create db profiler + neo4j_container = start_neo4j_container(neo4j_image) + config.SETTINGS.database.port = int(neo4j_container.get_exposed_port(PORT_BOLT_NEO4J)) + db = InfrahubDatabaseProfiler( + mode=InfrahubDatabaseMode.DRIVER, driver=await get_db(), queries_names_to_config=queries_names_to_config + ) + + # Create default branch + await create_root_node(db=db) + default_branch = await create_default_branch(db=db) + await create_global_branch(db=db) + registry.schema = SchemaManager() + + # Initialize indexes if needed + if load_indexes: + db.manager.index.init(nodes=node_indexes, rels=rel_indexes) + await db.manager.index.add() + + return db, default_branch 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/sync/examples/observium_to_infrahub/infrahub/__init__.py b/backend/tests/test_data/__init__.py similarity index 100% rename from sync/examples/observium_to_infrahub/infrahub/__init__.py rename to backend/tests/test_data/__init__.py diff --git a/backend/infrahub/test_data/dataset01.py b/backend/tests/test_data/dataset01.py similarity index 92% rename from backend/infrahub/test_data/dataset01.py rename to backend/tests/test_data/dataset01.py index cfc2dacaf2..f13f2f7fe7 100644 --- a/backend/infrahub/test_data/dataset01.py +++ b/backend/tests/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/dataset03.py b/backend/tests/test_data/dataset03.py similarity index 99% rename from backend/infrahub/test_data/dataset03.py rename to backend/tests/test_data/dataset03.py index 842f731a8f..e1a9f8ccac 100644 --- a/backend/infrahub/test_data/dataset03.py +++ b/backend/tests/test_data/dataset03.py @@ -2,7 +2,7 @@ from collections import defaultdict from ipaddress import IPv4Network -from infrahub_sdk import UUIDT +from infrahub_sdk.uuidt import UUIDT from infrahub.core.manager import NodeManager from infrahub.core.node import Node diff --git a/backend/infrahub/test_data/dataset04.py b/backend/tests/test_data/dataset04.py similarity index 100% rename from backend/infrahub/test_data/dataset04.py rename to backend/tests/test_data/dataset04.py 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..1699566382 100644 --- a/backend/tests/unit/api/test_05_query_api.py +++ b/backend/tests/unit/api/test_05_query_api.py @@ -1,34 +1,40 @@ +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( - db: InfrahubDatabase, - default_branch: Branch, - create_test_admin, - register_core_models_schema, -): + db: InfrahubDatabase, 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, + admin_headers, + create_test_admin, + 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( - "/api/query/query01?update_group=true&subscribers=AAAAAA&subscribers=BBBBBB", - headers=client_headers, + "/api/query/query01?update_group=true&subscribers=AAAAAA&subscribers=BBBBBB", headers=admin_headers ) assert "errors" not in response.json() @@ -61,17 +67,12 @@ 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, admin_headers, default_branch, create_test_admin, car_person_data +): # Must execute in a with block to execute the startup/shutdown events with client: - response = client.get( - "/api/query/query02?update_group=true&person=John", - headers=client_headers, - ) + response = client.get("/api/query/query02?update_group=true&person=John", headers=admin_headers) assert "errors" not in response.json() assert response.status_code == 200 @@ -98,14 +99,11 @@ 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, admin_headers, default_branch, create_test_admin, car_person_data ): # Must execute in a with block to execute the startup/shutdown events with client: - response = client.get( - "/api/query/query01", - headers=client_headers, - ) + response = client.get("/api/query/query01", headers=admin_headers) assert "errors" not in response.json() assert response.status_code == 200 @@ -120,7 +118,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 +144,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,16 +164,19 @@ 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, + admin_headers, + default_branch, + create_test_admin, + car_person_data, + authentication_base, ): await create_branch(branch_name="branch1", db=db) # Must execute in a with block to execute the startup/shutdown events with client: - response = client.get( - "/api/query/query01?branch=branch1", - headers=client_headers, - ) + response = client.get("/api/query/query01?branch=branch1", headers=admin_headers) assert "errors" not in response.json() assert response.status_code == 200 @@ -189,7 +190,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 +208,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 +223,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 deleted file mode 100644 index d4edb6790e..0000000000 --- a/backend/tests/unit/api/test_10_transformation_api.py +++ /dev/null @@ -1,84 +0,0 @@ -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 -): - 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="CoreTransformPython") - await t1.new( - db=db, - name="transform01", - query=str(queries[0].id), - repository=str(repositories[0].id), - file_path="transform01.py", - class_name="Transform01", - ) - await t1.save(db=db) - - # Must execute in a with block to execute the startup/shutdown events - with client: - mock_response = TransformPythonDataResponse( - data={"transformed_data": {"KEY1": "value1", "KEY2": "value2"}}, - ) - rpc_bus.add_mock_reply(response=mock_response) - - response = client.get( - "/api/transform/python/transform01", - headers=client_headers, - ) - - assert response.status_code == 200 - assert response.json() is not None - 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..ec133c8970 100644 --- a/backend/tests/unit/api/test_11_artifact.py +++ b/backend/tests/unit/api/test_11_artifact.py @@ -1,127 +1,140 @@ -from fastapi.testclient import TestClient +from unittest.mock import call, patch + +from starlette.testclient import TestClient from infrahub.core import registry from infrahub.core.constants import InfrahubKind from infrahub.core.node import Node from infrahub.database import InfrahubDatabase -from infrahub.message_bus import messages - - -async def test_artifact_definition_endpoint( - db: InfrahubDatabase, - admin_headers, - default_branch, - rpc_bus, - register_core_models_schema, - register_builtin_models_schema, - 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) - - t1 = await Node.init(db=db, schema="CoreTransformPython") - await t1.new( - db=db, - name="transform01", - query=str(car_person_data_generic["q1"].id), - repository=str(car_person_data_generic["r1"].id), - file_path="transform01.py", - class_name="Transform01", - ) - await t1.save(db=db) - - ad1 = await Node.init(db=db, schema=InfrahubKind.ARTIFACTDEFINITION) - await ad1.new( - db=db, - name="artifactdef01", - targets=g1, - transformation=t1, - content_type="application/json", - artifact_name="myartifact", - parameters={"value": {"name": "name__value"}}, - ) - await ad1.save(db=db) - - # Must execute in a with block to execute the startup/shutdown events - with client: - response = client.post( - f"/api/artifact/generate/{ad1.id}", - headers=admin_headers, +from infrahub.git.models import RequestArtifactDefinitionGenerate +from infrahub.server import app +from infrahub.workflows.catalogue import REQUEST_ARTIFACT_DEFINITION_GENERATE +from tests.helpers.test_app import TestInfrahubApp + + +class TestArtifact11(TestInfrahubApp): + async def test_artifact_definition_endpoint( + self, + db: InfrahubDatabase, + admin_headers, + default_branch, + register_core_models_schema, + register_builtin_models_schema, + car_person_data_generic, + authentication_base, + client, + ): + 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) + + t1 = await Node.init(db=db, schema="CoreTransformPython") + await t1.new( + db=db, + name="transform01", + query=str(car_person_data_generic["q1"].id), + repository=str(car_person_data_generic["r1"].id), + file_path="transform01.py", + class_name="Transform01", + ) + await t1.save(db=db) + + ad1 = await Node.init(db=db, schema=InfrahubKind.ARTIFACTDEFINITION) + await ad1.new( + db=db, + name="artifactdef01", + targets=g1, + transformation=t1, + content_type="application/json", + artifact_name="myartifact", + parameters={"value": {"name": "name__value"}}, + ) + await ad1.save(db=db) + + app_client = TestClient(app) + + # Must execute in a with block to execute the startup/shutdown events + with ( + app_client, + patch( + "infrahub.services.adapters.workflow.local.WorkflowLocalExecution.submit_workflow" + ) as mock_submit_workflow, + ): + response = app_client.post( + f"/api/artifact/generate/{ad1.id}", + headers=admin_headers, + ) + + assert response.status_code == 200 + expected_calls = [ + call( + workflow=REQUEST_ARTIFACT_DEFINITION_GENERATE, + parameters={ + "model": RequestArtifactDefinitionGenerate(artifact_definition=ad1.id, branch="main", limit=[]) + }, + ), + ] + mock_submit_workflow.assert_has_calls(expected_calls) + + async def test_artifact_endpoint( + self, + db: InfrahubDatabase, + admin_headers, + register_core_models_schema, + register_builtin_models_schema, + car_person_data_generic, + authentication_base, + ): + app_client = TestClient(app) + + with app_client: + response = app_client.get("/api/artifact/95008984-16ca-4e58-8323-0899bb60035f", headers=admin_headers) + assert response.status_code == 404 + + 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) + + t1 = await Node.init(db=db, schema="CoreTransformPython") + await t1.new( + db=db, + name="transform01", + query=str(car_person_data_generic["q1"].id), + repository=str(car_person_data_generic["r1"].id), + file_path="transform01.py", + class_name="Transform01", + ) + await t1.save(db=db) + + ad1 = await Node.init(db=db, schema=InfrahubKind.ARTIFACTDEFINITION) + await ad1.new( + db=db, + name="artifactdef01", + targets=g1, + transformation=t1, + content_type="application/json", + artifact_name="myartifact", + parameters={"value": {"name": "name__value"}}, ) + await ad1.save(db=db) + + art1 = await Node.init(db=db, schema=InfrahubKind.ARTIFACT) + await art1.new( + db=db, + name="myyartifact", + definition=ad1, + status="Ready", + object=car_person_data_generic["c1"], + storage_id="95008984-16ca-4e58-8323-0899bb60035f", + checksum="60d39063c26263353de24e1b913e1e1c", + content_type="application/json", + ) + await art1.save(db=db) + + registry.storage.store(identifier="95008984-16ca-4e58-8323-0899bb60035f", content='{"test": true}'.encode()) + + with app_client: + response = app_client.get(f"/api/artifact/{art1.id}", headers=admin_headers) - assert response.status_code == 200 - assert ( - messages.RequestArtifactDefinitionGenerate(artifact_definition=ad1.id, branch="main", limit=[]) - in rpc_bus.messages - ) - - -async def test_artifact_endpoint( - db: InfrahubDatabase, - 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 - - 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) - - t1 = await Node.init(db=db, schema="CoreTransformPython") - await t1.new( - db=db, - name="transform01", - query=str(car_person_data_generic["q1"].id), - repository=str(car_person_data_generic["r1"].id), - file_path="transform01.py", - class_name="Transform01", - ) - await t1.save(db=db) - - ad1 = await Node.init(db=db, schema=InfrahubKind.ARTIFACTDEFINITION) - await ad1.new( - db=db, - name="artifactdef01", - targets=g1, - transformation=t1, - content_type="application/json", - artifact_name="myartifact", - parameters={"value": {"name": "name__value"}}, - ) - await ad1.save(db=db) - - art1 = await Node.init(db=db, schema=InfrahubKind.ARTIFACT) - await art1.new( - db=db, - name="myyartifact", - definition=ad1, - status="Ready", - object=car_person_data_generic["c1"], - storage_id="95008984-16ca-4e58-8323-0899bb60035f", - checksum="60d39063c26263353de24e1b913e1e1c", - content_type="application/json", - ) - await art1.save(db=db) - - registry.storage.store(identifier="95008984-16ca-4e58-8323-0899bb60035f", content='{"test": true}'.encode()) - - with client: - response = client.get(f"/api/artifact/{art1.id}", headers=admin_headers) - - assert response.status_code == 200 - assert response.json() == {"test": True} + assert response.status_code == 200 + assert response.json() == {"test": True} 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..e5813b2b6b 100644 --- a/backend/tests/unit/api/test_20_graphql_api.py +++ b/backend/tests/unit/api/test_20_graphql_api.py @@ -7,7 +7,9 @@ from infrahub.database import InfrahubDatabase -async def test_graphql_endpoint(db: InfrahubDatabase, client, client_headers, default_branch: Branch, car_person_data): +async def test_graphql_endpoint( + db: InfrahubDatabase, client, admin_headers, default_branch: Branch, create_test_admin, car_person_data +): query = """ query { TestPerson { @@ -33,11 +35,7 @@ async def test_graphql_endpoint(db: InfrahubDatabase, client, client_headers, de # Must execute in a with block to execute the startup/shutdown events with client: - response = client.post( - "/graphql", - json={"query": query}, - headers=client_headers, - ) + response = client.post("/graphql", json={"query": query}, headers=admin_headers) assert response.status_code == 200 assert "errors" not in response.json() @@ -52,7 +50,7 @@ async def test_graphql_endpoint(db: InfrahubDatabase, client, client_headers, de async def test_graphql_endpoint_with_timestamp( - db: InfrahubDatabase, client, client_headers, default_branch: Branch, car_person_data + db: InfrahubDatabase, client, admin_headers, default_branch: Branch, create_test_admin, car_person_data ): time_before = Timestamp() @@ -76,11 +74,7 @@ async def test_graphql_endpoint_with_timestamp( # Must execute in a with block to execute the startup/shutdown events with client: - response = client.post( - "/graphql", - json={"query": query}, - headers=client_headers, - ) + response = client.post("/graphql", json={"query": query}, headers=admin_headers) assert response.status_code == 200 assert "errors" not in response.json() @@ -92,11 +86,7 @@ async def test_graphql_endpoint_with_timestamp( assert sorted(names) == ["Jane", "Johnny"] with client: - response = client.post( - f"/graphql?at={time_before.to_string()}", - json={"query": query}, - headers=client_headers, - ) + response = client.post(f"/graphql?at={time_before.to_string()}", json={"query": query}, headers=admin_headers) assert response.status_code == 200 assert "errors" not in response.json() @@ -221,7 +211,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..18cd52ad63 100644 --- a/backend/tests/unit/api/test_40_schema_api.py +++ b/backend/tests/unit/api/test_40_schema_api.py @@ -4,24 +4,20 @@ from infrahub.core.branch import Branch from infrahub.core.constants import InfrahubKind, SchemaPathType from infrahub.core.initialization import create_branch +from infrahub.core.node import Node 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 +93,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 +117,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 +136,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 +169,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 +190,8 @@ async def test_schema_load_endpoint_valid_simple( client: TestClient, admin_headers, default_branch: Branch, + prefect_test_fixture, + workflow_local, authentication_base, helper, ): @@ -225,11 +222,43 @@ async def test_schema_load_endpoint_valid_simple( assert relationships["tags"] == 7000 +async def test_schema_load_permission_failure( + db: InfrahubDatabase, + client: TestClient, + first_account, + default_branch: Branch, + prefect_test_fixture, + workflow_local, + authentication_base, + helper, +): + token = await Node.init(db=db, schema=InfrahubKind.ACCOUNTTOKEN) + await token.new(db=db, token="unprivileged", account=first_account) + await token.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) + + # Must execute in a with block to execute the startup/shutdown event + with client: + response = client.post( + "/api/schema/load", + headers={"X-INFRAHUB-KEY": "unprivileged"}, + json={"schemas": [helper.schema_file("infra_simple_01.json")]}, + ) + + assert response.status_code == 403 + assert response.json()["errors"][0]["message"] == "You are not allowed to manage the schema" + + async def test_schema_load_restricted_namespace( db: InfrahubDatabase, client: TestClient, admin_headers, default_branch: Branch, + prefect_test_fixture, + workflow_local, authentication_base, helper, ): @@ -249,6 +278,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 +327,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 +358,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 +407,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 +455,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 +476,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 +497,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 +518,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 +562,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 +571,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 +597,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..acdf53fa31 100644 --- a/backend/tests/unit/api/test_menu.py +++ b/backend/tests/unit/api/test_menu.py @@ -1,10 +1,10 @@ -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 -async def test_get_menu( +async def test_get_menu_not_admin( db: InfrahubDatabase, client, client_headers, @@ -12,6 +12,8 @@ async def test_get_menu( car_person_schema_generics: SchemaRoot, car_person_data_generic, ): + await create_default_menu(db=db) + with client: response = client.get( "/api/menu", @@ -20,7 +22,30 @@ async def test_get_menu( assert response.status_code == 200 assert response.json() is not None + data = response.json() + internal_menu_items = [item["identifier"] for item in data["sections"]["internal"]] + assert "BuiltinAdmin" not in internal_menu_items + + +async def test_get_menu_admin( + db: InfrahubDatabase, + client, + admin_headers, + authentication_base, + 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", + headers=admin_headers, + ) - menu = [InterfaceMenu(**menu_item) for menu_item in response.json()] - assert menu[0].title == "Objects" - assert menu[0].children[0].title == "Car" + assert response.status_code == 200 + assert response.json() is not None + data = response.json() + internal_menu_items = [item["identifier"] for item in data["sections"]["internal"]] + assert "BuiltinAdmin" in internal_menu_items diff --git a/backend/tests/unit/conftest.py b/backend/tests/unit/conftest.py index 81d168b0b6..c095db2100 100644 --- a/backend/tests/unit/conftest.py +++ b/backend/tests/unit/conftest.py @@ -6,7 +6,8 @@ import pendulum import pytest -from infrahub_sdk import UUIDT, Config, InfrahubClient +from infrahub_sdk import Config, InfrahubClient +from infrahub_sdk.uuidt import UUIDT from neo4j._codec.hydration.v1 import HydrationHandler from pytest_httpx import HTTPXMock @@ -22,7 +23,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,14 +50,16 @@ 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.services import InfrahubServices, services +from infrahub.services.adapters.workflow.local import WorkflowLocalExecution from tests.helpers.file_repo import FileRepo from tests.helpers.test_client import dummy_async_request +from tests.test_data import dataset01 as ds01 @pytest.fixture(scope="module", autouse=True) @@ -1650,6 +1660,7 @@ async def all_attribute_types_schema( "attributes": [ {"name": "name", "kind": "Text", "optional": True}, {"name": "mystring", "kind": "Text", "optional": True}, + {"name": "mytextarea", "kind": "TextArea", "optional": True}, {"name": "mybool", "kind": "Boolean", "optional": True}, {"name": "myint", "kind": "Number", "optional": True}, {"name": "mylist", "kind": "List", "optional": True}, @@ -2454,11 +2465,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 +2538,43 @@ 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, action=GlobalPermissions.SUPER_ADMIN.value, decision=PermissionDecision.ALLOW_ALL.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, + namespace="*", + name="*", + action=PermissionAction.ANY.value, + decision=PermissionDecision.ALLOW_ALL.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 +2608,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) @@ -2568,6 +2622,14 @@ async def second_account(db: InfrahubDatabase, data_schema, node_group_schema, r return obj +@pytest.fixture +async def session_second_account(db: InfrahubDatabase, second_account) -> AccountSession: + session = AccountSession( + authenticated=True, auth_type=AuthType.API, account_id=second_account.id, role="read-write" + ) + return session + + @pytest.fixture async def repos_in_main(db: InfrahubDatabase, register_core_models_schema): repo01 = await Node.init(db=db, schema=InfrahubKind.REPOSITORY) @@ -2867,3 +2929,23 @@ async def prefix_pool_01( ip_dataset_prefix_v4["prefix_pool"] = prefix_pool return ip_dataset_prefix_v4 + + +@pytest.fixture() +def workflow_local(): + original = config.OVERRIDE.workflow + workflow = WorkflowLocalExecution() + config.OVERRIDE.workflow = workflow + yield workflow + config.OVERRIDE.workflow = original + + +@pytest.fixture +def init_service(db: InfrahubDatabase): + original = services.service + database = db + workflow = WorkflowLocalExecution() + service = InfrahubServices(database=database, workflow=workflow) + services.service = service + yield service + services.service = original diff --git a/backend/tests/unit/core/constraint_validators/test_determiner.py b/backend/tests/unit/core/constraint_validators/test_determiner.py index 3156ae189d..9cccca7bac 100644 --- a/backend/tests/unit/core/constraint_validators/test_determiner.py +++ b/backend/tests/unit/core/constraint_validators/test_determiner.py @@ -1,10 +1,9 @@ import pytest -from infrahub_sdk.diff import NodeDiff from infrahub.core import registry from infrahub.core.branch import Branch -from infrahub.core.constants import DiffAction, SchemaPathType -from infrahub.core.diff.model.diff import DiffElementType +from infrahub.core.constants import SchemaPathType +from infrahub.core.diff.model.path import NodeDiffFieldSummary from infrahub.core.models import SchemaUpdateConstraintInfo from infrahub.core.node import Node from infrahub.core.path import SchemaPath @@ -14,22 +13,8 @@ @pytest.fixture def person_name_node_diff( person_john_main: Node, default_branch: Branch -) -> tuple[NodeDiff, set[SchemaUpdateConstraintInfo]]: - node_diff = { - "branch": default_branch.name, - "kind": "TestPerson", - "id": person_john_main.id, - "action": DiffAction.UPDATED.value, - "display_label": "Person John Main Display Label", - "elements": [ - { - "name": "name", - "element_type": DiffElementType.ATTRIBUTE.value, - "action": DiffAction.UPDATED.value, - "summary": {"added": 0, "updated": 1, "removed": 0}, - } - ], - } +) -> tuple[NodeDiffFieldSummary, set[SchemaUpdateConstraintInfo]]: + node_diff = NodeDiffFieldSummary(kind="TestPerson", attribute_names={"name"}) schema_updated_constraint_infos = { SchemaUpdateConstraintInfo( path=SchemaPath( @@ -84,26 +69,8 @@ def person_name_node_diff( @pytest.fixture def person_cars_node_diff( person_john_main: Node, default_branch: Branch -) -> tuple[NodeDiff, set[SchemaUpdateConstraintInfo]]: - node_diff = { - "branch": default_branch.name, - "kind": "TestPerson", - "id": person_john_main.id, - "action": DiffAction.UPDATED.value, - "display_label": "Person John Main Display Label", - "elements": [ - { - "name": "cars", - "element_type": DiffElementType.RELATIONSHIP_MANY.value, - "action": DiffAction.UPDATED.value, - "summary": {"added": 0, "updated": 1, "removed": 0}, - "peers": [ - {"action": DiffAction.REMOVED.value, "summary": {"added": 0, "updated": 0, "removed": 1}}, - {"action": DiffAction.ADDED.value, "summary": {"added": 1, "updated": 0, "removed": 0}}, - ], - } - ], - } +) -> tuple[NodeDiffFieldSummary, set[SchemaUpdateConstraintInfo]]: + node_diff = NodeDiffFieldSummary(kind="TestPerson", relationship_names={"cars"}) schema_updated_constraint_infos = { SchemaUpdateConstraintInfo( constraint_name="relationship.min_count.update", diff --git a/backend/tests/unit/core/constraint_validators/test_relationship_count.py b/backend/tests/unit/core/constraint_validators/test_relationship_count.py index ed92a51f3f..bcbf362255 100644 --- a/backend/tests/unit/core/constraint_validators/test_relationship_count.py +++ b/backend/tests/unit/core/constraint_validators/test_relationship_count.py @@ -48,7 +48,7 @@ async def test_query_failure_cardinality_one( ): person_schema = registry.schema.get(name="TestPerson") cars_rel = person_schema.get_relationship(name="cars") - cars_rel.cardinality = RelationshipCardinality.ONE + cars_rel.max_count = 1 schema_path = SchemaPath(path_type=SchemaPathType.RELATIONSHIP, schema_kind="TestPerson", field_name="cars") query = await RelationshipCountUpdateValidatorQuery.init( @@ -351,6 +351,88 @@ async def test_query_delete_on_branch_success( assert len(all_paths) == 0 +async def test_hierarchical_success(db: InfrahubDatabase, default_branch: Branch, hierarchical_location_data_simple): + site_schema = registry.schema.get(name="LocationSite", duplicate=False) + + schema_path = SchemaPath(path_type=SchemaPathType.RELATIONSHIP, schema_kind="LocationSite", field_name="parent") + query = await RelationshipCountUpdateValidatorQuery.init( + db=db, branch=default_branch, node_schema=site_schema, schema_path=schema_path + ) + + await query.execute(db=db) + + grouped_paths = await query.get_paths() + all_paths = grouped_paths.get_all_data_paths() + assert len(all_paths) == 0 + + +async def test_hierarchical_failure(db: InfrahubDatabase, default_branch: Branch, hierarchical_location_data_simple): + paris_site = hierarchical_location_data_simple["paris"] + branch = await create_branch(branch_name=str("branch2"), db=db) + schema_path = SchemaPath(path_type=SchemaPathType.RELATIONSHIP, schema_kind="LocationSite", field_name="children") + site_schema = registry.schema.get(name="LocationSite", branch=branch, duplicate=False) + + # check no violations to start with + query = await RelationshipCountUpdateValidatorQuery.init( + db=db, branch=branch, node_schema=site_schema, schema_path=schema_path + ) + + await query.execute(db=db) + grouped_paths = await query.get_paths() + all_paths = grouped_paths.get_all_data_paths() + assert len(all_paths) == 0 + + # add a violation + branch_rack = await NodeManager.get_one(db=db, branch=branch, id=paris_site.id) + extra_rack = await Node.init(db=db, branch=branch, schema="LocationRack") + await extra_rack.new(db=db, name="extra_rack", parent=branch_rack, status="online") + await extra_rack.save(db=db) + child_rel = site_schema.get_relationship(name="children") + child_rel.max_count = 2 + + query = await RelationshipCountUpdateValidatorQuery.init( + db=db, branch=branch, node_schema=site_schema, schema_path=schema_path + ) + await query.execute(db=db) + grouped_paths = await query.get_paths() + all_paths = grouped_paths.get_all_data_paths() + assert len(all_paths) == 2 + assert ( + DataPath( + branch=branch.name, + path_type=PathType.NODE, + node_id=paris_site.id, + kind="LocationSite", + field_name="children", + value=1, + ) + in all_paths + ) + assert ( + DataPath( + branch=default_branch.name, + path_type=PathType.NODE, + node_id=paris_site.id, + kind="LocationSite", + field_name="children", + value=2, + ) + in all_paths + ) + + # remove violation + branch_rack = await NodeManager.get_one(db=db, branch=branch, id=extra_rack.id) + await branch_rack.delete(db=db) + + query = await RelationshipCountUpdateValidatorQuery.init( + db=db, branch=branch, node_schema=site_schema, schema_path=schema_path + ) + await query.execute(db=db) + grouped_paths = await query.get_paths() + all_paths = grouped_paths.get_all_data_paths() + assert len(all_paths) == 0 + + async def test_validator( db: InfrahubDatabase, branch: Branch, diff --git a/backend/tests/unit/core/constraint_validators/test_relationship_peer_update.py b/backend/tests/unit/core/constraint_validators/test_relationship_peer_update.py index 33959afb4d..1dc294b7dd 100644 --- a/backend/tests/unit/core/constraint_validators/test_relationship_peer_update.py +++ b/backend/tests/unit/core/constraint_validators/test_relationship_peer_update.py @@ -316,9 +316,6 @@ async def test_query_update_on_branch_success( g_car = await Node.init(db=db, schema="TestGazCar", branch=default_branch) await g_car.new(db=db, name="GCar", nbr_seats=3, mpg=29, owner=p_1) await g_car.save(db=db) - await p_1.cars.get_relationships(db=db) - await p_1.cars.update(db=db, data=[*p_1.cars, g_car]) - await p_1.save(db=db) await branch.rebase(db=db) p_1 = await NodeManager.get_one(db=db, id=p_1.id, branch=branch) 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_coordinator.py b/backend/tests/unit/core/diff/test_coordinator.py index c23b40c08d..1e73123792 100644 --- a/backend/tests/unit/core/diff/test_coordinator.py +++ b/backend/tests/unit/core/diff/test_coordinator.py @@ -1,10 +1,7 @@ -from unittest.mock import AsyncMock - from infrahub.core.branch import Branch from infrahub.core.constants import DiffAction from infrahub.core.constants.database import DatabaseEdgeType from infrahub.core.diff.coordinator import DiffCoordinator -from infrahub.core.diff.data_check_synchronizer import DiffDataCheckSynchronizer from infrahub.core.initialization import create_branch from infrahub.core.manager import NodeManager from infrahub.core.node import Node @@ -24,8 +21,6 @@ async def test_node_deleted_after_branching( component_registry = get_component_registry() diff_coordinator = await component_registry.get_component(DiffCoordinator, db=db, branch=branch) - mock_synchronizer = AsyncMock(spec=DiffDataCheckSynchronizer) - diff_coordinator.data_check_synchronizer = mock_synchronizer diff = await diff_coordinator.update_branch_diff(base_branch=default_branch, diff_branch=branch) assert diff.base_branch_name == default_branch.name diff --git a/backend/tests/unit/core/diff/test_coordinator_lock.py b/backend/tests/unit/core/diff/test_coordinator_lock.py index a83e10c405..21aeb4ad1c 100644 --- a/backend/tests/unit/core/diff/test_coordinator_lock.py +++ b/backend/tests/unit/core/diff/test_coordinator_lock.py @@ -7,7 +7,6 @@ from infrahub import config, lock from infrahub.core.branch import Branch from infrahub.core.diff.coordinator import DiffCoordinator -from infrahub.core.diff.data_check_synchronizer import DiffDataCheckSynchronizer from infrahub.core.initialization import create_branch from infrahub.core.node import Node from infrahub.core.timestamp import Timestamp @@ -35,8 +34,6 @@ async def get_diff_coordinator(self, db: InfrahubDatabase, diff_branch: Branch) config.SETTINGS.database.max_depth_search_hierarchy = 10 component_registry = get_component_registry() diff_coordinator = await component_registry.get_component(DiffCoordinator, db=db, branch=diff_branch) - mock_synchronizer = AsyncMock(spec=DiffDataCheckSynchronizer) - diff_coordinator.data_check_synchronizer = mock_synchronizer wrapped_repo = AsyncMock(wraps=diff_coordinator.diff_repo) diff_coordinator.diff_repo = wrapped_repo wrapped_calculator = AsyncMock(wraps=diff_coordinator.diff_calculator) @@ -69,13 +66,13 @@ async def test_arbitrary_diff_locks_queue_up( diff_coordinator.create_or_update_arbitrary_timeframe_diff( base_branch=default_branch, diff_branch=diff_branch, - from_time=Timestamp(branch_with_data.created_at), + from_time=Timestamp(branch_with_data.branched_from), to_time=Timestamp(), ), diff_coordinator.create_or_update_arbitrary_timeframe_diff( base_branch=default_branch, diff_branch=diff_branch, - from_time=Timestamp(branch_with_data.created_at), + from_time=Timestamp(branch_with_data.branched_from), to_time=Timestamp(), ), ) @@ -102,7 +99,7 @@ async def test_arbitrary_diff_blocks_incremental_diff( diff_coordinator.create_or_update_arbitrary_timeframe_diff( base_branch=default_branch, diff_branch=diff_branch, - from_time=Timestamp(branch_with_data.created_at), + from_time=Timestamp(branch_with_data.branched_from), to_time=Timestamp(), ), diff_coordinator.update_branch_diff(base_branch=default_branch, diff_branch=diff_branch), @@ -133,7 +130,7 @@ async def test_incremental_diff_blocks_arbitrary_diff( diff_coordinator.create_or_update_arbitrary_timeframe_diff( base_branch=default_branch, diff_branch=diff_branch, - from_time=Timestamp(branch_with_data.created_at), + from_time=Timestamp(branch_with_data.branched_from), to_time=Timestamp(), ), ) diff --git a/backend/tests/unit/core/diff/test_diff_and_merge.py b/backend/tests/unit/core/diff/test_diff_and_merge.py new file mode 100644 index 0000000000..b712ab8ded --- /dev/null +++ b/backend/tests/unit/core/diff/test_diff_and_merge.py @@ -0,0 +1,247 @@ +from unittest.mock import AsyncMock + +import pytest + +from infrahub.core import registry +from infrahub.core.branch import Branch +from infrahub.core.diff.coordinator import DiffCoordinator +from infrahub.core.diff.data_check_synchronizer import DiffDataCheckSynchronizer +from infrahub.core.diff.merger.merger import DiffMerger +from infrahub.core.diff.model.path import ConflictSelection +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.schema.attribute_schema import AttributeSchema +from infrahub.core.schema.node_schema import NodeSchema +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 + + +class TestDiffAndMerge: + @pytest.fixture + async def diff_repository(self, db: InfrahubDatabase, default_branch: Branch) -> DiffRepository: + component_registry = get_component_registry() + return await component_registry.get_component(DiffRepository, db=db, branch=default_branch) + + async def _get_diff_coordinator(self, db: InfrahubDatabase, branch: Branch) -> DiffCoordinator: + component_registry = get_component_registry() + diff_coordinator = await component_registry.get_component(DiffCoordinator, db=db, branch=branch) + diff_coordinator.data_check_synchronizer = AsyncMock(spec=DiffDataCheckSynchronizer) + return diff_coordinator + + async def _get_diff_merger(self, db: InfrahubDatabase, branch: Branch) -> DiffMerger: + component_registry = get_component_registry() + return await component_registry.get_component(DiffMerger, db=db, branch=branch) + + async def test_diff_and_merge_with_list_attribute( + self, db: InfrahubDatabase, default_branch: Branch, all_attribute_types_schema: NodeSchema + ): + new_node = await Node.init(db=db, schema=all_attribute_types_schema.kind) + await new_node.new(db=db, mylist=["a", "b", 1, 2]) + await new_node.save(db=db) + branch2 = await create_branch(db=db, branch_name="branch2") + branch_node = await NodeManager.get_one(db=db, branch=branch2, id=new_node.id) + branch_node.mylist.value = ["c", "d", 3, 4] + await branch_node.save(db=db) + diff_coordinator = await self._get_diff_coordinator(db=db, branch=branch2) + await diff_coordinator.update_branch_diff(base_branch=default_branch, diff_branch=branch2) + diff_merger = await self._get_diff_merger(db=db, branch=branch2) + await diff_merger.merge_graph(at=Timestamp()) + + updated_node = await NodeManager.get_one(db=db, branch=default_branch, id=new_node.id) + assert updated_node.mylist.value == ["c", "d", 3, 4] + + async def test_diff_and_merge_schema_with_default_values( + self, db: InfrahubDatabase, default_branch: Branch, register_core_models_schema, car_person_schema: SchemaBranch + ): + schema_main = registry.schema.get_schema_branch(name=default_branch.name) + await registry.schema.update_schema_branch( + db=db, branch=default_branch, schema=schema_main, limit=["TestCar", "TestPerson"], update_db=True + ) + branch2 = await create_branch(db=db, branch_name="branch2") + schema_branch = registry.schema.get_schema_branch(name=branch2.name) + schema_branch.duplicate() + car_schema_branch = schema_branch.get(name="TestCar") + car_schema_branch.attributes.append(AttributeSchema(name="num_cupholders", kind="Number", default_value=15)) + car_schema_branch.attributes.append(AttributeSchema(name="is_cool", kind="Boolean", default_value=False)) + car_schema_branch.attributes.append(AttributeSchema(name="nickname", kind="Text", default_value="car")) + schema_branch.set(name="TestCar", schema=car_schema_branch) + schema_branch.process() + await registry.schema.update_schema_branch( + db=db, branch=branch2, schema=schema_branch, limit=["TestCar", "TestPerson"], update_db=True + ) + + diff_coordinator = await self._get_diff_coordinator(db=db, branch=branch2) + await diff_coordinator.update_branch_diff(base_branch=default_branch, diff_branch=branch2) + diff_merger = await self._get_diff_merger(db=db, branch=branch2) + await diff_merger.merge_graph(at=Timestamp()) + + updated_schema = await registry.schema.load_schema_from_db(db=db, branch=default_branch) + car_schema_main = updated_schema.get(name="TestCar", duplicate=False) + new_int_attr = car_schema_main.get_attribute(name="num_cupholders") + assert new_int_attr.default_value == 15 + new_bool_attr = car_schema_main.get_attribute(name="is_cool") + assert new_bool_attr.default_value is False + new_str_attr = car_schema_main.get_attribute(name="nickname") + assert new_str_attr.default_value == "car" + + @pytest.mark.parametrize( + "conflict_selection,expected_value", + [(ConflictSelection.BASE_BRANCH, "John-main"), (ConflictSelection.DIFF_BRANCH, "John-branch")], + ) + async def test_diff_and_merge_with_attribute_value_conflict( + self, + db: InfrahubDatabase, + default_branch: Branch, + diff_repository: DiffRepository, + person_john_main, + person_jane_main, + person_alfred_main, + car_accord_main, + conflict_selection, + expected_value, + ): + branch2 = await create_branch(db=db, branch_name="branch2") + john_main = await NodeManager.get_one(db=db, id=person_john_main.id) + john_main.name.value = "John-main" + await john_main.save(db=db) + john_branch = await NodeManager.get_one(db=db, branch=branch2, id=person_john_main.id) + john_branch.name.value = "John-branch" + await john_branch.save(db=db) + + diff_coordinator = await self._get_diff_coordinator(db=db, branch=branch2) + enriched_diff = await diff_coordinator.update_branch_diff(base_branch=default_branch, diff_branch=branch2) + conflicts_map = enriched_diff.get_all_conflicts() + assert len(conflicts_map) == 1 + conflict = next(iter(conflicts_map.values())) + await diff_repository.update_conflict_by_id(conflict_id=conflict.uuid, selection=conflict_selection) + diff_merger = await self._get_diff_merger(db=db, branch=branch2) + await diff_merger.merge_graph(at=Timestamp()) + + updated_john = await NodeManager.get_one(db=db, id=person_john_main.id) + assert updated_john.name.value == expected_value + + @pytest.mark.parametrize( + "conflict_selection", + [ConflictSelection.BASE_BRANCH, ConflictSelection.DIFF_BRANCH], + ) + async def test_diff_and_merge_with_relationship_conflict( + self, + db: InfrahubDatabase, + default_branch: Branch, + diff_repository: DiffRepository, + person_john_main, + person_jane_main, + person_alfred_main, + car_accord_main, + car_camry_main, + conflict_selection, + ): + branch2 = await create_branch(db=db, branch_name="branch2") + car_main = await NodeManager.get_one(db=db, id=car_accord_main.id) + await car_main.owner.update(db=db, data=person_alfred_main) + await car_main.save(db=db) + car_branch = await NodeManager.get_one(db=db, branch=branch2, id=car_accord_main.id) + await car_branch.owner.update(db=db, data=person_jane_main) + await car_branch.save(db=db) + + diff_coordinator = await self._get_diff_coordinator(db=db, branch=branch2) + enriched_diff = await diff_coordinator.update_branch_diff(base_branch=default_branch, diff_branch=branch2) + conflicts_map = enriched_diff.get_all_conflicts() + assert len(conflicts_map) == 1 + conflict = next(iter(conflicts_map.values())) + await diff_repository.update_conflict_by_id(conflict_id=conflict.uuid, selection=conflict_selection) + diff_merger = await self._get_diff_merger(db=db, branch=branch2) + await diff_merger.merge_graph(at=Timestamp()) + + updated_car = await NodeManager.get_one(db=db, id=car_accord_main.id) + owner_rel = await updated_car.owner.get(db=db) + if conflict_selection is ConflictSelection.BASE_BRANCH: + assert owner_rel.peer_id == person_alfred_main.id + if conflict_selection is ConflictSelection.DIFF_BRANCH: + assert owner_rel.peer_id == person_jane_main.id + + @pytest.mark.parametrize( + "conflict_selection", + [ConflictSelection.BASE_BRANCH, ConflictSelection.DIFF_BRANCH], + ) + async def test_diff_and_merge_with_attribute_property_conflict( + self, + db: InfrahubDatabase, + default_branch: Branch, + diff_repository: DiffRepository, + person_john_main, + person_jane_main, + person_alfred_main, + car_accord_main, + conflict_selection, + ): + branch2 = await create_branch(db=db, branch_name="branch2") + john_main = await NodeManager.get_one(db=db, id=person_john_main.id) + john_main.name.source = person_alfred_main + await john_main.save(db=db) + john_branch = await NodeManager.get_one(db=db, branch=branch2, id=person_john_main.id) + john_branch.name.source = person_jane_main + await john_branch.save(db=db) + + diff_coordinator = await self._get_diff_coordinator(db=db, branch=branch2) + enriched_diff = await diff_coordinator.update_branch_diff(base_branch=default_branch, diff_branch=branch2) + conflicts_map = enriched_diff.get_all_conflicts() + assert len(conflicts_map) == 1 + conflict = next(iter(conflicts_map.values())) + await diff_repository.update_conflict_by_id(conflict_id=conflict.uuid, selection=conflict_selection) + diff_merger = await self._get_diff_merger(db=db, branch=branch2) + await diff_merger.merge_graph(at=Timestamp()) + + updated_john = await NodeManager.get_one(db=db, id=person_john_main.id, include_source=True) + + attr_source = await updated_john.name.get_source(db=db) + if conflict_selection is ConflictSelection.BASE_BRANCH: + assert attr_source.id == person_alfred_main.id + if conflict_selection is ConflictSelection.DIFF_BRANCH: + assert attr_source.id == person_jane_main.id + + @pytest.mark.parametrize( + "conflict_selection", + [ConflictSelection.BASE_BRANCH, ConflictSelection.DIFF_BRANCH], + ) + async def test_diff_and_merge_with_relationship_property_conflict( + self, + db: InfrahubDatabase, + default_branch: Branch, + diff_repository: DiffRepository, + person_john_main, + person_jane_main, + person_alfred_main, + car_accord_main, + car_camry_main, + conflict_selection, + ): + branch2 = await create_branch(db=db, branch_name="branch2") + car_main = await NodeManager.get_one(db=db, id=car_accord_main.id) + await car_main.owner.update(db=db, data={"id": person_john_main.id, "_relation__owner": person_alfred_main.id}) + await car_main.save(db=db) + car_branch = await NodeManager.get_one(db=db, branch=branch2, id=car_accord_main.id) + await car_branch.owner.update(db=db, data={"id": person_john_main.id, "_relation__owner": person_jane_main.id}) + await car_branch.save(db=db) + + diff_coordinator = await self._get_diff_coordinator(db=db, branch=branch2) + enriched_diff = await diff_coordinator.update_branch_diff(base_branch=default_branch, diff_branch=branch2) + conflicts_map = enriched_diff.get_all_conflicts() + # conflict on both sides of the relationship + assert len(conflicts_map) == 2 + for conflict in conflicts_map.values(): + await diff_repository.update_conflict_by_id(conflict_id=conflict.uuid, selection=conflict_selection) + diff_merger = await self._get_diff_merger(db=db, branch=branch2) + await diff_merger.merge_graph(at=Timestamp()) + + updated_car = await NodeManager.get_one(db=db, id=car_accord_main.id, include_owner=True) + owner_rel = await updated_car.owner.get(db=db) + owner_prop = await owner_rel.get_owner(db=db) + if conflict_selection is ConflictSelection.BASE_BRANCH: + assert owner_prop.id == person_alfred_main.id + if conflict_selection is ConflictSelection.DIFF_BRANCH: + assert owner_prop.id == person_jane_main.id diff --git a/backend/tests/unit/core/diff/test_diff_calculator.py b/backend/tests/unit/core/diff/test_diff_calculator.py index a9c4a3d295..affc26a07b 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 @@ -693,7 +693,7 @@ async def test_relationship_one_property_branch_update( single_relationship = single_relationships_by_peer_id[person_john_main.id] assert single_relationship.peer_id == person_john_main.id assert single_relationship.action is DiffAction.REMOVED - assert len(single_relationship.properties) == 3 + assert len(single_relationship.properties) == 2 assert before_main_change < single_relationship.changed_at < after_main_change property_diff_by_type = {p.property_type: p for p in single_relationship.properties} property_diff = property_diff_by_type[DatabaseEdgeType.IS_RELATED] @@ -708,12 +708,6 @@ async def test_relationship_one_property_branch_update( assert property_diff.new_value is None assert property_diff.action is DiffAction.REMOVED assert before_main_change < property_diff.changed_at < after_main_change - property_diff = property_diff_by_type[DatabaseEdgeType.IS_PROTECTED] - assert property_diff.property_type == DatabaseEdgeType.IS_PROTECTED - assert property_diff.previous_value is False - assert property_diff.new_value is None - assert property_diff.action is DiffAction.REMOVED - assert before_main_change < property_diff.changed_at < after_main_change async def test_add_node_branch( @@ -1293,7 +1287,7 @@ async def test_agnostic_owner_relationship_added( branch_root_path = calculated_diffs.diff_branch_diff assert branch_root_path.branch == branch.name diff_nodes_by_id = {n.uuid: n for n in branch_root_path.nodes} - assert set(diff_nodes_by_id.keys()) == {new_car.get_id()} + assert set(diff_nodes_by_id.keys()) == {new_car.get_id(), person_1.get_id()} diff_node_car = diff_nodes_by_id[new_car.get_id()] assert diff_node_car.action is DiffAction.ADDED assert {(attr.name, attr.action) for attr in diff_node_car.attributes} == { @@ -1326,6 +1320,81 @@ async def test_agnostic_owner_relationship_added( (DatabaseEdgeType.IS_PROTECTED, DiffAction.ADDED, None, False), (DatabaseEdgeType.IS_VISIBLE, DiffAction.ADDED, None, True), } + diff_node_person = diff_nodes_by_id[person_1.get_id()] + assert diff_node_person.action is DiffAction.UPDATED + assert len(diff_node_person.attributes) == 0 + assert len(diff_node_person.relationships) == 1 + diff_relationship = diff_node_person.relationships.pop() + assert diff_relationship.name == "cars" + assert diff_relationship.action is DiffAction.UPDATED + assert len(diff_relationship.relationships) == 1 + diff_element = diff_relationship.relationships.pop() + assert diff_element.peer_id == new_car.get_id() + assert diff_element.action is DiffAction.ADDED + + diff_props_by_type = {p.property_type: p for p in diff_element.properties} + assert set(diff_props_by_type.keys()) == { + DatabaseEdgeType.IS_RELATED, + DatabaseEdgeType.HAS_OWNER, + DatabaseEdgeType.IS_PROTECTED, + DatabaseEdgeType.IS_VISIBLE, + } + diff_prop_tuples = { + (diff_prop.property_type, diff_prop.action, diff_prop.previous_value, diff_prop.new_value) + for diff_prop in diff_props_by_type.values() + } + assert diff_prop_tuples == { + (DatabaseEdgeType.IS_RELATED, DiffAction.ADDED, None, new_car.get_id()), + (DatabaseEdgeType.HAS_OWNER, DiffAction.ADDED, None, person_1.get_id()), + (DatabaseEdgeType.IS_PROTECTED, DiffAction.ADDED, None, False), + (DatabaseEdgeType.IS_VISIBLE, DiffAction.ADDED, None, True), + } + + +async def test_update_attribute_under_agnostic_node( + db: InfrahubDatabase, + default_branch: Branch, + fruit_tag_schema_global, +): + branch = await create_branch(db=db, branch_name="branch") + from_time = Timestamp(branch.created_at) + fruit_1 = await Node.init(db=db, schema="GardenFruit", branch=branch) + await fruit_1.new(db=db, name="blueberry", branch_aware_attr="branchval") + await fruit_1.save(db=db) + + diff_calculator = DiffCalculator(db=db) + calculated_diffs = await diff_calculator.calculate_diff( + base_branch=default_branch, diff_branch=branch, from_time=from_time, to_time=Timestamp() + ) + + base_root_path = calculated_diffs.base_branch_diff + assert base_root_path.nodes == [] + branch_root_path = calculated_diffs.diff_branch_diff + assert branch_root_path.branch == branch.name + diff_nodes_by_id = {n.uuid: n for n in branch_root_path.nodes} + assert set(diff_nodes_by_id.keys()) == {fruit_1.get_id()} + diff_node_fruit = diff_nodes_by_id[fruit_1.get_id()] + assert diff_node_fruit.action is DiffAction.UPDATED + assert len(diff_node_fruit.relationships) == 0 + assert len(diff_node_fruit.attributes) == 1 + attr_diff = diff_node_fruit.attributes.pop() + assert attr_diff.name == "branch_aware_attr" + assert attr_diff.action is DiffAction.ADDED + properties_by_type = {p.property_type: p for p in attr_diff.properties} + assert set(properties_by_type.keys()) == { + DatabaseEdgeType.HAS_VALUE, + DatabaseEdgeType.IS_VISIBLE, + DatabaseEdgeType.IS_PROTECTED, + } + for property_type, new_value in ( + (DatabaseEdgeType.HAS_VALUE, "branchval"), + (DatabaseEdgeType.IS_VISIBLE, True), + (DatabaseEdgeType.IS_PROTECTED, False), + ): + prop_diff = properties_by_type[property_type] + assert prop_diff.action is DiffAction.ADDED + assert prop_diff.previous_value is None + assert prop_diff.new_value == new_value async def test_diff_attribute_branch_update_with_previous_base_update_ignored( 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..3b861bf949 --- /dev/null +++ b/backend/tests/unit/core/diff/test_diff_merger.py @@ -0,0 +1,670 @@ +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.constants.database import DatabaseEdgeType +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.schema.schema_branch import SchemaBranch +from infrahub.core.timestamp import Timestamp +from infrahub.database import InfrahubDatabase +from infrahub.exceptions import NodeNotFoundError + +from .factories import ( + EnrichedAttributeFactory, + EnrichedConflictFactory, + EnrichedNodeFactory, + EnrichedPropertyFactory, + EnrichedRelationshipElementFactory, + EnrichedRelationshipGroupFactory, + 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, + car_person_schema: SchemaBranch, + ) -> DiffMerger: + db.add_schema(car_person_schema) + db.add_schema(car_person_schema, name=source_branch.name) + return DiffMerger( + db=db, + source_branch=source_branch, + destination_branch=default_branch, + diff_repository=mock_diff_repository, + serializer=DiffMergeSerializer(db=db, max_batch_size=50), + ) + + @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) + new_node.height.is_protected = True + await new_node.save(db=db) + return new_node + + @pytest.fixture + async def car_node_branch( + self, db: InfrahubDatabase, source_branch: Branch, person_node_branch: Node, car_person_schema + ) -> Node: + new_node = await Node.init(db=db, schema="TestCar", branch=source_branch) + await new_node.new(db=db, name="El Camino", color="black", nbr_seats=5, owner=person_node_branch) + 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 + async def person_node_main2(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="Jermaine", height=165) + await new_node.save(db=db) + return new_node + + @pytest.fixture + async def car_node_main( + self, db: InfrahubDatabase, default_branch: Branch, person_node_main: Node, car_person_schema + ) -> Node: + new_node = await Node.init(db=db, schema="TestCar", branch=default_branch) + await new_node.new(db=db, name="El Camino", color="black", nbr_seats=5, owner=person_node_main) + await new_node.save(db=db) + return new_node + + @pytest.fixture + async def car_node_main2( + self, db: InfrahubDatabase, default_branch: Branch, person_node_main2: Node, car_person_schema + ) -> Node: + new_node = await Node.init(db=db, schema="TestCar", branch=default_branch) + await new_node.new(db=db, name="Civic", color="purple", nbr_seats=5, owner=person_node_main2) + 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(), + ) + + @pytest.fixture + def deleted_person_node_diff(self, person_node_main, car_node_main) -> EnrichedDiffNode: + deleted_node_diff = self._get_empty_node_diff(node=person_node_main, action=DiffAction.REMOVED) + # attributes + deleted_height_value_property = EnrichedPropertyFactory.build( + property_type=DatabaseEdgeType.HAS_VALUE, + previous_value=str(person_node_main.height.value), + new_value=None, + action=DiffAction.REMOVED, + conflict=None, + ) + deleted_height_owner_property = EnrichedPropertyFactory.build( + property_type=DatabaseEdgeType.HAS_OWNER, + previous_value=person_node_main.id, + new_value=None, + action=DiffAction.REMOVED, + conflict=None, + ) + deleted_height_visible_property = EnrichedPropertyFactory.build( + property_type=DatabaseEdgeType.IS_VISIBLE, + previous_value="True", + new_value=None, + action=DiffAction.REMOVED, + conflict=None, + ) + deleted_height_protected_property = EnrichedPropertyFactory.build( + property_type=DatabaseEdgeType.IS_PROTECTED, + previous_value="False", + new_value=None, + action=DiffAction.REMOVED, + conflict=None, + ) + deleted_attribute_diff = EnrichedAttributeFactory.build( + name="height", + action=DiffAction.REMOVED, + properties={ + deleted_height_value_property, + deleted_height_owner_property, + deleted_height_visible_property, + deleted_height_protected_property, + }, + ) + deleted_node_diff.attributes = {deleted_attribute_diff} + # relationships + deleted_rel_is_visible_property = EnrichedPropertyFactory.build( + property_type=DatabaseEdgeType.IS_VISIBLE, + previous_value="True", + new_value=None, + action=DiffAction.REMOVED, + conflict=None, + ) + deleted_rel_is_protected_property = EnrichedPropertyFactory.build( + property_type=DatabaseEdgeType.IS_PROTECTED, + previous_value="False", + new_value=None, + action=DiffAction.REMOVED, + conflict=None, + ) + deleted_rel_source_property = EnrichedPropertyFactory.build( + property_type=DatabaseEdgeType.HAS_SOURCE, + previous_value=car_node_main.id, + new_value=None, + action=DiffAction.REMOVED, + conflict=None, + ) + deleted_is_related_property = EnrichedPropertyFactory.build( + property_type=DatabaseEdgeType.IS_RELATED, + previous_value=car_node_main.id, + new_value=None, + action=DiffAction.REMOVED, + conflict=None, + ) + deleted_relationship_element = EnrichedRelationshipElementFactory.build( + action=DiffAction.REMOVED, + peer_id=car_node_main.id, + conflict=None, + properties={ + deleted_is_related_property, + deleted_rel_is_visible_property, + deleted_rel_is_protected_property, + deleted_rel_source_property, + }, + ) + deleted_relationship = EnrichedRelationshipGroupFactory.build( + name="cars", + relationships={deleted_relationship_element}, + action=DiffAction.UPDATED, + ) + deleted_node_diff.relationships = {deleted_relationship} + return deleted_node_diff + + @pytest.fixture + def added_person_node_diff(self, person_node_branch, car_node_branch) -> EnrichedDiffNode: + added_node_diff = self._get_empty_node_diff(node=person_node_branch, action=DiffAction.ADDED) + # attributes + added_height_value_property = EnrichedPropertyFactory.build( + property_type=DatabaseEdgeType.HAS_VALUE, + previous_value=None, + new_value=str(person_node_branch.height.value), + action=DiffAction.ADDED, + conflict=None, + ) + added_height_owner_property = EnrichedPropertyFactory.build( + property_type=DatabaseEdgeType.HAS_OWNER, + previous_value=None, + new_value=person_node_branch.id, + action=DiffAction.ADDED, + conflict=None, + ) + added_height_visible_property = EnrichedPropertyFactory.build( + property_type=DatabaseEdgeType.IS_VISIBLE, + previous_value=None, + new_value="True", + action=DiffAction.ADDED, + conflict=None, + ) + added_height_protected_property = EnrichedPropertyFactory.build( + property_type=DatabaseEdgeType.IS_PROTECTED, + previous_value=None, + new_value="True", + action=DiffAction.ADDED, + conflict=None, + ) + added_attribute_diff = EnrichedAttributeFactory.build( + name="height", + action=DiffAction.ADDED, + properties={ + added_height_value_property, + added_height_owner_property, + added_height_visible_property, + added_height_protected_property, + }, + ) + added_node_diff.attributes = {added_attribute_diff} + # relationships + added_rel_is_visible_property = EnrichedPropertyFactory.build( + property_type=DatabaseEdgeType.IS_VISIBLE, + previous_value=None, + new_value="True", + action=DiffAction.ADDED, + conflict=None, + ) + added_rel_is_protected_property = EnrichedPropertyFactory.build( + property_type=DatabaseEdgeType.IS_PROTECTED, + previous_value=None, + new_value="False", + action=DiffAction.ADDED, + conflict=None, + ) + added_rel_source_property = EnrichedPropertyFactory.build( + property_type=DatabaseEdgeType.HAS_SOURCE, + previous_value=None, + new_value=car_node_branch.id, + action=DiffAction.ADDED, + conflict=None, + ) + added_is_related_property = EnrichedPropertyFactory.build( + property_type=DatabaseEdgeType.IS_RELATED, + previous_value=None, + new_value=car_node_branch.id, + action=DiffAction.ADDED, + conflict=None, + ) + added_relationship_element = EnrichedRelationshipElementFactory.build( + action=DiffAction.ADDED, + peer_id=car_node_branch.id, + conflict=None, + properties={ + added_is_related_property, + added_rel_is_visible_property, + added_rel_is_protected_property, + added_rel_source_property, + }, + ) + added_relationship = EnrichedRelationshipGroupFactory.build( + name="cars", + relationships={added_relationship_element}, + action=DiffAction.UPDATED, + ) + added_node_diff.relationships = {added_relationship} + return added_node_diff + + 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() + ) + + @pytest.mark.parametrize("check_idempotent", [False, True]) + async def test_merge_node_added( + self, + db: InfrahubDatabase, + default_branch: Branch, + source_branch: Branch, + person_node_branch: Node, + car_node_branch: Node, + mock_diff_repository: DiffRepository, + diff_merger: DiffMerger, + empty_diff_root: EnrichedDiffRoot, + added_person_node_diff: EnrichedDiffNode, + check_idempotent: bool, + ): + empty_diff_root.nodes = {added_person_node_diff} + mock_diff_repository.get_empty_roots.return_value = [empty_diff_root] + mock_diff_repository.get_one.return_value = empty_diff_root + at = Timestamp() + + await diff_merger.merge_graph(at=at) + if check_idempotent: + await diff_merger.merge_graph(at=at) + + expected_awaits = [ + call(diff_branch_name=source_branch.name, diff_id=empty_diff_root.uuid), + ] + if check_idempotent: + expected_awaits *= 2 + assert mock_diff_repository.get_one.await_args_list == expected_awaits + + retrieved_node = await NodeManager.get_one( + db=db, id=person_node_branch.id, branch=default_branch, include_owner=True, include_source=True + ) + assert retrieved_node.get_updated_at() == at + assert retrieved_node.height.value == person_node_branch.height.value + owner_node = await retrieved_node.height.get_owner(db=db) + assert owner_node.id == retrieved_node.id + assert retrieved_node.height.is_visible is True + assert retrieved_node.height.is_protected is True + car_rel_elements = await retrieved_node.cars.get(db=db) + car_elements_by_peer_id = {c.get_peer_id(): c for c in car_rel_elements} + assert set(car_elements_by_peer_id.keys()) == {car_node_branch.id} + car_element = car_elements_by_peer_id[car_node_branch.id] + assert car_element.source_id == car_node_branch.id + + @pytest.mark.parametrize("check_idempotent", [False, True]) + async def test_merge_node_deleted( + self, + db: InfrahubDatabase, + default_branch: Branch, + person_node_main: Node, + car_node_main: Node, + source_branch: Branch, + mock_diff_repository: DiffRepository, + diff_merger: DiffMerger, + empty_diff_root: EnrichedDiffRoot, + deleted_person_node_diff: EnrichedDiffNode, + check_idempotent: bool, + ): + person_branch = await NodeManager.get_one(db=db, branch=source_branch, id=person_node_main.id) + await person_branch.delete(db=db) + empty_diff_root.nodes = {deleted_person_node_diff} + mock_diff_repository.get_empty_roots.return_value = [empty_diff_root] + mock_diff_repository.get_one.return_value = empty_diff_root + at = Timestamp() + + await diff_merger.merge_graph(at=at) + if check_idempotent: + await diff_merger.merge_graph(at=at) + + expected_awaits = [ + call(diff_branch_name=source_branch.name, diff_id=empty_diff_root.uuid), + ] + if check_idempotent: + expected_awaits *= 2 + assert mock_diff_repository.get_one.await_args_list == expected_awaits + + 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_empty_roots.return_value = [empty_diff_root] + 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, diff_id=empty_diff_root.uuid + ) + 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_person = await NodeManager.get_one(db=db, branch=default_branch, id=person_node_branch.id) + assert target_person.id == person_node_branch.id + assert target_person.get_updated_at() < at + + @pytest.fixture + def updated_person_node_diff( + self, person_node_main, car_node_main, person_node_main2, car_node_main2 + ) -> EnrichedDiffNode: + updated_node_diff = self._get_empty_node_diff(node=person_node_main, action=DiffAction.UPDATED) + # attributes + updated_height_value_property = EnrichedPropertyFactory.build( + property_type=DatabaseEdgeType.HAS_VALUE, + previous_value=str(person_node_main.height.value), + new_value=str(person_node_main.height.value + 1), + action=DiffAction.UPDATED, + conflict=None, + ) + added_height_owner_property = EnrichedPropertyFactory.build( + property_type=DatabaseEdgeType.HAS_OWNER, + previous_value=None, + new_value=str(person_node_main.id), + action=DiffAction.ADDED, + conflict=None, + ) + updated_height_attribute_diff = EnrichedAttributeFactory.build( + name="height", + action=DiffAction.UPDATED, + properties={updated_height_value_property, added_height_owner_property}, + ) + updated_name_value_property = EnrichedPropertyFactory.build( + property_type=DatabaseEdgeType.HAS_VALUE, + previous_value=str(person_node_main.name.value), + new_value=str(person_node_main.name.value + "-branch"), + action=DiffAction.UPDATED, + conflict=EnrichedConflictFactory.build( + base_branch_action=DiffAction.UPDATED, + base_branch_value="whatever", + diff_branch_action=DiffAction.UPDATED, + diff_branch_value=str(person_node_main.name.value + "-branch"), + selected_branch=ConflictSelection.DIFF_BRANCH, + ), + ) + updated_name_attribute_diff = EnrichedAttributeFactory.build( + name="name", + action=DiffAction.UPDATED, + properties={updated_name_value_property}, + ) + updated_node_diff.attributes = {updated_height_attribute_diff, updated_name_attribute_diff} + # relationships + deleted_is_related_property = EnrichedPropertyFactory.build( + property_type=DatabaseEdgeType.IS_RELATED, + previous_value=car_node_main.id, + new_value=None, + action=DiffAction.REMOVED, + conflict=None, + ) + deleted_rel_is_visible_property = EnrichedPropertyFactory.build( + property_type=DatabaseEdgeType.IS_VISIBLE, + previous_value="True", + new_value=None, + action=DiffAction.REMOVED, + conflict=None, + ) + deleted_rel_is_protected_property = EnrichedPropertyFactory.build( + property_type=DatabaseEdgeType.IS_PROTECTED, + previous_value="False", + new_value=None, + action=DiffAction.REMOVED, + conflict=None, + ) + deleted_rel_source_property = EnrichedPropertyFactory.build( + property_type=DatabaseEdgeType.HAS_SOURCE, + previous_value=person_node_main2.id, + new_value=None, + action=DiffAction.REMOVED, + conflict=None, + ) + deleted_rel_owner_property = EnrichedPropertyFactory.build( + property_type=DatabaseEdgeType.HAS_OWNER, + previous_value=car_node_main2.id, + new_value=None, + action=DiffAction.REMOVED, + conflict=None, + ) + deleted_relationship_element = EnrichedRelationshipElementFactory.build( + action=DiffAction.REMOVED, + peer_id=car_node_main.id, + conflict=None, + properties={ + deleted_is_related_property, + deleted_rel_is_visible_property, + deleted_rel_is_protected_property, + deleted_rel_source_property, + deleted_rel_owner_property, + }, + ) + deleted_relationship = EnrichedRelationshipGroupFactory.build( + name="cars", + relationships={deleted_relationship_element}, + action=DiffAction.UPDATED, + ) + updated_node_diff.relationships = {deleted_relationship} + return updated_node_diff + + @pytest.fixture + def updated_car_diff(self, person_node_main, car_node_main, person_node_main2, car_node_main2) -> EnrichedDiffNode: + updated_node_diff = self._get_empty_node_diff(node=car_node_main, action=DiffAction.UPDATED) + # relationships + updated_is_related_property = EnrichedPropertyFactory.build( + property_type=DatabaseEdgeType.IS_RELATED, + previous_value=person_node_main.id, + new_value=person_node_main2.id, + action=DiffAction.UPDATED, + conflict=None, + ) + updated_rel_source_property = EnrichedPropertyFactory.build( + property_type=DatabaseEdgeType.HAS_SOURCE, + previous_value=person_node_main2.id, + new_value=person_node_main.id, + action=DiffAction.UPDATED, + conflict=None, + ) + updated_rel_owner_property = EnrichedPropertyFactory.build( + property_type=DatabaseEdgeType.HAS_OWNER, + previous_value=car_node_main2.id, + new_value=car_node_main.id, + action=DiffAction.UPDATED, + conflict=None, + ) + updated_rel_is_protected_property = EnrichedPropertyFactory.build( + property_type=DatabaseEdgeType.IS_PROTECTED, + previous_value="False", + new_value="True", + action=DiffAction.UPDATED, + conflict=None, + ) + updated_relationship_element = EnrichedRelationshipElementFactory.build( + action=DiffAction.UPDATED, + peer_id=person_node_main2.id, + conflict=None, + properties={ + updated_is_related_property, + updated_rel_source_property, + updated_rel_owner_property, + updated_rel_is_protected_property, + }, + ) + updated_relationship = EnrichedRelationshipGroupFactory.build( + name="owner", + relationships={updated_relationship_element}, + action=DiffAction.UPDATED, + ) + updated_node_diff.relationships = {updated_relationship} + return updated_node_diff + + @pytest.mark.parametrize("check_idempotent", [False, True]) + async def test_merge_node_updated( + self, + db: InfrahubDatabase, + default_branch: Branch, + person_node_main: Node, + person_node_main2: Node, + car_node_main: Node, + car_node_main2: Node, + mock_diff_repository: DiffRepository, + diff_merger: DiffMerger, + empty_diff_root: EnrichedDiffRoot, + updated_person_node_diff: EnrichedDiffNode, + updated_car_diff: EnrichedDiffNode, + check_idempotent: bool, + ): + car_main = await NodeManager.get_one(db=db, branch=default_branch, id=car_node_main.id) + await car_main.owner.update( + db=db, + data={ + "id": person_node_main.id, + "_relation__owner": car_node_main2.id, + "_relation__source": person_node_main2.id, + }, + ) + await car_main.owner.save(db=db) + + source_branch = await create_branch(db=db, branch_name="source") + + person_branch = await NodeManager.get_one(db=db, branch=source_branch, id=person_node_main.id) + person_branch.height.value += 1 + person_branch.height.set_owner(value=person_branch.id) + person_branch.name.value += "-branch" + await person_branch.save(db=db) + + car_branch = await NodeManager.get_one(db=db, branch=source_branch, id=car_node_main.id) + await car_branch.owner.update( + db=db, + data={ + "id": person_node_main2.id, + "_relation__owner": car_node_main.id, + "_relation__source": person_node_main.id, + "_relation__is_protected": True, + }, + ) + await car_branch.save(db=db) + + empty_diff_root.nodes = {updated_person_node_diff, updated_car_diff} + mock_diff_repository.get_empty_roots.return_value = [empty_diff_root] + mock_diff_repository.get_one.return_value = empty_diff_root + at = Timestamp() + + await diff_merger.merge_graph(at=at) + if check_idempotent: + await diff_merger.merge_graph(at=at) + + expected_awaits = [ + call(diff_branch_name=source_branch.name, diff_id=empty_diff_root.uuid), + ] + if check_idempotent: + expected_awaits *= 2 + assert mock_diff_repository.get_one.await_args_list == expected_awaits + updated_person = await NodeManager.get_one( + db=db, branch=default_branch, id=person_node_main.id, include_owner=True + ) + assert updated_person.get_updated_at() < at + assert updated_person.height.value == person_node_main.height.value + 1 + owner_node = await updated_person.height.get_owner(db=db) + assert owner_node.id == person_node_main.id + assert updated_person.name.value == person_node_main.name.value + "-branch" + car_rels = await updated_person.cars.get(db=db) + assert len(car_rels) == 0 + updated_car = await NodeManager.get_one( + db=db, branch=default_branch, id=car_node_main.id, include_owner=True, include_source=True + ) + owner_rel = await updated_car.owner.get(db=db) + assert owner_rel.is_protected is True + assert owner_rel.is_visible is True + assert owner_rel.peer_id == person_node_main2.id + rel_owner_node = await owner_rel.get_owner(db=db) + assert rel_owner_node.id == car_node_main.id + rel_source_node = await owner_rel.get_source(db=db) + assert rel_source_node.id == person_node_main.id diff --git a/backend/tests/unit/core/diff/test_diff_payload.py b/backend/tests/unit/core/diff/test_diff_payload.py index 5d8aaf4df3..646c3fee00 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 @@ -114,8 +114,8 @@ async def test_diff_payload_two_updates_one_relationship(db: InfrahubDatabase, p assert owner_element.type is DiffElementType.RELATIONSHIP_ONE assert owner_element.name == "owner" assert owner_element.branch == branch.name - assert owner_element.action is DiffAction.UPDATED - assert owner_element.peer.previous.id == person_albert_main.id + assert owner_element.action is DiffAction.ADDED + assert owner_element.peer.previous is None assert owner_element.peer.new.id == person_alfred_main.id @@ -159,8 +159,8 @@ async def test_diff_payload_three_updates_one_relationship( assert owner_element.type is DiffElementType.RELATIONSHIP_ONE assert owner_element.name == "owner" assert owner_element.branch == branch.name - assert owner_element.action is DiffAction.UPDATED - assert owner_element.peer.previous.id == person_jane_main.id + assert owner_element.action is DiffAction.ADDED + assert owner_element.peer.previous is None assert owner_element.peer.new.id == person_alfred_main.id diff --git a/backend/tests/unit/core/diff/test_diff_repository.py b/backend/tests/unit/core/diff/test_diff_repository.py index 92fc7bce7d..d96d8e2a80 100644 --- a/backend/tests/unit/core/diff/test_diff_repository.py +++ b/backend/tests/unit/core/diff/test_diff_repository.py @@ -15,6 +15,7 @@ EnrichedDiffRoot, EnrichedDiffs, NameTrackingId, + NodeDiffFieldSummary, ) from infrahub.core.diff.repository.deserializer import EnrichedDiffDeserializer from infrahub.core.diff.repository.repository import DiffRepository @@ -43,7 +44,7 @@ def diff_repository(self, db: InfrahubDatabase) -> DiffRepository: config.SETTINGS.database.max_depth_search_hierarchy = 10 return DiffRepository(db=db, deserializer=EnrichedDiffDeserializer()) - def build_diff_node(self, num_sub_fields=2) -> EnrichedDiffNode: + def build_diff_node(self, num_sub_fields=2, no_recurse=False) -> EnrichedDiffNode: enriched_node = EnrichedNodeFactory.build( attributes={ EnrichedAttributeFactory.build( @@ -70,6 +71,8 @@ def build_diff_node(self, num_sub_fields=2) -> EnrichedDiffNode: for _ in range(num_sub_fields) }, ) + if no_recurse: + return enriched_node if num_sub_fields > 1 and len(enriched_node.relationships) > 0: for relationship_group in enriched_node.relationships: relationship_group.nodes = { @@ -455,167 +458,6 @@ async def test_filter_root_node_uuids(self, diff_repository: DiffRepository, res assert len(retrieved) == 1 assert retrieved[0] == replace(this_diff, nodes={parent_node, thin_middle_node, expected_leaf_node}) - # async def test_filter_limit_and_offset_flat(self, diff_repository: DiffRepository, reset_database): - # ordered_nodes = [] - # for kind, label in (("A", "a"), ("A", "b"), ("B", "a"), ("B", "b")): - # ordered_nodes.append(EnrichedNodeFactory.build(kind=kind, label=label, relationships=set())) - # enriched_diff = EnrichedRootFactory.build( - # base_branch_name=self.base_branch_name, - # diff_branch_name=self.diff_branch_name, - # from_time=Timestamp(self.diff_from_time), - # to_time=Timestamp(self.diff_to_time), - # nodes=set(ordered_nodes), - # ) - # await diff_repository.save(enriched_diff=enriched_diff) - - # retrieved = await diff_repository.get( - # base_branch_name=self.base_branch_name, - # diff_branch_names=[self.diff_branch_name], - # from_time=Timestamp(self.diff_from_time), - # to_time=Timestamp(self.diff_to_time), - # limit=2, - # ) - # assert len(retrieved) == 1 - # assert retrieved[0].nodes == set(ordered_nodes[:2]) - - # retrieved = await diff_repository.get( - # base_branch_name=self.base_branch_name, - # diff_branch_names=[self.diff_branch_name], - # from_time=Timestamp(self.diff_from_time), - # to_time=Timestamp(self.diff_to_time), - # limit=2, - # offset=2, - # ) - # assert len(retrieved) == 1 - # assert retrieved[0].nodes == set(ordered_nodes[2:]) - - # async def test_filter_limit_and_offset_with_nested_nodes(self, diff_repository: DiffRepository, reset_database): - # nodes = self._build_nodes(num_nodes=10, num_sub_fields=3) - # enriched_diff = EnrichedRootFactory.build( - # base_branch_name=self.base_branch_name, - # diff_branch_name=self.diff_branch_name, - # from_time=Timestamp(self.diff_from_time), - # to_time=Timestamp(self.diff_to_time), - # nodes=nodes, - # ) - # root_nodes = enriched_diff.get_nodes_without_parents() - # ordered_nodes = list(root_nodes) - # kinds = sorted(random.sample(string.ascii_uppercase, k=5)) - # for i in range(5): - # kind = kinds[i] - # labels = sorted(random.sample(string.ascii_lowercase, k=2)) - # ordered_nodes[2 * i].kind = kind - # ordered_nodes[2 * i + 1].kind = kind - # ordered_nodes[2 * i].label = labels[0] - # ordered_nodes[2 * i + 1].label = labels[1] - # await diff_repository.save(enriched_diff=enriched_diff) - - # retrieved = await diff_repository.get( - # base_branch_name=self.base_branch_name, - # diff_branch_names=[self.diff_branch_name], - # from_time=Timestamp(self.diff_from_time), - # to_time=Timestamp(self.diff_to_time), - # limit=2, - # ) - # expected_root_nodes = set(ordered_nodes[:2]) - # all_expected_nodes = set(expected_root_nodes) - # for n in expected_root_nodes: - # all_expected_nodes |= n.get_all_child_nodes() - # assert len(retrieved) == 1 - # assert retrieved[0].get_nodes_without_parents() == expected_root_nodes - # assert retrieved[0].nodes == all_expected_nodes - - # retrieved = await diff_repository.get( - # base_branch_name=self.base_branch_name, - # diff_branch_names=[self.diff_branch_name], - # from_time=Timestamp(self.diff_from_time), - # to_time=Timestamp(self.diff_to_time), - # limit=4, - # offset=2, - # ) - # expected_root_nodes = set(ordered_nodes[2:6]) - # all_expected_nodes = set(expected_root_nodes) - # for n in expected_root_nodes: - # all_expected_nodes |= n.get_all_child_nodes() - # assert len(retrieved) == 1 - # assert retrieved[0].get_nodes_without_parents() == set(ordered_nodes[2:6]) - # assert retrieved[0].nodes == all_expected_nodes - - # async def test_filter_limit_and_offset_across_multiple_roots(self, diff_repository: DiffRepository, reset_database): - # enriched_diffs = [] - # node_uuids = [str(uuid4()) for _ in range(3)] - # first_nodes = [] - # second_nodes = [] - # third_nodes = [] - # start_time = self.diff_from_time.add(minutes=1) - # for i in range(3): - # nodes = self._build_nodes(num_nodes=3, num_sub_fields=2) - # enriched_diff = EnrichedRootFactory.build( - # base_branch_name=self.base_branch_name, - # diff_branch_name=self.diff_branch_name, - # from_time=Timestamp(start_time.add(minutes=i * 30)), - # to_time=Timestamp(start_time.add(minutes=(i * 30) + 29)), - # nodes=nodes, - # ) - # enriched_diffs.append(enriched_diff) - # root_nodes = enriched_diff.get_nodes_without_parents() - # ordered_nodes = list(root_nodes) - # first_node, second_node, third_node = ordered_nodes - # first_node.kind = "A" - # first_node.label = "a" - # first_node.uuid = node_uuids[0] - # first_nodes.append(first_node) - # second_node.kind = "B" - # second_node.label = "b" - # second_node.uuid = node_uuids[1] - # second_nodes.append(second_node) - # third_node.kind = "C" - # third_node.label = "c" - # third_node.uuid = node_uuids[2] - # third_nodes.append(third_node) - # await diff_repository.save(enriched_diff=enriched_diff) - - # retrieved = await diff_repository.get( - # base_branch_name=self.base_branch_name, - # diff_branch_names=[self.diff_branch_name], - # from_time=Timestamp(start_time), - # to_time=Timestamp(start_time.add(minutes=100)), - # limit=1, - # ) - # assert len(retrieved) == 3 - # for index, retrieved_root in enumerate(retrieved): - # root_nodes = retrieved_root.get_nodes_without_parents() - # assert len(root_nodes) == 1 - # assert root_nodes == {first_nodes[index]} - - # retrieved = await diff_repository.get( - # base_branch_name=self.base_branch_name, - # diff_branch_names=[self.diff_branch_name], - # from_time=Timestamp(start_time), - # to_time=Timestamp(start_time.add(minutes=100)), - # limit=1, - # offset=1, - # ) - # assert len(retrieved) == 3 - # for index, retrieved_root in enumerate(retrieved): - # root_nodes = retrieved_root.get_nodes_without_parents() - # assert len(root_nodes) == 1 - # assert root_nodes == {second_nodes[index]} - - # retrieved = await diff_repository.get( - # base_branch_name=self.base_branch_name, - # diff_branch_names=[self.diff_branch_name], - # from_time=Timestamp(start_time), - # to_time=Timestamp(start_time.add(minutes=100)), - # limit=1, - # offset=2, - # ) - # assert len(retrieved) == 3 - # for index, retrieved_root in enumerate(retrieved): - # root_nodes = retrieved_root.get_nodes_without_parents() - # assert len(root_nodes) == 1 - # assert root_nodes == {third_nodes[index]} - async def test_save_and_retrieve_many_diffs(self, diff_repository: DiffRepository, reset_database): diffs_to_retrieve: list[EnrichedDiffRoot] = [] start_time = self.diff_from_time.add(seconds=1) @@ -729,3 +571,46 @@ async def test_get_by_tracking_id(self, diff_repository: DiffRepository, reset_d tracking_id=BranchTrackingId(name="not a branch"), diff_branch_name=self.diff_branch_name, ) + + async def test_get_node_field_summaries(self, diff_repository: DiffRepository): + diff_nodes = self._build_nodes(num_nodes=5, num_sub_fields=2) + for diff_node in list(diff_nodes)[:3]: + same_kind_diff_node = self.build_diff_node(num_sub_fields=3, no_recurse=True) + same_kind_diff_node.kind = diff_node.kind + same_attr_names = random.sample([a.name for a in diff_node.attributes], k=min(len(diff_node.attributes), 2)) + for attr_diff, attr_name in zip(list(same_kind_diff_node.attributes)[:2], same_attr_names): + attr_diff.name = attr_name + same_rel_names = random.sample( + [r.name for r in diff_node.relationships], k=min(len(diff_node.relationships), 2) + ) + for rel_diff, rel_name in zip(list(same_kind_diff_node.relationships)[:2], same_rel_names): + rel_diff.name = rel_name + diff_nodes.add(same_kind_diff_node) + diff_root = EnrichedRootFactory.build(nodes=diff_nodes) + diff_root.tracking_id = BranchTrackingId(name=diff_root.diff_branch_name) + await self._save_single_diff(diff_repository=diff_repository, enriched_diff=diff_root) + + expected_map: dict[str, NodeDiffFieldSummary] = {} + for node in diff_root.nodes: + if node.action is DiffAction.UNCHANGED: + continue + if node.kind not in expected_map: + expected_map[node.kind] = NodeDiffFieldSummary(kind=node.kind) + field_summary = expected_map[node.kind] + attr_names = {a.name for a in node.attributes if a.action is not DiffAction.UNCHANGED} + field_summary.attribute_names.update(attr_names) + rel_names = {r.name for r in node.relationships if r.action is not DiffAction.UNCHANGED} + field_summary.relationship_names.update(rel_names) + expected_map = {k: v for k, v in expected_map.items() if v.relationship_names or v.attribute_names} + + retrieved_node_field_summaries = await diff_repository.get_node_field_summaries( + diff_branch_name=diff_root.diff_branch_name, tracking_id=diff_root.tracking_id + ) + retrieved_map = {summary.kind: summary for summary in retrieved_node_field_summaries} + assert expected_map == retrieved_map + + retrieved_node_field_summaries = await diff_repository.get_node_field_summaries( + diff_branch_name=diff_root.diff_branch_name, diff_id=diff_root.uuid + ) + retrieved_map = {summary.kind: summary for summary in retrieved_node_field_summaries} + assert expected_map == retrieved_map 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_001.py b/backend/tests/unit/core/migrations/graph/test_001.py index a12a1fa42c..8c907de4f5 100644 --- a/backend/tests/unit/core/migrations/graph/test_001.py +++ b/backend/tests/unit/core/migrations/graph/test_001.py @@ -1,4 +1,4 @@ -from infrahub_sdk import UUIDT +from infrahub_sdk.uuidt import UUIDT from infrahub.core.migrations.graph import Migration001 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/migrations/schema/test_node_attribute_add.py b/backend/tests/unit/core/migrations/schema/test_node_attribute_add.py index 2ad9c01ae7..91addd687c 100644 --- a/backend/tests/unit/core/migrations/schema/test_node_attribute_add.py +++ b/backend/tests/unit/core/migrations/schema/test_node_attribute_add.py @@ -1,13 +1,20 @@ import uuid import pytest -from infrahub_sdk import UUIDT, InfrahubClient +from infrahub_sdk import InfrahubClient +from infrahub_sdk.uuidt import UUIDT -from infrahub.core.constants import SchemaPathType +from infrahub.core import registry +from infrahub.core.branch import Branch +from infrahub.core.constants import HashableModelState, SchemaPathType from infrahub.core.migrations.schema.node_attribute_add import ( NodeAttributeAddMigration, NodeAttributeAddMigrationQuery01, ) +from infrahub.core.migrations.schema.node_attribute_remove import ( + NodeAttributeRemoveMigration, + NodeAttributeRemoveMigrationQuery01, +) from infrahub.core.path import SchemaPath from infrahub.core.schema import NodeSchema from infrahub.core.timestamp import Timestamp @@ -87,6 +94,54 @@ async def test_query01(db: InfrahubDatabase, default_branch, init_database, sche assert await count_nodes(db=db, label="Attribute") == 5 +async def test_query01_re_add(db: InfrahubDatabase, default_branch: Branch, car_accord_main, car_camry_main): + schema = registry.schema.get_schema_branch(name=default_branch.name) + + assert await count_nodes(db=db, label="TestCar") == 2 + assert await count_nodes(db=db, label="Attribute") == 14 + + # ------------------------------------------ + # Delete the attribute Color + # ------------------------------------------ + candidate_schema = schema.duplicate() + car_schema = candidate_schema.get_node(name="TestCar") + attr = car_schema.get_attribute(name="color") + attr.state = HashableModelState.ABSENT + + migration_remove = NodeAttributeRemoveMigration( + previous_node_schema=schema.get_node(name="TestCar"), + new_node_schema=car_schema, + schema_path=SchemaPath(path_type=SchemaPathType.ATTRIBUTE, schema_kind="TestCar", field_name="color"), + ) + query = await NodeAttributeRemoveMigrationQuery01.init(db=db, branch=default_branch, migration=migration_remove) + await query.execute(db=db) + assert query.get_nbr_migrations_executed() == 2 + + # ------------------------------------------ + # Add the attribute Color back + # ------------------------------------------ + migration_add = NodeAttributeAddMigration( + new_node_schema=schema.get_node(name="TestCar"), + previous_node_schema=car_schema, + schema_path=SchemaPath(path_type=SchemaPathType.ATTRIBUTE, schema_kind="TestCar", field_name="color"), + ) + query = await NodeAttributeAddMigrationQuery01.init(db=db, branch=default_branch, migration=migration_add) + await query.execute(db=db) + + assert query.get_nbr_migrations_executed() == 2 + + assert await count_nodes(db=db, label="TestCar") == 2 + assert await count_nodes(db=db, label="Attribute") == 16 + + # Re-execute the query once to ensure that it won't recreate the attribute twice + query = await NodeAttributeAddMigrationQuery01.init(db=db, branch=default_branch, migration=migration_add) + await query.execute(db=db) + + assert query.get_nbr_migrations_executed() == 0 + assert await count_nodes(db=db, label="TestCar") == 2 + assert await count_nodes(db=db, label="Attribute") == 16 + + async def test_migration(db: InfrahubDatabase, default_branch, init_database, schema_aware): node = schema_aware migration = NodeAttributeAddMigration( diff --git a/backend/tests/unit/core/migrations/schema/test_node_remove.py b/backend/tests/unit/core/migrations/schema/test_node_remove.py index 9262e17c08..361a503ef7 100644 --- a/backend/tests/unit/core/migrations/schema/test_node_remove.py +++ b/backend/tests/unit/core/migrations/schema/test_node_remove.py @@ -1,4 +1,5 @@ -from infrahub_sdk import UUIDT, InfrahubClient +from infrahub_sdk import InfrahubClient +from infrahub_sdk.uuidt import UUIDT from infrahub.core import registry from infrahub.core.branch import Branch 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_number_pool.py b/backend/tests/unit/core/resource_manager/test_number_pool.py index 02ea6c0219..b05f28d9b2 100644 --- a/backend/tests/unit/core/resource_manager/test_number_pool.py +++ b/backend/tests/unit/core/resource_manager/test_number_pool.py @@ -24,3 +24,10 @@ async def test_allocate_from_number_pool(db: InfrahubDatabase, default_branch: B assert ticket1.ticket_id.value == 1 assert ticket2.ticket_id.value == 2 + + # If a resource is deleted the allocated number should be returned to the pool + await ticket2.delete(db=db) + recreated_ticket2 = await Node.init(db=db, schema=TICKET.kind) + await recreated_ticket2.new(db=db, title="ticket2", ticket_id={"from_pool": {"id": np1.id}}) + await recreated_ticket2.save(db=db) + assert recreated_ticket2.ticket_id.value == 2 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/sync/examples/observium_to_infrahub/observium/__init__.py b/backend/tests/unit/core/schema/__init__.py similarity index 100% rename from sync/examples/observium_to_infrahub/observium/__init__.py rename to backend/tests/unit/core/schema/__init__.py diff --git a/sync/examples/peering-manager_to_infrahub/infrahub/__init__.py b/backend/tests/unit/core/schema/schema_branch/__init__.py similarity index 100% rename from sync/examples/peering-manager_to_infrahub/infrahub/__init__.py rename to backend/tests/unit/core/schema/schema_branch/__init__.py diff --git a/backend/tests/unit/core/schema/schema_branch/test_schema_uniqueness_constraints.py b/backend/tests/unit/core/schema/schema_branch/test_schema_uniqueness_constraints.py new file mode 100644 index 0000000000..b44524a696 --- /dev/null +++ b/backend/tests/unit/core/schema/schema_branch/test_schema_uniqueness_constraints.py @@ -0,0 +1,73 @@ +import pytest + +from infrahub.core.schema import AttributeSchema, NodeSchema, SchemaRoot +from infrahub.core.schema.schema_branch import SchemaBranch + + +@pytest.mark.parametrize( + "schema_root,expected_error", + [ + pytest.param( + SchemaRoot( + nodes=[ + NodeSchema( + name="Person", + namespace="Testing", + uniqueness_constraints=[["first_name__value"]], + attributes=[ + AttributeSchema( + name="name", + kind="Text", + ), + AttributeSchema( + name="description", + kind="Text", + optional=True, + ), + ], + ), + ], + ), + "TestingPerson: Requested unique constraint not found within node. (`first_name__value`)", + id="missing_all", + ), + pytest.param( + SchemaRoot( + nodes=[ + NodeSchema( + name="Person", + namespace="Testing", + uniqueness_constraints=[ + ["first_name__value", "last_name__value"], + ["origin__value", "family__value"], + ], + attributes=[ + AttributeSchema( + name="first_name", + kind="Text", + ), + AttributeSchema( + name="last_name", + kind="Text", + ), + AttributeSchema( + name="origin", + kind="Text", + ), + ], + ), + ], + ), + "TestingPerson.uniqueness_constraints: family__value is invalid on schema TestingPerson", + id="missing_single", + ), + ], +) +async def test_schema_protected_generics(schema_root: SchemaRoot, expected_error: str): + schema = SchemaBranch(cache={}, name="test") + schema.load_schema(schema=schema_root) + + with pytest.raises(ValueError) as exc: + schema.process_validate() + + assert expected_error == str(exc.value) 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..1f534aa51e 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,7 +11,6 @@ from infrahub.core.constants import ( AllowOverrideType, BranchSupportType, - FilterSchemaKind, HashableModelState, InfrahubKind, RelationshipDeleteBehavior, @@ -25,7 +24,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 @@ -368,6 +368,41 @@ async def test_schema_branch_add_groups(schema_all_in_one): assert std_group.get_relationship_or_none(name="subscriber_of_groups") is None +async def test_schema_branch_cleanup_inherited_elements(schema_all_in_one): + schema = SchemaBranch(cache={}, name="test") + schema.load_schema(schema=SchemaRoot(**schema_all_in_one)) + + schema.process_inheritance() + + schema = SchemaBranch(cache={}, name="test") + schema.load_schema(schema=SchemaRoot(**schema_all_in_one)) + schema.process() + + generic = schema.get(name="InfraGenericInterface") + attr1 = generic.get_attribute(name="mybool") + attr1.state = HashableModelState.ABSENT + rel1 = generic.get_relationship(name="primary_tag") + rel1.state = HashableModelState.ABSENT + schema.set(name=generic.kind, schema=generic) + + node = schema.get(name="BuiltinCriticality") + attr1_node = node.get_attribute(name="mybool") + assert attr1_node.inherited is True + assert attr1_node.state == HashableModelState.PRESENT + rel1_node = node.get_relationship(name="primary_tag") + assert rel1_node.inherited is True + assert rel1_node.state == HashableModelState.PRESENT + + schema.cleanup_inherited_elements() + node = schema.get(name="BuiltinCriticality") + attr1_node = node.get_attribute(name="mybool") + assert attr1_node.inherited is True + assert attr1_node.state == HashableModelState.ABSENT + rel1_node = node.get_relationship(name="primary_tag") + assert rel1_node.inherited is True + assert rel1_node.state == HashableModelState.ABSENT + + @pytest.mark.parametrize( "schema_dict,expected_error", [ @@ -921,115 +956,6 @@ async def test_schema_branch_validate_kinds_core(register_core_models_schema: Sc register_core_models_schema.validate_kinds() -async def test_schema_branch_validate_menu_placement(): - """Validate that menu placements points to objects that exists in the schema.""" - FULL_SCHEMA = { - "version": "1.0", - "nodes": [ - { - "name": "Criticality", - "namespace": "Test", - "default_filter": "name__value", - "branch": BranchSupportType.AWARE.value, - "attributes": [ - {"name": "name", "kind": "Text", "unique": True}, - ], - }, - { - "name": "SubObject", - "namespace": "Test", - "menu_placement": "NoSuchObject", - "default_filter": "name__value", - "branch": BranchSupportType.AWARE.value, - "attributes": [ - {"name": "name", "kind": "Text", "unique": True}, - ], - }, - ], - } - - schema = SchemaBranch(cache={}) - schema.load_schema(schema=SchemaRoot(**FULL_SCHEMA)) - - with pytest.raises(SchemaNotFoundError) as exc: - schema.validate_menu_placements() - - assert exc.value.message == "TestSubObject refers to an invalid menu placement node: NoSuchObject." - - -async def test_schema_branch_validate_same_node_menu_placement(): - """Validate that menu placements points to objects that exists in the schema.""" - FULL_SCHEMA = { - "version": "1.0", - "nodes": [ - { - "name": "Criticality", - "namespace": "Test", - "default_filter": "name__value", - "branch": BranchSupportType.AWARE.value, - "attributes": [ - {"name": "name", "kind": "Text", "unique": True}, - ], - }, - { - "name": "SubObject", - "namespace": "Test", - "menu_placement": "TestSubObject", - "default_filter": "name__value", - "branch": BranchSupportType.AWARE.value, - "attributes": [ - {"name": "name", "kind": "Text", "unique": True}, - ], - }, - ], - } - - schema = SchemaBranch(cache={}) - schema.load_schema(schema=SchemaRoot(**FULL_SCHEMA)) - - with pytest.raises(ValueError) as exc: - schema.validate_menu_placements() - - assert str(exc.value) == "TestSubObject: cannot be placed under itself in the menu" - - -async def test_schema_branch_validate_cyclic_menu_placement(): - """Validate that menu placements points to objects that exists in the schema.""" - FULL_SCHEMA = { - "version": "1.0", - "nodes": [ - { - "name": "Criticality", - "namespace": "Test", - "menu_placement": "TestSubObject", - "default_filter": "name__value", - "branch": BranchSupportType.AWARE.value, - "attributes": [ - {"name": "name", "kind": "Text", "unique": True}, - ], - }, - { - "name": "SubObject", - "namespace": "Test", - "menu_placement": "TestCriticality", - "default_filter": "name__value", - "branch": BranchSupportType.AWARE.value, - "attributes": [ - {"name": "name", "kind": "Text", "unique": True}, - ], - }, - ], - } - - schema = SchemaBranch(cache={}) - schema.load_schema(schema=SchemaRoot(**FULL_SCHEMA)) - - with pytest.raises(ValueError) as exc: - schema.validate_menu_placements() - - assert str(exc.value) == "TestSubObject: cyclic menu placement with TestCriticality" - - @pytest.mark.parametrize( "uniqueness_constraints", [ @@ -1489,513 +1415,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_attribute.py b/backend/tests/unit/core/test_attribute.py index 668e596b48..fa5d6f60a0 100644 --- a/backend/tests/unit/core/test_attribute.py +++ b/backend/tests/unit/core/test_attribute.py @@ -1,10 +1,21 @@ from enum import Enum import pytest -from infrahub_sdk import UUIDT +from infrahub_sdk.uuidt import UUIDT from infrahub import config -from infrahub.core.attribute import URL, DateTime, Dropdown, Integer, IPHost, IPNetwork, MacAddress, String +from infrahub.core.attribute import ( + MAX_STRING_LENGTH, + URL, + DateTime, + Dropdown, + Integer, + IPHost, + IPNetwork, + ListAttribute, + MacAddress, + String, +) from infrahub.core.branch import Branch from infrahub.core.constants import InfrahubKind from infrahub.core.manager import NodeManager @@ -674,3 +685,103 @@ async def test_to_graphql_no_fields( "value": "mystring", } assert await attr2.to_graphql(db=db) == expected_data + + +async def test_attribute_size(db: InfrahubDatabase, default_branch: Branch, all_attribute_types_schema): + obj = await Node.init(db=db, schema="TestAllAttributeTypes") + + large_string = "a" * 5_000 + + await obj.new(db=db, name="obj1", mystring=large_string) + + # Text field + with pytest.raises( + ValidationError, match=f"Text attribute length should be less than {MAX_STRING_LENGTH} characters." + ): + await obj.save(db=db) + + # TextArea field should have no size limitation + await obj.new(db=db, name="obj2", mytextarea=large_string) + await obj.save(db=db) + + +@pytest.mark.parametrize( + "regex_value,input_value,error", + [ + pytest.param( + "^box_", + ["mystring"], + "mystring must conform with the regex: '^box_' at test", + id="not-a-box", + ), + pytest.param( + "^box_", + ["box_a", "box_b", "chest_a"], + "chest_a must conform with the regex: '^box_' at test", + id="is-chest", + ), + ], +) +def test_attribute_list_invalid_regex( + default_branch: Branch, regex_value: str, input_value: list[str], error: str +) -> None: + storage_attribute = AttributeSchema(name="storage", kind="List", regex=regex_value) + widget = NodeSchema( + name="Widget", + namespace="Testing", + label="Widget", + attributes=[ + storage_attribute, + ], + ) + + with pytest.raises(ValidationError) as exc: + ListAttribute( + id=str(UUIDT()), + name="test", + schema=storage_attribute, + branch=default_branch, + at=Timestamp(), + node=Node(schema=widget, branch=default_branch, at=Timestamp()), + data=input_value, + ) + + assert error in str(exc.value) + + +@pytest.mark.parametrize( + "regex_value,input_value", + [ + pytest.param( + "^box_", + ["box_one"], + id="a-box", + ), + pytest.param( + "^box_", + ["box_a", "box_b", "box_another"], + id="several_boxes", + ), + ], +) +def test_attribute_list_regex(default_branch: Branch, regex_value: str, input_value: list[str]) -> None: + storage_attribute = AttributeSchema(name="storage", kind="List", regex=regex_value) + widget = NodeSchema( + name="Widget", + namespace="Testing", + label="Widget", + attributes=[ + storage_attribute, + ], + ) + + list_attrib = ListAttribute( + id=str(UUIDT()), + name="test", + schema=storage_attribute, + branch=default_branch, + at=Timestamp(), + node=Node(schema=widget, branch=default_branch, at=Timestamp()), + data=input_value, + ) + assert list_attrib.value == input_value diff --git a/backend/tests/unit/core/test_branch_merge.py b/backend/tests/unit/core/test_branch_merge.py index f93537d36a..c010def8d0 100644 --- a/backend/tests/unit/core/test_branch_merge.py +++ b/backend/tests/unit/core/test_branch_merge.py @@ -1,6 +1,8 @@ from infrahub.core import registry from infrahub.core.branch import Branch from infrahub.core.constants import InfrahubKind +from infrahub.core.diff.coordinator import DiffCoordinator +from infrahub.core.diff.merger.merger import DiffMerger from infrahub.core.initialization import create_branch from infrahub.core.manager import NodeManager from infrahub.core.merge import BranchMerger @@ -8,13 +10,18 @@ from infrahub.core.node import Node from infrahub.core.path import SchemaPath, SchemaPathType from infrahub.core.schema import AttributeSchema +from infrahub.core.timestamp import Timestamp from infrahub.database import InfrahubDatabase +from infrahub.dependencies.registry import get_component_registry async def test_validate_graph(db: InfrahubDatabase, base_dataset_02, register_core_models_schema): branch1 = await Branch.get_by_name(name="branch1", db=db) - merger = BranchMerger(db=db, source_branch=branch1) + component_registry = get_component_registry() + diff_coordinator = await component_registry.get_component(DiffCoordinator, db=db, branch=branch1) + diff_merger = await component_registry.get_component(DiffMerger, db=db, branch=branch1) + merger = BranchMerger(db=db, diff_coordinator=diff_coordinator, diff_merger=diff_merger, source_branch=branch1) conflicts = await merger.validate_graph() assert not conflicts @@ -25,7 +32,7 @@ async def test_validate_graph(db: InfrahubDatabase, base_dataset_02, register_co c1.name.value = "new name" await c1.save(db=db) - merger = BranchMerger(db=db, source_branch=branch1) + merger = BranchMerger(db=db, diff_coordinator=diff_coordinator, diff_merger=diff_merger, source_branch=branch1) conflicts = await merger.validate_graph() assert conflicts @@ -35,18 +42,24 @@ async def test_validate_graph(db: InfrahubDatabase, base_dataset_02, register_co async def test_validate_empty_branch(db: InfrahubDatabase, base_dataset_02, register_core_models_schema): branch2 = await create_branch(branch_name="branch2", db=db) - merger = BranchMerger(db=db, source_branch=branch2) + component_registry = get_component_registry() + diff_coordinator = await component_registry.get_component(DiffCoordinator, db=db, branch=branch2) + diff_merger = await component_registry.get_component(DiffMerger, db=db, branch=branch2) + merger = BranchMerger(db=db, diff_coordinator=diff_coordinator, diff_merger=diff_merger, source_branch=branch2) conflicts = await merger.validate_graph() assert not conflicts assert conflicts == [] -async def test_merge_graph(db: InfrahubDatabase, base_dataset_02, register_core_models_schema): +async def test_merge_graph(db: InfrahubDatabase, default_branch, base_dataset_02, register_core_models_schema): branch1 = await Branch.get_by_name(name="branch1", db=db) - - merger = BranchMerger(db=db, source_branch=branch1) - await merger.merge_graph() + at = Timestamp() + component_registry = get_component_registry() + diff_coordinator = await component_registry.get_component(DiffCoordinator, db=db, branch=branch1) + diff_merger = await component_registry.get_component(DiffMerger, db=db, branch=branch1) + await diff_coordinator.update_branch_diff(base_branch=default_branch, diff_branch=branch1) + await diff_merger.merge_graph(at=at) # Query all cars in MAIN, AFTER the merge cars = sorted(await NodeManager.query(schema="TestCar", db=db), key=lambda c: c.id) @@ -80,12 +93,13 @@ async def test_merge_graph(db: InfrahubDatabase, base_dataset_02, register_core_ assert cars[0].nbr_seats.value == 4 # It should be possible to merge a graph even without changes - merger = BranchMerger(db=db, source_branch=branch1) - await merger.merge_graph() + await diff_merger.merge_graph(at=at) -async def test_merge_graph_delete(db: InfrahubDatabase, base_dataset_02, register_core_models_schema): +async def test_merge_graph_delete(db: InfrahubDatabase, default_branch, base_dataset_02, register_core_models_schema): branch1 = await Branch.get_by_name(name="branch1", db=db) + component_registry = get_component_registry() + diff_coordinator = await component_registry.get_component(DiffCoordinator, db=db, branch=branch1) persons = sorted(await NodeManager.query(schema="TestPerson", db=db), key=lambda p: p.id) assert len(persons) == 3 @@ -93,8 +107,9 @@ async def test_merge_graph_delete(db: InfrahubDatabase, base_dataset_02, registe p3 = await NodeManager.get_one(id="p3", branch=branch1, db=db) await p3.delete(db=db) - merger = BranchMerger(db=db, source_branch=branch1) - await merger.merge_graph() + await diff_coordinator.update_branch_diff(base_branch=default_branch, diff_branch=branch1) + diff_merger = await component_registry.get_component(DiffMerger, db=db, branch=branch1) + await diff_merger.merge_graph(at=Timestamp()) # Query all cars in MAIN, AFTER the merge persons = sorted(await NodeManager.query(schema="TestPerson", db=db), key=lambda p: p.id) @@ -121,6 +136,8 @@ async def test_merge_relationship_many( await org1.save(db=db) branch1 = await create_branch(branch_name="branch1", db=db) + component_registry = get_component_registry() + diff_coordinator = await component_registry.get_component(DiffCoordinator, db=db, branch=branch1) org1_branch = await NodeManager.get_one(id=org1.id, branch=branch1, db=db) await org1_branch.tags.update(data=[blue, red], db=db) @@ -130,8 +147,9 @@ async def test_merge_relationship_many( await org1_main.tags.update(data=[blue, yellow], db=db) await org1_main.save(db=db) - merger = BranchMerger(db=db, source_branch=branch1) - await merger.merge_graph() + await diff_coordinator.update_branch_diff(base_branch=default_branch, diff_branch=branch1) + diff_merger = await component_registry.get_component(DiffMerger, db=db, branch=branch1) + await diff_merger.merge_graph(at=Timestamp()) org1_main = await NodeManager.get_one(id=org1.id, db=db) assert len(await org1_main.tags.get(db=db)) == 3 @@ -178,7 +196,16 @@ async def test_merge_update_schema( ) schema_branch = registry.schema.get_schema_branch(name=branch2.name) - merger = BranchMerger(db=db, source_branch=branch2, destination_branch=default_branch) + component_registry = get_component_registry() + diff_coordinator = await component_registry.get_component(DiffCoordinator, db=db, branch=branch2) + diff_merger = await component_registry.get_component(DiffMerger, db=db, branch=branch2) + merger = BranchMerger( + db=db, + diff_coordinator=diff_coordinator, + diff_merger=diff_merger, + source_branch=branch2, + destination_branch=default_branch, + ) assert await merger.update_schema() is True assert sorted(merger.migrations, key=lambda x: x.path.get_path()) == sorted( [ diff --git a/backend/tests/unit/core/test_branch_rebase.py b/backend/tests/unit/core/test_branch_rebase.py index a0fe595ded..9a7615cd9c 100644 --- a/backend/tests/unit/core/test_branch_rebase.py +++ b/backend/tests/unit/core/test_branch_rebase.py @@ -1,9 +1,13 @@ +import pytest + from infrahub.core.branch import Branch +from infrahub.core.branch.tasks import rebase_branch from infrahub.core.constants import InfrahubKind from infrahub.core.initialization import create_branch from infrahub.core.manager import NodeManager from infrahub.core.node import Node from infrahub.database import InfrahubDatabase +from infrahub.exceptions import ValidationError async def test_rebase_graph(db: InfrahubDatabase, base_dataset_02, register_core_models_schema): @@ -79,3 +83,23 @@ async def test_merge_relationship_many( # All Relationship are in BRANCH1 after the REBASE org1_branch = await NodeManager.get_one(id=org1.id, branch=branch1, db=db) assert len(await org1_branch.tags.get(db=db)) == 3 + + +async def test_branch_rebase_diff_conflict( + db: InfrahubDatabase, + default_branch: Branch, + workflow_local, + init_service, + car_person_schema, + car_camry_main, +): + branch2 = await create_branch(db=db, branch_name="branch2") + car_main = await NodeManager.get_one(db=db, id=car_camry_main.id) + car_main.name.value += "-main" + await car_main.save(db=db) + car_branch = await NodeManager.get_one(db=db, branch=branch2, id=car_camry_main.id) + car_branch.name.value += "-branch" + await car_branch.save(db=db) + + with pytest.raises(ValidationError, match="contains conflicts with the default branch that must be addressed"): + await rebase_branch(branch=branch2.name) diff --git a/backend/tests/unit/core/test_manager_node.py b/backend/tests/unit/core/test_manager_node.py index f256f8146b..f0b5c476de 100644 --- a/backend/tests/unit/core/test_manager_node.py +++ b/backend/tests/unit/core/test_manager_node.py @@ -1,5 +1,5 @@ import pytest -from infrahub_sdk import UUIDT +from infrahub_sdk.uuidt import UUIDT from infrahub.core.branch import Branch from infrahub.core.initialization import create_branch @@ -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_node.py b/backend/tests/unit/core/test_node.py index ccc01c2df1..0707311247 100644 --- a/backend/tests/unit/core/test_node.py +++ b/backend/tests/unit/core/test_node.py @@ -1,5 +1,5 @@ import pytest -from infrahub_sdk import UUIDT +from infrahub_sdk.uuidt import UUIDT from infrahub.core import registry from infrahub.core.branch import Branch diff --git a/backend/tests/unit/core/test_node_get_list_query.py b/backend/tests/unit/core/test_node_get_list_query.py index e69c687e2c..7818a570ae 100644 --- a/backend/tests/unit/core/test_node_get_list_query.py +++ b/backend/tests/unit/core/test_node_get_list_query.py @@ -13,8 +13,10 @@ from infrahub.core.node import Node from infrahub.core.query.node import NodeGetListQuery from infrahub.core.registry import registry +from infrahub.core.schema import SchemaRoot from infrahub.core.schema.relationship_schema import RelationshipSchema from infrahub.database import InfrahubDatabase +from tests.helpers.schema import WIDGET async def test_query_NodeGetListQuery( @@ -818,3 +820,32 @@ async def test_query_NodeGetListQuery_multiple_profiles_same_priority_filter_and query = await NodeGetListQuery.init(db=db, branch=branch, schema=car_schema) await query.execute(db=db) assert query.get_node_ids() == [car_camry_main.id, car_accord_main.id] + + +async def test_query_NodeGetListQuery_pagination_order_by( + db: InfrahubDatabase, default_branch: Branch, node_group_schema +): + """Validate that pagination works for nodes which have an order_by clause on non unique attributes.""" + schema_root = SchemaRoot(nodes=[WIDGET]) + + registry.schema.register_schema(schema=schema_root, branch=default_branch.name) + + widget_schema = registry.schema.get_node_schema("TestingWidget", branch=default_branch, duplicate=False) + + for i in range(20): + car_profile = await Node.init(db=db, schema=widget_schema, branch=default_branch) + await car_profile.new(db=db, name="top-widget", description=f"widget index {i}") + await car_profile.save(db=db) + + node_ids = set() + for offset in range(0, 19, 2): + query = await NodeGetListQuery.init(db=db, branch=default_branch, schema=widget_schema, limit=2, offset=offset) + await query.execute(db=db) + + result_ids = query.get_node_ids() + node_ids.update(result_ids) + + # If we don't get 20 results it means that the pagination is returning the same node multiple times + assert len(node_ids) == 20 + # Validate that the order_by clause hasn't changed on the test schema which would defeat the purpose of this test + assert widget_schema.order_by == ["name__value"] 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_relationships_rebase.py b/backend/tests/unit/core/test_relationships_rebase.py new file mode 100644 index 0000000000..3eb3c0f098 --- /dev/null +++ b/backend/tests/unit/core/test_relationships_rebase.py @@ -0,0 +1,356 @@ +from random import choice +from typing import Any, NamedTuple + +import pytest + +from infrahub.core.branch import Branch +from infrahub.core.initialization import create_branch +from infrahub.core.manager import NodeManager +from infrahub.core.node import Node +from infrahub.core.schema.schema_branch import SchemaBranch +from infrahub.core.timestamp import Timestamp +from infrahub.database import InfrahubDatabase + + +class DatabaseEdge(NamedTuple): + type: str + branch: str + status: str + from_time: Timestamp + to_time: Timestamp | None + + +class DatabasePath(NamedTuple): + uuid: str + peer_or_value: Any + edge_1: DatabaseEdge + edge_2: DatabaseEdge + + +async def get_database_edges_state( + db: InfrahubDatabase, + node_uuids: list[str], + rel_identiers: list[str], +) -> set[DatabasePath]: + query = """ + MATCH (n:Node)-[r1:IS_RELATED]-(r:Relationship)-[r2]-(peer) + WHERE n.uuid in $node_uuids + AND r.name in $rel_identifiers + AND r1.branch = r2.branch + RETURN n, r1, r2, peer + """ + results = await db.execute_query(query=query, params={"node_uuids": node_uuids, "rel_identifiers": rel_identiers}) + retrieved_path_tuples = set() + for result in results: + node_uuid = result.get("n").get("uuid") + r1 = result.get("r1") + r2 = result.get("r2") + edges_tuples = [] + for edge in [r1, r2]: + to_time_str = r1.get("to") + to_time = Timestamp(to_time_str) if to_time_str else None + edges_tuples.append( + DatabaseEdge( + type=edge.type, + branch=edge.get("branch"), + status=edge.get("status"), + from_time=Timestamp(edge.get("from")), + to_time=to_time, + ) + ) + peer = result.get("peer") + peer_id_or_value = peer.get("uuid", peer.get("value")) + retrieved_path_tuples.add( + DatabasePath(uuid=node_uuid, edge_1=edges_tuples[0], edge_2=edges_tuples[1], peer_or_value=peer_id_or_value) + ) + return retrieved_path_tuples + + +class TestRelationshipsWithRebase: + async def verify_database_state_cardinality_one( + self, + db: InfrahubDatabase, + car_uuid: str, + main_peer_id: str, + branch_peer_id: str, + branch_name: str, + rebase_time: Timestamp, + ): + database_paths = await get_database_edges_state( + db=db, node_uuids=[car_uuid], rel_identiers=["testcar__testperson"] + ) + # node_uuid, (type, branch, status, from_time == rebased_time, to_time == rebased_time) * 2, uuid OR value + retrieved_path_tuples = { + ( + db_path.uuid, + ( + db_path.edge_1.type, + db_path.edge_1.branch, + db_path.edge_1.status, + db_path.edge_1.from_time == rebase_time, + db_path.edge_1.to_time == rebase_time, + ), + ( + db_path.edge_2.type, + db_path.edge_2.branch, + db_path.edge_2.status, + db_path.edge_2.from_time == rebase_time, + db_path.edge_2.to_time == rebase_time, + ), + db_path.peer_or_value, + ) + for db_path in database_paths + } + + expected_path_tuples = { + ( + car_uuid, + ("IS_RELATED", "main", "active", False, False), + ("IS_RELATED", "main", "active", False, False), + main_peer_id, + ), + ( + car_uuid, + ("IS_RELATED", "main", "active", False, False), + ("IS_VISIBLE", "main", "active", False, False), + True, + ), + ( + car_uuid, + ("IS_RELATED", "main", "active", False, False), + ("IS_PROTECTED", "main", "active", False, False), + False, + ), + ( + car_uuid, + ("IS_RELATED", branch_name, "deleted", True, False), + ("IS_RELATED", branch_name, "deleted", True, False), + main_peer_id, + ), + ( + car_uuid, + ("IS_RELATED", branch_name, "deleted", True, False), + ("IS_VISIBLE", branch_name, "deleted", True, False), + True, + ), + ( + car_uuid, + ("IS_RELATED", branch_name, "deleted", True, False), + ("IS_PROTECTED", branch_name, "deleted", True, False), + False, + ), + ( + car_uuid, + ("IS_RELATED", branch_name, "active", True, False), + ("IS_RELATED", branch_name, "active", True, False), + branch_peer_id, + ), + ( + car_uuid, + ("IS_RELATED", branch_name, "active", True, False), + ("IS_VISIBLE", branch_name, "active", True, False), + True, + ), + ( + car_uuid, + ("IS_RELATED", branch_name, "active", True, False), + ("IS_PROTECTED", branch_name, "active", True, False), + False, + ), + } + + assert expected_path_tuples == retrieved_path_tuples + + @pytest.mark.parametrize("num_updates", [2, 3]) + async def test_rebase_cardinality_one( + self, + db: InfrahubDatabase, + default_branch: Branch, + car_person_schema: SchemaBranch, + person_john_main: Node, + person_alfred_main: Node, + person_jane_main: Node, + person_jim_main: Node, + car_accord_main: Node, + num_updates: int, + ): + branch_2 = await create_branch(db=db, branch_name="branch_2") + car_branch = await NodeManager.get_one(db=db, id=car_accord_main.id, branch=branch_2) + await car_branch.owner.update(db=db, data=person_alfred_main) + await car_branch.save(db=db) + if num_updates > 2: + car_branch = await NodeManager.get_one(db=db, id=car_accord_main.id, branch=branch_2) + await car_branch.owner.update(db=db, data=person_jim_main) + await car_branch.save(db=db) + car_branch = await NodeManager.get_one(db=db, id=car_accord_main.id, branch=branch_2) + await car_branch.owner.update(db=db, data=person_jane_main) + await car_branch.save(db=db) + + rebase_time = Timestamp() + await branch_2.rebase(db=db, at=rebase_time) + + rebased_car = await NodeManager.get_one(db=db, branch=branch_2, id=car_branch.id) + owner_peer = await rebased_car.owner.get_peer(db=db) + assert owner_peer.id == person_jane_main.id + main_car = await NodeManager.get_one(db=db, id=car_branch.id) + owner_peer = await main_car.owner.get_peer(db=db) + assert owner_peer.id == person_john_main.id + await self.verify_database_state_cardinality_one( + db=db, + car_uuid=car_accord_main.id, + main_peer_id=person_john_main.id, + branch_peer_id=person_jane_main.id, + branch_name=branch_2.name, + rebase_time=rebase_time, + ) + + async def verify_database_state_cardinality_many( + self, + db: InfrahubDatabase, + person_uuids: list[str], + car_person_id_map_main: dict[str, str], + car_person_id_map_branch: dict[str, str], + branch_name: str, + rebase_time: Timestamp, + ): + database_paths = await get_database_edges_state( + db=db, node_uuids=person_uuids, rel_identiers=["testcar__testperson"] + ) + retrieved_path_tuples = { + ( + db_path.uuid, + ( + db_path.edge_1.type, + db_path.edge_1.branch, + db_path.edge_1.status, + db_path.edge_1.from_time == rebase_time, + db_path.edge_1.to_time == rebase_time, + ), + ( + db_path.edge_2.type, + db_path.edge_2.branch, + db_path.edge_2.status, + db_path.edge_2.from_time == rebase_time, + db_path.edge_2.to_time == rebase_time, + ), + db_path.peer_or_value, + ) + for db_path in database_paths + } + + expected_path_tuples = set() + for person_uuid in person_uuids: + expected_car_ids_main = {c_id for c_id, p_id in car_person_id_map_main.items() if p_id == person_uuid} + for car_id in expected_car_ids_main: + expected_path_tuples.update( + ( + person_uuid, + ("IS_RELATED", "main", "active", False, False), + (edge_type, "main", "active", False, False), + peer_or_value, + ) + for edge_type, peer_or_value in ( + ("IS_RELATED", car_id), + ("IS_VISIBLE", True), + ("IS_PROTECTED", False), + ) + ) + expected_path_tuples.update( + ( + person_uuid, + ("IS_RELATED", branch_name, "deleted", True, False), + (edge_type, branch_name, "deleted", True, False), + peer_or_value, + ) + for edge_type, peer_or_value in ( + ("IS_RELATED", car_id), + ("IS_VISIBLE", True), + ("IS_PROTECTED", False), + ) + ) + + expected_car_ids_branch = {c_id for c_id, p_id in car_person_id_map_branch.items() if p_id == person_uuid} + for car_id in expected_car_ids_branch: + expected_path_tuples.update( + ( + person_uuid, + ("IS_RELATED", branch_name, "active", True, False), + (edge_type, branch_name, "active", True, False), + peer_or_value, + ) + for edge_type, peer_or_value in ( + ("IS_RELATED", car_id), + ("IS_VISIBLE", True), + ("IS_PROTECTED", False), + ) + ) + + for ept in expected_path_tuples: + assert ept in retrieved_path_tuples + + assert expected_path_tuples == retrieved_path_tuples + + async def test_rebase_cardinality_many( + self, + db: InfrahubDatabase, + default_branch: Branch, + car_person_schema: SchemaBranch, + ): + people = [] + cars = [] + car_person_id_map_main = {} + for i in range(3): + person = await Node.init(db=db, schema="TestPerson") + await person.new(db=db, name=f"Person{i}") + await person.save(db=db) + people.append(person) + for j in range(2): + car = await Node.init(db=db, schema="TestCar") + await car.new(db=db, name=f"Car{i}{j}", owner=person) + await car.save(db=db) + cars.append(car) + car_person_id_map_main[car.id] = person.id + branch_2 = await create_branch(db=db, branch_name="branch_2") + # make a bunch of branch updates + car_person_id_map_branch = {} + for _ in range(3): + for car in cars: + branch_car = await NodeManager.get_one(db=db, branch=branch_2, id=car.id) + owner_peer = await branch_car.owner.get_peer(db=db) + random_person = choice([p for p in people if p.id != owner_peer.id]) + await branch_car.owner.update(db=db, data=random_person) + await branch_car.save(db=db) + car_person_id_map_branch[car.id] = random_person.id + + rebase_time = Timestamp() + await branch_2.rebase(db=db, at=rebase_time) + + for person in people: + main_person = await NodeManager.get_one(db=db, branch=default_branch, id=person.id) + expected_car_ids = {c_id for c_id, p_id in car_person_id_map_main.items() if p_id == person.id} + car_peers = await main_person.cars.get_peers(db=db) + retrieved_car_ids = set(car_peers.keys()) + assert expected_car_ids == retrieved_car_ids + for car in cars: + main_car = await NodeManager.get_one(db=db, branch=default_branch, id=car.id) + owner_peer = await main_car.owner.get_peer(db=db) + assert owner_peer.id == car_person_id_map_main[car.id] + + for person in people: + rebased_person = await NodeManager.get_one(db=db, branch=branch_2, id=person.id) + expected_car_ids = {c_id for c_id, p_id in car_person_id_map_branch.items() if p_id == person.id} + car_peers = await rebased_person.cars.get_peers(db=db) + retrieved_car_ids = set(car_peers.keys()) + assert expected_car_ids == retrieved_car_ids + for car in cars: + rebased_car = await NodeManager.get_one(db=db, branch=branch_2, id=car.id) + owner_peer = await rebased_car.owner.get_peer(db=db) + assert owner_peer.id == car_person_id_map_branch[car.id] + await self.verify_database_state_cardinality_many( + db=db, + person_uuids=[p.id for p in people], + car_person_id_map_main=car_person_id_map_main, + car_person_id_map_branch=car_person_id_map_branch, + branch_name=branch_2.name, + rebase_time=rebase_time, + ) 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/conftest.py b/backend/tests/unit/git/conftest.py index 2267f1c7a5..c1702aaf24 100644 --- a/backend/tests/unit/git/conftest.py +++ b/backend/tests/unit/git/conftest.py @@ -8,9 +8,11 @@ import pytest import ujson from git import Repo -from infrahub_sdk import UUIDT, Config, InfrahubClient, InfrahubNode -from infrahub_sdk import SchemaRoot as ClientSchemaRoot +from infrahub_sdk import Config, InfrahubClient from infrahub_sdk.branch import BranchData +from infrahub_sdk.node import InfrahubNode +from infrahub_sdk.schema import SchemaRoot as ClientSchemaRoot +from infrahub_sdk.uuidt import UUIDT from pytest_httpx import HTTPXMock from infrahub.core.constants import InfrahubKind diff --git a/backend/tests/unit/git/test_git_read_only_repository.py b/backend/tests/unit/git/test_git_read_only_repository.py index edbf63f2bb..e57076e2ab 100644 --- a/backend/tests/unit/git/test_git_read_only_repository.py +++ b/backend/tests/unit/git/test_git_read_only_repository.py @@ -2,8 +2,8 @@ from typing import Dict from unittest.mock import AsyncMock -from infrahub_sdk import UUIDT from infrahub_sdk.client import Config, InfrahubClient +from infrahub_sdk.uuidt import UUIDT from infrahub.git.repository import InfrahubReadOnlyRepository from tests.helpers.test_client import dummy_async_request diff --git a/backend/tests/unit/git/test_git_repository.py b/backend/tests/unit/git/test_git_repository.py index 9b1115c9f6..561e8f8d58 100644 --- a/backend/tests/unit/git/test_git_repository.py +++ b/backend/tests/unit/git/test_git_repository.py @@ -3,8 +3,10 @@ import pytest from git import Repo -from infrahub_sdk import UUIDT, Config, InfrahubClient, InfrahubNode +from infrahub_sdk import Config, InfrahubClient from infrahub_sdk.branch import BranchData +from infrahub_sdk.node import InfrahubNode +from infrahub_sdk.uuidt import UUIDT from pytest_httpx._httpx_mock import HTTPXMock from infrahub.core.constants import InfrahubKind diff --git a/backend/tests/unit/git/test_git_rpc.py b/backend/tests/unit/git/test_git_rpc.py index 3150e444ee..c4edf9831d 100644 --- a/backend/tests/unit/git/test_git_rpc.py +++ b/backend/tests/unit/git/test_git_rpc.py @@ -1,9 +1,11 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Optional +from typing import TYPE_CHECKING, Any from unittest.mock import AsyncMock, patch -from infrahub_sdk import UUIDT, Config, InfrahubClient +from infrahub_sdk import Config, InfrahubClient +from infrahub_sdk.uuidt import UUIDT +from typing_extensions import Self from infrahub.core.constants import InfrahubKind, RepositoryInternalStatus from infrahub.exceptions import RepositoryError @@ -27,18 +29,18 @@ class AsyncContextManagerMock: - async def __aenter__(self, *args: Any, **kwargs: Any): + async def __aenter__(self, *args: Any, **kwargs: Any) -> Self: return self async def __aexit__( self, - exc_type: Optional[type[BaseException]], - exc_value: Optional[BaseException], - traceback: Optional[TracebackType], - ): + exc_type: type[BaseException] | None, + exc_value: BaseException | None, + traceback: TracebackType | None, + ) -> None: pass - def __call__(self, *args: Any, **kwargs: Any): + def __call__(self, *args: Any, **kwargs: Any) -> Self: return self diff --git a/backend/tests/unit/git/test_git_transform.py b/backend/tests/unit/git/test_git_transform.py index 8909f82671..43b709f6a3 100644 --- a/backend/tests/unit/git/test_git_transform.py +++ b/backend/tests/unit/git/test_git_transform.py @@ -1,14 +1,27 @@ -from infrahub_sdk import UUIDT +import pytest +from infrahub_sdk import InfrahubClient +from infrahub_sdk.uuidt import UUIDT from infrahub.core.constants import InfrahubKind +from infrahub.exceptions import RepositoryFileNotFoundError, TransformError from infrahub.git import InfrahubRepository -from infrahub.message_bus import messages -from infrahub.services import InfrahubServices +from infrahub.services import InfrahubServices, services +from infrahub.transformations.models import TransformJinjaTemplateData, TransformPythonData +from infrahub.transformations.tasks import transform_python, transform_render_jinja2_template -async def test_git_transform_jinja2_success(git_repo_jinja: InfrahubRepository, helper): +@pytest.fixture +def init_service(): + original = services.service + service = InfrahubServices(client=InfrahubClient()) + services.service = service + yield service + services.service = original + + +async def test_git_transform_jinja2_success(git_repo_jinja: InfrahubRepository, prefect_test_fixture, helper): commit = git_repo_jinja.get_commit_value(branch_name="main") - message = messages.TransformJinjaTemplate( + message = TransformJinjaTemplateData( repository_id=str(UUIDT()), repository_name=git_repo_jinja.name, repository_kind=InfrahubKind.REPOSITORY, @@ -23,20 +36,14 @@ async def test_git_transform_jinja2_success(git_repo_jinja: InfrahubRepository, album magnum """ + response = await transform_render_jinja2_template(message=message) + assert response == expected_response - bus_simulator = helper.get_message_bus_simulator() - service = InfrahubServices(message_bus=bus_simulator) - bus_simulator.service = service - - reply = await service.message_bus.rpc(message=message, response_class=messages.TransformJinjaTemplateResponse) - assert reply.passed - assert reply.data.rendered_template == expected_response - -async def test_git_transform_jinja2_missing(git_repo_jinja: InfrahubRepository, helper): +async def test_git_transform_jinja2_missing(git_repo_jinja: InfrahubRepository, prefect_test_fixture, helper): commit = git_repo_jinja.get_commit_value(branch_name="main") - message = messages.TransformJinjaTemplate( + message = TransformJinjaTemplateData( repository_id=str(UUIDT()), repository_name=git_repo_jinja.name, repository_kind=InfrahubKind.REPOSITORY, @@ -46,19 +53,16 @@ async def test_git_transform_jinja2_missing(git_repo_jinja: InfrahubRepository, data={"data": {"items": ["consilium", "potum", "album", "magnum"]}}, ) - bus_simulator = helper.get_message_bus_simulator() - service = InfrahubServices(message_bus=bus_simulator) - bus_simulator.service = service + with pytest.raises(RepositoryFileNotFoundError) as exc: + await transform_render_jinja2_template(message=message) - reply = await service.message_bus.rpc(message=message, response_class=messages.TransformJinjaTemplateResponse) - assert not reply.passed - assert "Unable to find the file" in reply.errors[0] + assert "Unable to find the file" in exc.value.message -async def test_git_transform_jinja2_invalid(git_repo_jinja: InfrahubRepository, helper, caplog): +async def test_git_transform_jinja2_invalid(git_repo_jinja: InfrahubRepository, prefect_test_fixture, helper, caplog): commit = git_repo_jinja.get_commit_value(branch_name="main") - message = messages.TransformJinjaTemplate( + message = TransformJinjaTemplateData( repository_id=str(UUIDT()), repository_name=git_repo_jinja.name, repository_kind=InfrahubKind.REPOSITORY, @@ -68,10 +72,26 @@ async def test_git_transform_jinja2_invalid(git_repo_jinja: InfrahubRepository, data={"data": {"items": ["consilium", "potum", "album", "magnum"]}}, ) - bus_simulator = helper.get_message_bus_simulator() - service = InfrahubServices(message_bus=bus_simulator) - bus_simulator.service = service + with pytest.raises(TransformError) as exc: + await transform_render_jinja2_template(message=message) + + assert "Encountered unknown tag" in exc.value.message + + +async def test_transform_python_success( + git_fixture_repo: InfrahubRepository, init_service, prefect_test_fixture, helper +): + commit = git_fixture_repo.get_commit_value(branch_name="main") + + message = TransformPythonData( + repository_id=str(git_fixture_repo.id), + repository_name=git_fixture_repo.name, + repository_kind=InfrahubKind.REPOSITORY, + commit=commit, + branch="main", + transform_location="unit/transforms/multiplier.py::Multiplier", + data={"multiplier": 2, "key": "abc", "answer": 21}, + ) - reply = await service.message_bus.rpc(message=message, response_class=messages.TransformJinjaTemplateResponse) - assert not reply.passed - assert "Encountered unknown tag 'end'." in reply.errors[0] + response = await transform_python(message=message) + assert response == {"key": "abcabc", "answer": 42} diff --git a/sync/examples/peering-manager_to_infrahub/peeringmanager/__init__.py b/backend/tests/unit/graphql/auth/query_permission_checker/__init__.py similarity index 100% rename from sync/examples/peering-manager_to_infrahub/peeringmanager/__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..0069c46135 --- /dev/null +++ b/backend/tests/unit/graphql/auth/query_permission_checker/test_default_branch_checker.py @@ -0,0 +1,139 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING +from unittest.mock import MagicMock, patch +from uuid import uuid4 + +import pytest + +from infrahub.auth import AccountSession, AuthType +from infrahub.core.constants import AccountRole, GlobalPermissions, InfrahubKind, 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.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, action=GlobalPermissions.EDIT_DEFAULT_BRANCH.value, decision=PermissionDecision.ALLOW_ALL.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() + with patch("infrahub.config.SETTINGS.main.allow_anonymous_access", False): + 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_names = ["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_names = ["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..c2fe3079df --- /dev/null +++ b/backend/tests/unit/graphql/auth/query_permission_checker/test_merge_operation_checker.py @@ -0,0 +1,142 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING +from unittest.mock import AsyncMock, MagicMock, patch +from uuid import uuid4 + +import pytest + +from infrahub.auth import AccountSession, AuthType +from infrahub.core.constants import AccountRole, GlobalPermissions, InfrahubKind, 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.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, action=GlobalPermissions.MERGE_BRANCH.value, decision=PermissionDecision.ALLOW_ALL.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() + with patch("infrahub.config.SETTINGS.main.allow_anonymous_access", False): + 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..7a6de5da22 --- /dev/null +++ b/backend/tests/unit/graphql/auth/query_permission_checker/test_object_permission_checker.py @@ -0,0 +1,706 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING +from unittest.mock import patch +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( + namespace="Builtin", + name="*", + action=PermissionAction.ANY.value, + decision=PermissionDecision.ALLOW_DEFAULT.value, + ), + ObjectPermission( + namespace="Core", + name="GraphQLQuery", + action=PermissionAction.VIEW.value, + decision=PermissionDecision.ALLOW_DEFAULT.value, + ), + ]: + obj = await Node.init(db=db, schema=InfrahubKind.OBJECTPERMISSION) + await obj.new( + db=db, + 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=r":Repository:view:"): + 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=r"Repository:view:"): + 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, action=GlobalPermissions.MANAGE_ACCOUNTS.value, decision=PermissionDecision.ALLOW_ALL.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() + with patch("infrahub.config.SETTINGS.main.allow_anonymous_access", False): + 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, action=GlobalPermissions.MANAGE_PERMISSIONS.value, decision=PermissionDecision.ALLOW_ALL.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() + with patch("infrahub.config.SETTINGS.main.allow_anonymous_access", False): + 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, action=GlobalPermissions.MANAGE_REPOSITORIES.value, decision=PermissionDecision.ALLOW_ALL.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() + with patch("infrahub.config.SETTINGS.main.allow_anonymous_access", False): + 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..47a5eca97e --- /dev/null +++ b/backend/tests/unit/graphql/auth/query_permission_checker/test_super_admin_checker.py @@ -0,0 +1,100 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING +from unittest.mock import MagicMock, patch +from uuid import uuid4 + +import pytest + +from infrahub.auth import AccountSession, AuthType +from infrahub.core.constants import AccountRole, GlobalPermissions, InfrahubKind, PermissionDecision +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, action=GlobalPermissions.SUPER_ADMIN.value, decision=PermissionDecision.ALLOW_ALL.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() + with patch("infrahub.config.SETTINGS.main.allow_anonymous_access", False): + 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 deleted file mode 100644 index 53fd65efbb..0000000000 --- a/backend/tests/unit/graphql/auth/test_default_checker.py +++ /dev/null @@ -1,28 +0,0 @@ -from unittest.mock import AsyncMock - -import pytest - -from infrahub.auth import AccountSession, AuthType -from infrahub.core.constants import AccountRole -from infrahub.exceptions import AuthorizationError -from infrahub.graphql.analyzer import InfrahubGraphQLQueryAnalyzer -from infrahub.graphql.auth.query_permission_checker.default_checker import DefaultGraphQLPermissionChecker - - -class TestDefaultAuthChecker: - def setup_method(self): - self.account_session = AccountSession(account_id="abc", auth_type=AuthType.JWT) - self.graphql_query = AsyncMock(spec=InfrahubGraphQLQueryAnalyzer) - self.checker = DefaultGraphQLPermissionChecker() - - @pytest.mark.parametrize("role", [x.value for x in AccountRole]) - async def test_supports_all_accounts(self, role): - self.account_session.role = role - - is_supported = await self.checker.supports(self.account_session) - - assert is_supported is True - - async def test_always_raises_error(self): - with pytest.raises(AuthorizationError): - await self.checker.check(self.graphql_query) 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..2b97a4c57b 100644 --- a/backend/tests/unit/graphql/mutations/test_branch.py +++ b/backend/tests/unit/graphql/mutations/test_branch.py @@ -5,9 +5,10 @@ from infrahub.core.branch import Branch from infrahub.core.constants import InfrahubKind from infrahub.core.initialization import create_branch +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 @@ -329,36 +330,6 @@ async def test_branch_create_with_repositories( assert await Branch.get_by_name(db=db, name="branch2") -async def test_branch_rebase(db: InfrahubDatabase, default_branch: Branch, car_person_schema, session_admin): - branch2 = await create_branch(db=db, branch_name="branch2") - - query = """ - mutation { - BranchRebase(data: { name: "branch2" }) { - ok - object { - id - } - } - } - """ - recorder = BusRecorder() - service = InfrahubServices(message_bus=recorder) - result = await graphql_mutation( - query=query, db=db, branch=default_branch, service=service, account_session=session_admin - ) - - assert result.errors is None - assert result.data - assert result.data["BranchRebase"]["ok"] is True - assert result.data["BranchRebase"]["object"]["id"] == str(branch2.uuid) - - new_branch2 = await Branch.get_by_name(db=db, name="branch2") - assert new_branch2.branched_from != branch2.branched_from - - assert recorder.seen_routing_keys == ["event.branch.rebased"] - - async def test_branch_rebase_wrong_branch( db: InfrahubDatabase, default_branch: Branch, car_person_schema, session_admin ): @@ -453,12 +424,14 @@ async def test_branch_update_description(db: InfrahubDatabase, base_dataset_02): assert branch4_updated.description == "testing" -async def test_branch_merge(db: InfrahubDatabase, base_dataset_02, register_core_models_schema, session_admin): +async def test_branch_merge_wrong_branch( + db: InfrahubDatabase, base_dataset_02, register_core_models_schema, session_admin +): branch1 = await Branch.get_by_name(db=db, name="branch1") query = """ mutation { - BranchMerge(data: { name: "branch1" }) { + BranchMerge(data: { name: "branch99" }) { ok object { id @@ -466,8 +439,10 @@ async def test_branch_merge(db: InfrahubDatabase, base_dataset_02, register_core } } """ + recorder = BusRecorder() + service = InfrahubServices(message_bus=recorder) gql_params = prepare_graphql_params( - db=db, include_subscription=False, branch=branch1, account_session=session_admin + db=db, include_subscription=False, branch=branch1, account_session=session_admin, service=service ) result = await graphql( schema=gql_params.schema, @@ -477,7 +452,44 @@ async def test_branch_merge(db: InfrahubDatabase, base_dataset_02, register_core variable_values={}, ) - assert result.errors is None - assert result.data - assert result.data["BranchMerge"]["ok"] is True - assert result.data["BranchMerge"]["object"]["id"] == str(branch1.uuid) + assert result.errors + assert len(result.errors) == 1 + assert result.errors[0].message == "Branch: branch99 not found." + + +async def test_branch_merge_with_conflict_fails(db: InfrahubDatabase, car_person_schema, car_camry_main, session_admin): + query = """ + mutation { + BranchMerge(data: { name: "branch2" }) { + ok + object { + id + } + } + } + """ + + branch2 = await create_branch(db=db, branch_name="branch2") + car_main = await NodeManager.get_one(db=db, id=car_camry_main.id) + car_main.name.value += "-main" + await car_main.save(db=db) + car_branch = await NodeManager.get_one(db=db, branch=branch2, id=car_camry_main.id) + car_branch.name.value += "-branch" + await car_branch.save(db=db) + + recorder = BusRecorder() + service = InfrahubServices(message_bus=recorder) + gql_params = prepare_graphql_params( + db=db, include_subscription=False, branch=branch2, account_session=session_admin, service=service + ) + result = await graphql( + schema=gql_params.schema, + source=query, + context_value=gql_params.context, + root_value=None, + variable_values={}, + ) + + assert result.errors + assert len(result.errors) == 1 + assert "contains conflicts with the default branch" in result.errors[0].message 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..e50472d6e3 --- /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="/branches", + 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="/branches", + 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..d79793ceb5 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 update_status.errors[0].message == "You do not have the permission to merge proposed changes" + + 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..f6f8a65dd9 --- /dev/null +++ b/backend/tests/unit/graphql/queries/test_list_permissions.py @@ -0,0 +1,409 @@ +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 +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.constants import BranchRelativePermissionDecision, PermissionDecisionFlag +from infrahub.permissions.local_backend import LocalPermissionBackend + +if TYPE_CHECKING: + from infrahub.core.branch import Branch + from infrahub.core.protocols import CoreAccount + from infrahub.core.schema.schema_branch import SchemaBranch + 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 + } + } + } + } +} +""" + + +QUERY_ACCOUNT_ROLE = """ +query { + CoreAccountRole { + edges { + node { + display_label + } + } + permissions { + count + edges { + node { + kind + create + update + delete + view + } + } + } + } +} +""" + + +class TestObjectPermissions: + async def test_setup( + self, + db: InfrahubDatabase, + register_core_models_schema: SchemaBranch, + 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( + namespace="Builtin", + name="*", + action=PermissionAction.VIEW.value, + decision=PermissionDecisionFlag.ALLOW_ALL, + ), + ObjectPermission( + namespace="Builtin", + name="*", + action=PermissionAction.CREATE.value, + decision=PermissionDecisionFlag.ALLOW_OTHER, + ), + ObjectPermission( + namespace="Builtin", + name="*", + action=PermissionAction.DELETE.value, + decision=PermissionDecisionFlag.ALLOW_OTHER, + ), + ObjectPermission( + namespace="Core", + name="*", + action=PermissionAction.ANY.value, + decision=PermissionDecisionFlag.ALLOW_OTHER, + ), + ObjectPermission( + namespace="Core", + name="*", + action=PermissionAction.VIEW.value, + decision=PermissionDecisionFlag.ALLOW_ALL, + ), + ]: + obj = await Node.init(db=db, schema=InfrahubKind.OBJECTPERMISSION) + await obj.new( + db=db, + 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(self, db: InfrahubDatabase, permissions_helper: PermissionsHelper) -> None: + """In the main branch the first account doesn't have the permission to make changes, but it has in the other branches""" + 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": BranchRelativePermissionDecision.ALLOW_OTHER.name, + "update": BranchRelativePermissionDecision.DENY.name, + "delete": BranchRelativePermissionDecision.ALLOW_OTHER.name, + "view": BranchRelativePermissionDecision.ALLOW.name, + } + } + + 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": BranchRelativePermissionDecision.ALLOW.name, + "update": BranchRelativePermissionDecision.DENY.name, + "delete": BranchRelativePermissionDecision.ALLOW.name, + "view": BranchRelativePermissionDecision.ALLOW.name, + } + } + + 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": BranchRelativePermissionDecision.ALLOW_OTHER.name, + "update": BranchRelativePermissionDecision.ALLOW_OTHER.name, + "delete": BranchRelativePermissionDecision.ALLOW_OTHER.name, + "view": BranchRelativePermissionDecision.ALLOW.name, + } + } in result.data["CoreGenericRepository"]["permissions"]["edges"] + assert { + "node": { + "kind": "CoreRepository", + "create": BranchRelativePermissionDecision.ALLOW_OTHER.name, + "update": BranchRelativePermissionDecision.ALLOW_OTHER.name, + "delete": BranchRelativePermissionDecision.ALLOW_OTHER.name, + "view": BranchRelativePermissionDecision.ALLOW.name, + } + } in result.data["CoreGenericRepository"]["permissions"]["edges"] + assert { + "node": { + "kind": "CoreReadOnlyRepository", + "create": BranchRelativePermissionDecision.ALLOW_OTHER.name, + "update": BranchRelativePermissionDecision.ALLOW_OTHER.name, + "delete": BranchRelativePermissionDecision.ALLOW_OTHER.name, + "view": BranchRelativePermissionDecision.ALLOW.name, + } + } in result.data["CoreGenericRepository"]["permissions"]["edges"] + + async def test_first_account_account_role( + self, db: InfrahubDatabase, permissions_helper: PermissionsHelper + ) -> None: + """In the main branch the first account doesn't have the permission to make changes, but it has in the other branches""" + 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_ACCOUNT_ROLE, context_value=gql_params.context) + + assert not result.errors + assert result.data + assert result.data["CoreAccountRole"]["permissions"]["count"] == 1 + assert result.data["CoreAccountRole"]["permissions"]["edges"][0] == { + "node": { + "kind": "CoreAccountRole", + "create": BranchRelativePermissionDecision.ALLOW_OTHER.name, + "update": BranchRelativePermissionDecision.ALLOW_OTHER.name, + "delete": BranchRelativePermissionDecision.ALLOW_OTHER.name, + "view": BranchRelativePermissionDecision.ALLOW.name, + } + } + assert result.data["CoreAccountRole"]["edges"][0]["node"]["display_label"] == "admin" + + +QUERY_TAGS_ATTR = """ +query { + BuiltinTag { + count + edges { + node { + name { + value + permissions { + update_value + } + } + } + } + } +} +""" + + +class TestAttributePermissions: + async def test_setup( + self, + db: InfrahubDatabase, + register_core_models_schema: SchemaBranch, + 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( + namespace="Builtin", + name="*", + action=PermissionAction.VIEW.value, + decision=PermissionDecisionFlag.ALLOW_ALL, + ), + ObjectPermission( + namespace="Builtin", + name="*", + action=PermissionAction.CREATE.value, + decision=PermissionDecisionFlag.ALLOW_ALL, + ), + ObjectPermission( + namespace="Builtin", + name="*", + action=PermissionAction.DELETE.value, + decision=PermissionDecisionFlag.ALLOW_ALL, + ), + ObjectPermission( + namespace="Builtin", + name="*", + action=PermissionAction.UPDATE.value, + decision=PermissionDecisionFlag.ALLOW_OTHER, + ), + ]: + obj = await Node.init(db=db, schema=InfrahubKind.OBJECTPERMISSION) + await obj.new( + db=db, + 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) + + tag = await Node.init(db=db, schema=InfrahubKind.TAG) + await tag.new(db=db, name="Blue", description="Blue tag") + await tag.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, so attribute cannot be changed""" + 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_ATTR, context_value=gql_params.context) + + assert not result.errors + assert result.data + assert result.data["BuiltinTag"]["count"] == 1 + assert result.data["BuiltinTag"]["edges"][0]["node"]["name"]["permissions"] == { + "update_value": BranchRelativePermissionDecision.ALLOW_OTHER.name + } + + 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, attribute should be updatable""" + 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_ATTR, context_value=gql_params.context) + + assert not result.errors + assert result.data + assert result.data["BuiltinTag"]["count"] == 1 + assert result.data["BuiltinTag"]["edges"][0]["node"]["name"]["permissions"] == { + "update_value": BranchRelativePermissionDecision.ALLOW.name + } 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..dcafe219cf 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 @@ -633,6 +633,7 @@ async def test_number_pool_utilization(db: InfrahubDatabase, default_branch: Bra assert first.data assert second.data assert third.data + second_id = second.data["TestingTicketCreate"]["object"]["id"] utilization = await graphql( schema=gql_params.schema, @@ -670,6 +671,32 @@ async def test_number_pool_utilization(db: InfrahubDatabase, default_branch: Bra numbers = [entry["node"]["display_label"] for entry in allocation.data["InfrahubResourcePoolAllocated"]["edges"]] assert sorted(numbers) == ["1", "2", "3"] + remove_two = await graphql( + schema=gql_params.schema, + source=DELETE_TICKET, + context_value=gql_params.context, + root_value=None, + variable_values={"id": second_id}, + ) + assert not remove_two.errors + + allocation = await graphql( + schema=gql_params.schema, + source=POOL_ALLOCATION, + context_value=gql_params.context, + root_value=None, + variable_values={ + "pool_id": pool_id, + "resource_id": pool_id, + }, + ) + + assert not allocation.errors + assert allocation.data + assert allocation.data["InfrahubResourcePoolAllocated"]["count"] == 2 + numbers = [entry["node"]["display_label"] for entry in allocation.data["InfrahubResourcePoolAllocated"]["edges"]] + assert sorted(numbers) == ["1", "3"] + CREATE_NUMBER_POOL = """ mutation CreateNumberPool( @@ -720,6 +747,14 @@ async def test_number_pool_utilization(db: InfrahubDatabase, default_branch: Bra } """ +DELETE_TICKET = """ +mutation DeleteTicket($id: String!) { + TestingTicketDelete(data: {id: $id}) { + ok + } +} +""" + POOL_UTILIZATION = """ query PoolUtilization($pool_id: String!) { InfrahubResourcePoolUtilization(pool_id: $pool_id) { 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..45072d1095 --- /dev/null +++ b/backend/tests/unit/graphql/test_core_account.py @@ -0,0 +1,115 @@ +import bcrypt +import pytest +from graphql import graphql + +from infrahub.auth import AccountSession, AuthType +from infrahub.core import registry +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 +from infrahub.permissions.local_backend import LocalPermissionBackend + + +@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 +): + registry.permission_backends = [LocalPermissionBackend()] + 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(action=GlobalPermissions.SUPER_ADMIN.value, decision=PermissionDecision.ALLOW_ALL.value)) + ] + + perms = [edge["node"]["identifier"] for edge in result.data["InfrahubPermissions"]["object_permissions"]["edges"]] + assert perms == [ + str( + ObjectPermission( + namespace="*", name="*", action=PermissionAction.ANY.value, decision=PermissionDecision.ALLOW_ALL.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..a389e278af 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" @@ -215,7 +215,7 @@ async def test_diff_tree_no_changes( diff_branch: Branch, ): enriched_diff = await diff_coordinator.update_branch_diff(base_branch=default_branch, diff_branch=diff_branch) - from_time = datetime.fromisoformat(diff_branch.created_at) + from_time = datetime.fromisoformat(diff_branch.branched_from) to_time = datetime.fromisoformat(enriched_diff.to_time.to_string()) params = prepare_graphql_params(db=db, include_mutation=False, include_subscription=False, branch=default_branch) @@ -292,7 +292,8 @@ async def test_diff_tree_one_attr_change( after_change_datetime = datetime.now(tz=UTC) enriched_diff = await diff_coordinator.update_branch_diff(base_branch=default_branch, diff_branch=diff_branch) - enriched_conflict = enriched_diff.get_all_conflicts()[0] + enriched_conflict_map = enriched_diff.get_all_conflicts() + enriched_conflict = list(enriched_conflict_map.values())[0] await diff_repository.update_conflict_by_id( conflict_id=enriched_conflict.uuid, selection=ConflictSelection.DIFF_BRANCH ) @@ -312,7 +313,7 @@ async def test_diff_tree_one_attr_change( root_value=None, variable_values={"branch": diff_branch.name}, ) - from_time = datetime.fromisoformat(diff_branch.created_at) + from_time = datetime.fromisoformat(diff_branch.branched_from) to_time = datetime.fromisoformat(enriched_diff.to_time.to_string()) assert result.errors is None @@ -425,7 +426,7 @@ async def test_diff_tree_one_relationship_change( root_value=None, variable_values={"branch": diff_branch.name}, ) - from_time = datetime.fromisoformat(diff_branch.created_at) + from_time = datetime.fromisoformat(diff_branch.branched_from) to_time = datetime.fromisoformat(enriched_diff.to_time.to_string()) assert result.errors is None @@ -705,7 +706,7 @@ async def test_diff_tree_summary_no_changes( diff_branch: Branch, ): enriched_diff = await diff_coordinator.update_branch_diff(base_branch=default_branch, diff_branch=diff_branch) - from_time = datetime.fromisoformat(diff_branch.created_at) + from_time = datetime.fromisoformat(diff_branch.branched_from) to_time = datetime.fromisoformat(enriched_diff.to_time.to_string()) params = prepare_graphql_params(db=db, include_mutation=False, include_subscription=False, branch=default_branch) diff --git a/backend/tests/unit/graphql/test_graphql_partial_match.py b/backend/tests/unit/graphql/test_graphql_partial_match.py index e1a5931b19..2bbb7dfdb1 100644 --- a/backend/tests/unit/graphql/test_graphql_partial_match.py +++ b/backend/tests/unit/graphql/test_graphql_partial_match.py @@ -217,6 +217,7 @@ async def test_query_filter_local_attrs_partial_match_values( assert result.errors is None assert result.data assert len(result.data["TestCriticality"]["edges"]) == 2 - assert ["green", "grey"] == sorted( - [node["node"]["name"]["value"] for node in result.data["TestCriticality"]["edges"]] - ) + assert sorted([node["node"]["name"]["value"] for node in result.data["TestCriticality"]["edges"]]) == [ + "green", + "grey", + ] diff --git a/backend/tests/unit/graphql/test_graphql_query.py b/backend/tests/unit/graphql/test_graphql_query.py index 39204a8a74..2c3f05e4b8 100644 --- a/backend/tests/unit/graphql/test_graphql_query.py +++ b/backend/tests/unit/graphql/test_graphql_query.py @@ -11,10 +11,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): @@ -316,6 +316,59 @@ async def test_display_hfid(db: InfrahubDatabase, default_branch: Branch, animal } +async def test_display_hfid_related_node( + db: InfrahubDatabase, default_branch: Branch, animal_person_schema: SchemaBranch +): + person_schema = animal_person_schema.get(name="TestPerson") + dog_schema = animal_person_schema.get(name="TestDog") + + person1 = await Node.init(db=db, schema=person_schema, branch=default_branch) + await person1.new(db=db, name="Jack") + await person1.save(db=db) + + dog1 = await Node.init(db=db, schema=dog_schema, branch=default_branch) + await dog1.new(db=db, name="Rocky", breed="Labrador", owner=person1) + await dog1.save(db=db) + + query = """ + query { + TestPerson { + edges { + node { + hfid + animals { + edges { + node { + hfid + } + } + } + } + } + } + } + """ + gql_params = prepare_graphql_params( + db=db, include_mutation=False, include_subscription=False, branch=default_branch + ) + 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 len(result.data["TestPerson"]["edges"]) == 1 + assert result.data["TestPerson"]["edges"][0] == { + "node": { + "animals": {"edges": [{"node": {"hfid": ["Jack", "Rocky"]}}]}, + "hfid": ["Jack"], + }, + } + + async def test_display_label_generic(db: InfrahubDatabase, default_branch: Branch, animal_person_schema: SchemaBranch): person_schema = animal_person_schema.get(name="TestPerson") dog_schema = animal_person_schema.get(name="TestDog") diff --git a/backend/tests/unit/graphql/test_graphql_utils.py b/backend/tests/unit/graphql/test_graphql_utils.py index 11ad5f7b4d..d2eac517d6 100644 --- a/backend/tests/unit/graphql/test_graphql_utils.py +++ b/backend/tests/unit/graphql/test_graphql_utils.py @@ -1,11 +1,30 @@ -from graphql import parse +from graphql import GraphQLSchema, parse from infrahub_sdk.utils import extract_fields +from infrahub.core import registry 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.manager import GraphQLSchemaManager + + +def generate_graphql_schema( + db: InfrahubDatabase, # pylint: disable=unused-argument + branch: 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, + ) async def test_schema_models(db: InfrahubDatabase, default_branch: Branch, car_person_schema_generics, query_01: str): @@ -38,6 +57,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_manager.py b/backend/tests/unit/graphql/test_manager.py index 0657841991..6928414cad 100644 --- a/backend/tests/unit/graphql/test_manager.py +++ b/backend/tests/unit/graphql/test_manager.py @@ -1,9 +1,11 @@ import inspect import graphene +import pytest from infrahub.core import registry from infrahub.core.branch import Branch +from infrahub.core.timestamp import Timestamp from infrahub.database import InfrahubDatabase from infrahub.graphql.manager import GraphQLSchemaManager from infrahub.graphql.types import InfrahubObject @@ -258,3 +260,52 @@ async def test_generate_filters(db: InfrahubDatabase, default_branch: Branch, da "subscriber_of_groups__name__values", ] assert sorted(list(filters.keys())) == sorted(expected_filters) + + +@pytest.mark.parametrize( + "schema_changed_at_null,schema_hash_null", [(False, False), (True, False), (False, True), (True, True)] +) +async def test_branch_caching_hit( + db: InfrahubDatabase, + default_branch: Branch, + data_schema, + car_person_schema_generics, + schema_changed_at_null: bool, + schema_hash_null: bool, +): + default_branch.update_schema_hash() + same_branch = default_branch.model_copy() + if schema_changed_at_null: + same_branch.schema_changed_at = None + if schema_hash_null: + same_branch.schema_hash = None + schema_branch = registry.schema.get_schema_branch(default_branch.name) + + manager1 = GraphQLSchemaManager.get_manager_for_branch(branch=default_branch, schema_branch=schema_branch) + manager2 = GraphQLSchemaManager.get_manager_for_branch(branch=same_branch, schema_branch=schema_branch) + + assert manager1 is manager2 + + +@pytest.mark.parametrize("schema_changed_at_new,schema_hash_updated", [(True, False), (False, True), (True, True)]) +async def test_branch_caching_miss( + db: InfrahubDatabase, + default_branch: Branch, + data_schema, + car_person_schema_generics, + schema_changed_at_new: bool, + schema_hash_updated: bool, +): + default_branch.update_schema_hash() + same_branch = default_branch.model_copy() + schema_branch = registry.schema.get_schema_branch(default_branch.name) + if schema_changed_at_new: + same_branch.schema_changed_at = Timestamp().to_string() + if schema_hash_updated: + default_branch.schema_hash.main = "abc" + same_branch.update_schema_hash() + + manager1 = GraphQLSchemaManager.get_manager_for_branch(branch=default_branch, schema_branch=schema_branch) + manager2 = GraphQLSchemaManager.get_manager_for_branch(branch=same_branch, schema_branch=schema_branch) + + assert manager1 is not manager2 diff --git a/backend/tests/unit/graphql/test_mutation_artifact_definition.py b/backend/tests/unit/graphql/test_mutation_artifact_definition.py index 25b07d8779..661ee850a9 100644 --- a/backend/tests/unit/graphql/test_mutation_artifact_definition.py +++ b/backend/tests/unit/graphql/test_mutation_artifact_definition.py @@ -1,4 +1,5 @@ from typing import Dict +from unittest.mock import call, patch import pytest from graphql import graphql @@ -8,9 +9,11 @@ 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.message_bus import messages +from infrahub.git.models import RequestArtifactDefinitionGenerate +from infrahub.graphql.initialization import prepare_graphql_params from infrahub.services import InfrahubServices +from infrahub.services.adapters.workflow.local import WorkflowLocalExecution +from infrahub.workflows.catalogue import REQUEST_ARTIFACT_DEFINITION_GENERATE from tests.adapters.message_bus import BusRecorder @@ -92,29 +95,38 @@ async def test_create_artifact_definition( transformation1.id, ) recorder = BusRecorder() - service = InfrahubServices(message_bus=recorder) + service = InfrahubServices(message_bus=recorder, workflow=WorkflowLocalExecution()) gql_params = prepare_graphql_params(db=db, include_subscription=False, branch=branch, service=service) - 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["CoreArtifactDefinitionCreate"]["ok"] is True - ad_id = result.data["CoreArtifactDefinitionCreate"]["object"]["id"] + with patch( + "infrahub.services.adapters.workflow.local.WorkflowLocalExecution.submit_workflow" + ) as mock_submit_workflow: + result = await graphql( + schema=gql_params.schema, + source=query, + context_value=gql_params.context, + root_value=None, + variable_values={}, + ) - ad1 = await NodeManager.get_one(db=db, id=ad_id, include_owner=True, include_source=True, branch=branch) + assert result.errors is None + assert result.data["CoreArtifactDefinitionCreate"]["ok"] is True + ad_id = result.data["CoreArtifactDefinitionCreate"]["object"]["id"] - assert ad1.name.value == "Artifact 01" + ad1 = await NodeManager.get_one(db=db, id=ad_id, include_owner=True, include_source=True, branch=branch) - assert ( - messages.RequestArtifactDefinitionGenerate(artifact_definition=ad_id, branch=branch.name, limit=[]) - in service.message_bus.messages - ) + assert ad1.name.value == "Artifact 01" + + expected_calls = [ + call( + workflow=REQUEST_ARTIFACT_DEFINITION_GENERATE, + parameters={ + "model": RequestArtifactDefinitionGenerate(artifact_definition=ad1.id, branch=branch.name, limit=[]) + }, + ), + ] + mock_submit_workflow.assert_has_calls(expected_calls) async def test_update_artifact_definition( @@ -140,27 +152,37 @@ async def test_update_artifact_definition( """ % (definition1.id) recorder = BusRecorder() - service = InfrahubServices(message_bus=recorder) + service = InfrahubServices(message_bus=recorder, workflow=WorkflowLocalExecution()) gql_params = prepare_graphql_params(db=db, include_subscription=False, branch=branch, service=service) - 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["CoreArtifactDefinitionUpdate"]["ok"] is True - - ad1_post = await NodeManager.get_one( - db=db, id=definition1.id, include_owner=True, include_source=True, branch=branch - ) - - assert ad1_post.artifact_name.value == "myartifact2" - - assert ( - messages.RequestArtifactDefinitionGenerate(artifact_definition=definition1.id, branch=branch.name, limit=[]) - in service.message_bus.messages - ) + with patch( + "infrahub.services.adapters.workflow.local.WorkflowLocalExecution.submit_workflow" + ) as mock_submit_workflow: + 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["CoreArtifactDefinitionUpdate"]["ok"] is True + + ad1_post = await NodeManager.get_one( + db=db, id=definition1.id, include_owner=True, include_source=True, branch=branch + ) + + assert ad1_post.artifact_name.value == "myartifact2" + + expected_calls = [ + call( + workflow=REQUEST_ARTIFACT_DEFINITION_GENERATE, + parameters={ + "model": RequestArtifactDefinitionGenerate( + artifact_definition=definition1.id, branch=branch.name, limit=[] + ) + }, + ), + ] + mock_submit_workflow.assert_has_calls(expected_calls) diff --git a/backend/tests/unit/graphql/test_mutation_create.py b/backend/tests/unit/graphql/test_mutation_create.py index 1d1b1958d6..2d9860bee7 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): @@ -62,134 +62,6 @@ async def test_create_simple_object_with_ok_return(db: InfrahubDatabase, default assert result.data["TestPersonCreate"]["ok"] is True -@pytest.mark.parametrize( - "graphql_enums_on,enum_value,response_value", [(True, "MANUAL", "MANUAL"), (False, '"manual"', "manual")] -) -async def test_create_simple_object_with_enum( - db: InfrahubDatabase, - default_branch, - person_john_main, - car_person_schema, - graphql_enums_on, - enum_value, - response_value, -): - config.SETTINGS.experimental_features.graphql_enums = graphql_enums_on - query = """ - mutation { - TestCarCreate(data: { - name: { value: "JetTricycle"}, - nbr_seats: { value: 1 }, - is_electric: { value: false }, - transmission: { value: %s }, - owner: { id: "John" } - }) { - ok - object { - id - transmission { - value - } - } - } - } - """ % (enum_value) - gql_params = prepare_graphql_params(db=db, include_subscription=False, branch=default_branch) - 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["TestCarCreate"]["ok"] is True - assert result.data["TestCarCreate"]["object"]["transmission"]["value"] == response_value - - car_id = result.data["TestCarCreate"]["object"]["id"] - database_car = await NodeManager.get_one(db=db, id=car_id) - assert database_car.transmission.value.value == "manual" - - -async def test_create_enum_when_enums_off_fails( - db: InfrahubDatabase, - default_branch, - person_john_main, - car_person_schema, -): - config.SETTINGS.experimental_features.graphql_enums = False - query = """ - mutation { - TestCarCreate(data: { - name: { value: "JetTricycle"}, - nbr_seats: { value: 1 }, - is_electric: { value: false }, - transmission: { value: MANUAL }, - owner: { id: "John" } - }) { - ok - object { - id - transmission { - value - } - } - } - } - """ - gql_params = prepare_graphql_params(db=db, include_subscription=False, branch=default_branch) - result = await graphql( - schema=gql_params.schema, - source=query, - context_value=gql_params.context, - root_value=None, - variable_values={}, - ) - - assert len(result.errors) == 1 - assert "String cannot represent a non string value" in result.errors[0].message - - -async def test_create_string_when_enums_on_fails( - db: InfrahubDatabase, - default_branch, - person_john_main, - car_person_schema, -): - config.SETTINGS.experimental_features.graphql_enums = True - query = """ - mutation { - TestCarCreate(data: { - name: { value: "JetTricycle"}, - nbr_seats: { value: 1 }, - is_electric: { value: false }, - transmission: { value: "manual" }, - owner: { id: "John" } - }) { - ok - object { - id - transmission { - value - } - } - } - } - """ - gql_params = prepare_graphql_params(db=db, include_subscription=False, branch=default_branch) - result = await graphql( - schema=gql_params.schema, - source=query, - context_value=gql_params.context, - root_value=None, - variable_values={}, - ) - - assert len(result.errors) == 1 - assert "'TestCarTransmissionValue' cannot represent non-enum value" in result.errors[0].message - - async def test_create_with_id(db: InfrahubDatabase, default_branch, car_person_schema): uuid1 = "79c83773-6b23-4537-a3ce-b214b625ff1d" query = ( @@ -947,8 +819,10 @@ async def test_create_with_attribute_not_valid(db: InfrahubDatabase, default_bra async def test_create_with_uniqueness_constraint_violation(db: InfrahubDatabase, default_branch, car_person_schema): - car_schema = registry.schema.get("TestCar", branch=default_branch, duplicate=False) + schema_branch = registry.schema.get_schema_branch(name=default_branch.name) + car_schema = schema_branch.get("TestCar", duplicate=True) car_schema.uniqueness_constraints = [["owner", "color"]] + schema_branch.set(name="TestCar", schema=car_schema) p1 = await Node.init(db=db, schema="TestPerson") await p1.new(db=db, name="Bruce Wayne", height=180) @@ -1141,3 +1015,134 @@ async def test_create_valid_datetime_failure(db: InfrahubDatabase, default_branc ) assert result.errors[0].args[0] == "10:1010 is not a valid DateTime at time" assert result.data["TestCriticalityCreate"] is None + + +# These tests have been moved at the end of the file to avoid colliding with other and breaking them + + +@pytest.mark.parametrize( + "graphql_enums_on,enum_value,response_value", [(True, "MANUAL", "MANUAL"), (False, '"manual"', "manual")] +) +async def test_create_simple_object_with_enum( + db: InfrahubDatabase, + default_branch, + person_john_main, + car_person_schema, + graphql_enums_on, + enum_value, + response_value, +): + config.SETTINGS.experimental_features.graphql_enums = graphql_enums_on + query = """ + mutation { + TestCarCreate(data: { + name: { value: "JetTricycle"}, + nbr_seats: { value: 1 }, + is_electric: { value: false }, + transmission: { value: %s }, + owner: { id: "John" } + }) { + ok + object { + id + transmission { + value + } + } + } + } + """ % (enum_value) + gql_params = prepare_graphql_params(db=db, include_subscription=False, branch=default_branch) + 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["TestCarCreate"]["ok"] is True + assert result.data["TestCarCreate"]["object"]["transmission"]["value"] == response_value + + car_id = result.data["TestCarCreate"]["object"]["id"] + database_car = await NodeManager.get_one(db=db, id=car_id) + assert database_car.transmission.value.value == "manual" + + +async def test_create_enum_when_enums_off_fails( + db: InfrahubDatabase, + default_branch, + person_john_main, + car_person_schema, +): + config.SETTINGS.experimental_features.graphql_enums = False + query = """ + mutation { + TestCarCreate(data: { + name: { value: "JetTricycle"}, + nbr_seats: { value: 1 }, + is_electric: { value: false }, + transmission: { value: MANUAL }, + owner: { id: "John" } + }) { + ok + object { + id + transmission { + value + } + } + } + } + """ + gql_params = prepare_graphql_params(db=db, include_subscription=False, branch=default_branch) + result = await graphql( + schema=gql_params.schema, + source=query, + context_value=gql_params.context, + root_value=None, + variable_values={}, + ) + + assert len(result.errors) == 1 + assert "String cannot represent a non string value" in result.errors[0].message + + +async def test_create_string_when_enums_on_fails( + db: InfrahubDatabase, + default_branch, + person_john_main, + car_person_schema, +): + config.SETTINGS.experimental_features.graphql_enums = True + query = """ + mutation { + TestCarCreate(data: { + name: { value: "JetTricycle"}, + nbr_seats: { value: 1 }, + is_electric: { value: false }, + transmission: { value: "manual" }, + owner: { id: "John" } + }) { + ok + object { + id + transmission { + value + } + } + } + } + """ + gql_params = prepare_graphql_params(db=db, include_subscription=False, branch=default_branch) + result = await graphql( + schema=gql_params.schema, + source=query, + context_value=gql_params.context, + root_value=None, + variable_values={}, + ) + + assert len(result.errors) == 1 + assert "'TestCarTransmissionValue' cannot represent non-enum value" in result.errors[0].message 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..39f5321237 100644 --- a/backend/tests/unit/graphql/test_mutation_relationship.py +++ b/backend/tests/unit/graphql/test_mutation_relationship.py @@ -1,5 +1,5 @@ from graphql import graphql -from infrahub_sdk import UUIDT +from infrahub_sdk.uuidt import UUIDT from infrahub.core.branch import Branch from infrahub.core.constants import InfrahubKind @@ -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..5284621e05 100644 --- a/backend/tests/unit/graphql/test_mutation_update.py +++ b/backend/tests/unit/graphql/test_mutation_update.py @@ -7,7 +7,8 @@ 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.graphql.manager import GraphQLSchemaManager async def test_update_simple_object(db: InfrahubDatabase, person_john_main: Node, branch: Branch): @@ -85,6 +86,7 @@ async def test_update_simple_object_with_enum( enum_value, response_value, ): + GraphQLSchemaManager.clear_cache() config.SETTINGS.experimental_features.graphql_enums = graphql_enums_on query = """ mutation { 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/backend/tests/unit/menu/__init__.py b/backend/tests/unit/menu/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/backend/tests/unit/menu/test_generator.py b/backend/tests/unit/menu/test_generator.py new file mode 100644 index 0000000000..f9d8b597a3 --- /dev/null +++ b/backend/tests/unit/menu/test_generator.py @@ -0,0 +1,118 @@ +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 FULL_DEFAULT_MENU, MenuSection +from infrahub.menu.generator import generate_menu +from infrahub.menu.models import MenuItemDefinition +from infrahub.menu.utils import create_menu_children + + +def generate_menu_fixtures(prefix: str = "Menu", depth: int = 1, nbr_item: int = 10) -> list[MenuItemDefinition]: + max_depth = 3 + next_level_item: int = 3 + + menu: list[MenuItemDefinition] = [] + + for idx in range(nbr_item): + item = MenuItemDefinition( + namespace="Test", + name=f"{prefix}{idx}", + label=f"{prefix}{idx}", + section=MenuSection.OBJECT, + order_weight=(idx + 1) * 1000, + ) + + if depth <= max_depth: + item.children = generate_menu_fixtures(prefix=f"{prefix}{idx}", depth=depth + 1, nbr_item=next_level_item) + + menu.append(item) + + return menu + + +async def test_generate_menu_placement( + db: InfrahubDatabase, + default_branch: Branch, + car_person_schema_generics: SchemaRoot, + helper, +): + schema_branch = registry.schema.get_schema_branch(name=default_branch.name) + + schema_car = schema_branch.get(name="TestCar") + schema_car.menu_placement = "BuiltinObjectManagement" + schema_branch.set(name="TestCar", schema=schema_car) + + await create_default_menu(db=db) + + new_menu_items = generate_menu_fixtures(nbr_item=2) + + for item in new_menu_items: + 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) + + menu_items = await registry.manager.query(db=db, schema=CoreMenuItem, branch=default_branch) + menu = await generate_menu(db=db, branch=default_branch, menu_items=menu_items) + + assert menu + assert "TestMenu0" in menu.data.keys() + assert "BuiltinObjectManagement" in menu.data.keys() + assert "TestCar" in menu.data["BuiltinObjectManagement"].children.keys() + + +async def test_generate_menu_top_level( + db: InfrahubDatabase, + default_branch: Branch, + car_person_schema_generics: SchemaRoot, + helper, +): + await create_default_menu(db=db) + + new_menu_items = generate_menu_fixtures(nbr_item=2) + + for item in new_menu_items: + 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) + + menu_items = await registry.manager.query(db=db, schema=CoreMenuItem, branch=default_branch) + menu = await generate_menu(db=db, branch=default_branch, menu_items=menu_items) + + assert menu + assert "TestMenu0" in menu.data.keys() + assert "TestCar" in menu.data.keys() + assert "TestCarSub" in menu.data["TestCar"].children.keys() + + +async def test_generate_menu_default( + db: InfrahubDatabase, + default_branch: Branch, + car_person_schema_generics: SchemaRoot, + helper, +): + schema_branch = registry.schema.get_schema_branch(name=default_branch.name) + schema_car = schema_branch.get(name="TestCar") + schema_car.menu_placement = "DoesNotExist" + schema_branch.set(name="TestCar", schema=schema_car) + + await create_default_menu(db=db) + + new_menu_items = generate_menu_fixtures(nbr_item=2) + + for item in new_menu_items: + 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) + + menu_items = await registry.manager.query(db=db, schema=CoreMenuItem, branch=default_branch) + menu = await generate_menu(db=db, branch=default_branch, menu_items=menu_items) + + assert menu + assert "TestMenu0" in menu.data.keys() + assert "TestCar" in menu.data[FULL_DEFAULT_MENU].children.keys() diff --git a/backend/tests/unit/message_bus/operations/event/test_branch.py b/backend/tests/unit/message_bus/operations/event/test_branch.py index e29a13bd54..7644008caa 100644 --- a/backend/tests/unit/message_bus/operations/event/test_branch.py +++ b/backend/tests/unit/message_bus/operations/event/test_branch.py @@ -1,18 +1,35 @@ -from unittest.mock import AsyncMock, MagicMock, Mock, patch +from unittest.mock import AsyncMock, MagicMock, Mock, call, patch from uuid import uuid4 +import pytest + from infrahub.core.branch import Branch from infrahub.core.diff.model.path import BranchTrackingId, EnrichedDiffRoot +from infrahub.core.diff.models import RequestDiffRefresh, RequestDiffUpdate from infrahub.core.diff.repository.repository import DiffRepository from infrahub.core.timestamp import Timestamp from infrahub.dependencies.component.registry import ComponentDependencyRegistry from infrahub.message_bus import messages from infrahub.message_bus.operations.event.branch import delete, merge, rebased -from infrahub.services import InfrahubServices +from infrahub.services import InfrahubServices, services +from infrahub.services.adapters.workflow.local import WorkflowLocalExecution +from infrahub.workflows.catalogue import REQUEST_DIFF_REFRESH, REQUEST_DIFF_UPDATE, TRIGGER_ARTIFACT_DEFINITION_GENERATE from tests.adapters.message_bus import BusRecorder -async def test_delete(): +@pytest.fixture +def init_service(): + original = services.service + recorder = BusRecorder() + database = MagicMock() + workflow = WorkflowLocalExecution() + service = InfrahubServices(message_bus=recorder, database=database, workflow=workflow) + services.service = service + yield service + services.service = original + + +async def test_delete(prefect_test_fixture): """Validate that a deleted branch triggers a registry refresh and cancels open proposed changes""" message = messages.EventBranchDelete( @@ -31,17 +48,20 @@ async def test_delete(): assert trigger_cancel.branch == "cr1234" -async def test_merged(default_branch: Branch): +async def test_merged(default_branch: Branch, init_service: InfrahubServices, prefect_test_fixture): + """ + Test that merge flow triggers corrects events/workflows. It does not actually test these events/workflows behaviors + as they are mocked. + """ + source_branch_name = "cr1234" target_branch_name = "main" right_now = Timestamp() message = messages.EventBranchMerge( source_branch=source_branch_name, target_branch=target_branch_name, ipam_node_details=[] ) + service = init_service - recorder = BusRecorder() - database = MagicMock() - service = InfrahubServices(message_bus=recorder, database=database) tracked_diff_roots = [ EnrichedDiffRoot( base_branch_name=target_branch_name, @@ -71,21 +91,39 @@ async def test_merged(default_branch: Branch): mock_get_component_registry = MagicMock(return_value=mock_component_registry) mock_component_registry.get_component.return_value = diff_repo - with patch("infrahub.message_bus.operations.event.branch.get_component_registry", new=mock_get_component_registry): + with ( + patch("infrahub.message_bus.operations.event.branch.get_component_registry", new=mock_get_component_registry), + patch( + "infrahub.services.adapters.workflow.local.WorkflowLocalExecution.submit_workflow" + ) as mock_submit_workflow, + ): await merge(message=message, service=service) - mock_component_registry.get_component.assert_awaited_once_with(DiffRepository, db=database, branch=default_branch) + expected_calls = [ + call(workflow=TRIGGER_ARTIFACT_DEFINITION_GENERATE, parameters={"branch": message.target_branch}), + call( + workflow=REQUEST_DIFF_UPDATE, + parameters={"model": RequestDiffUpdate(branch_name=tracked_diff_roots[0].diff_branch_name)}, + ), + call( + workflow=REQUEST_DIFF_UPDATE, + parameters={"model": RequestDiffUpdate(branch_name=tracked_diff_roots[1].diff_branch_name)}, + ), + ] + mock_submit_workflow.assert_has_calls(expected_calls) + assert mock_submit_workflow.call_count == len(expected_calls) + + mock_component_registry.get_component.assert_awaited_once_with( + DiffRepository, db=service.database, branch=default_branch + ) diff_repo.get_empty_roots.assert_awaited_once_with(base_branch_names=[target_branch_name]) - assert len(recorder.messages) == 6 - assert recorder.messages[0] == messages.RefreshRegistryBranches() - assert recorder.messages[1] == messages.TriggerIpamReconciliation(branch=target_branch_name, ipam_node_details=[]) - assert recorder.messages[2] == messages.TriggerArtifactDefinitionGenerate(branch=target_branch_name) - assert recorder.messages[3] == messages.TriggerGeneratorDefinitionRun(branch=target_branch_name) - assert recorder.messages[4] == messages.RequestDiffUpdate(branch_name=tracked_diff_roots[0].diff_branch_name) - assert recorder.messages[5] == messages.RequestDiffUpdate(branch_name=tracked_diff_roots[1].diff_branch_name) + + assert len(service.message_bus.messages) == 2 + assert service.message_bus.messages[0] == messages.RefreshRegistryBranches() + assert service.message_bus.messages[1] == messages.TriggerGeneratorDefinitionRun(branch=target_branch_name) -async def test_rebased(default_branch: Branch): +async def test_rebased(default_branch: Branch, prefect_test_fixture): """Validate that a rebased branch triggers a registry refresh and cancels open proposed changes""" branch_name = "cr1234" right_now = Timestamp() @@ -93,7 +131,7 @@ async def test_rebased(default_branch: Branch): recorder = BusRecorder() database = MagicMock() - service = InfrahubServices(message_bus=recorder, database=database) + service = InfrahubServices(message_bus=recorder, database=database, workflow=WorkflowLocalExecution()) diff_roots = [ EnrichedDiffRoot( base_branch_name="main", @@ -111,14 +149,30 @@ async def test_rebased(default_branch: Branch): mock_get_component_registry = MagicMock(return_value=mock_component_registry) mock_component_registry.get_component.return_value = diff_repo - with patch("infrahub.message_bus.operations.event.branch.get_component_registry", new=mock_get_component_registry): + with ( + patch("infrahub.message_bus.operations.event.branch.get_component_registry", new=mock_get_component_registry), + patch( + "infrahub.services.adapters.workflow.local.WorkflowLocalExecution.submit_workflow" + ) as mock_submit_workflow, + ): await rebased(message=message, service=service) + expected_calls = [ + call( + workflow=REQUEST_DIFF_REFRESH, + parameters={"model": RequestDiffRefresh(branch_name=branch_name, diff_id=diff_roots[0].uuid)}, + ), + call( + workflow=REQUEST_DIFF_REFRESH, + parameters={"model": RequestDiffRefresh(branch_name=branch_name, diff_id=diff_roots[1].uuid)}, + ), + ] + mock_submit_workflow.assert_has_calls(expected_calls) + assert mock_submit_workflow.call_count == len(expected_calls) + mock_component_registry.get_component.assert_awaited_once_with(DiffRepository, db=database, branch=default_branch) diff_repo.get_empty_roots.assert_awaited_once_with(diff_branch_names=[branch_name]) - assert len(recorder.messages) == 3 + assert len(recorder.messages) == 1 assert isinstance(recorder.messages[0], messages.RefreshRegistryRebasedBranch) refresh_message: messages.RefreshRegistryRebasedBranch = recorder.messages[0] assert refresh_message.branch == "cr1234" - assert recorder.messages[1] == messages.RequestDiffRefresh(branch_name=branch_name, diff_id=diff_roots[0].uuid) - assert recorder.messages[2] == messages.RequestDiffRefresh(branch_name=branch_name, diff_id=diff_roots[1].uuid) diff --git a/backend/tests/unit/message_bus/operations/git/test_branch.py b/backend/tests/unit/message_bus/operations/git/test_branch.py deleted file mode 100644 index 9fa7e5974f..0000000000 --- a/backend/tests/unit/message_bus/operations/git/test_branch.py +++ /dev/null @@ -1,26 +0,0 @@ -from infrahub_sdk import InfrahubClient - -from infrahub.git import InfrahubRepository -from infrahub.message_bus import messages -from infrahub.services import InfrahubServices - - -async def test_branch_create(git_fixture_repo: InfrahubRepository, helper): - repo = git_fixture_repo.get_git_repo_main() - original_branches = [ref.name for ref in repo.refs if not ref.name.startswith("origin/")] - message = messages.GitBranchCreate( - repository_id=str(git_fixture_repo.id), - repository_name=git_fixture_repo.name, - branch="new-branch", - branch_id="69a92981-1694-4b1e-84ad-78ee4de0fe26", - ) - - bus_simulator = helper.get_message_bus_simulator() - service = InfrahubServices(client=InfrahubClient(), message_bus=bus_simulator) - bus_simulator.service = service - - await service.send(message=message) - - branches = [ref.name for ref in repo.refs if not ref.name.startswith("origin/")] - assert original_branches == ["main"] - assert branches == sorted(["main", "new-branch"]) 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 f21b5239d2..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", @@ -289,7 +289,7 @@ async def test_schema_integrity( # Ignore creation of Task Report response httpx_mock.add_response(method="POST", url="http://mock/graphql/main", json={"data": {}}) - await proposed_change.schema_integrity(message=schema_integrity_01, service=service_all) + 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/operations/transform/test_python.py b/backend/tests/unit/message_bus/operations/transform/test_python.py deleted file mode 100644 index 6f08a9701a..0000000000 --- a/backend/tests/unit/message_bus/operations/transform/test_python.py +++ /dev/null @@ -1,28 +0,0 @@ -from infrahub_sdk import InfrahubClient - -from infrahub.core.constants import InfrahubKind -from infrahub.git import InfrahubRepository -from infrahub.message_bus import messages -from infrahub.services import InfrahubServices - - -async def test_transform_python_success(git_fixture_repo: InfrahubRepository, helper): - commit = git_fixture_repo.get_commit_value(branch_name="main") - - message = messages.TransformPythonData( - repository_id=str(git_fixture_repo.id), - repository_name=git_fixture_repo.name, - repository_kind=InfrahubKind.REPOSITORY, - commit=commit, - branch="main", - transform_location="unit/transforms/multiplier.py::Multiplier", - data={"multiplier": 2, "key": "abc", "answer": 21}, - ) - - bus_simulator = helper.get_message_bus_simulator() - service = InfrahubServices(message_bus=bus_simulator, client=InfrahubClient()) - bus_simulator.service = service - - reply = await service.message_bus.rpc(message=message, response_class=messages.TransformPythonDataResponse) - assert reply.passed - assert reply.data.transformed_data == {"key": "abcabc", "answer": 42} 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/backend/tests/unit/permissions/__init__.py b/backend/tests/unit/permissions/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/backend/tests/unit/permissions/test_backends.py b/backend/tests/unit/permissions/test_backends.py new file mode 100644 index 0000000000..e152d86b21 --- /dev/null +++ b/backend/tests/unit/permissions/test_backends.py @@ -0,0 +1,269 @@ +from infrahub.auth import AccountSession +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.database import InfrahubDatabase +from infrahub.permissions import LocalPermissionBackend +from infrahub.permissions.constants import PermissionDecisionFlag + + +async def test_load_permissions( + db: InfrahubDatabase, default_branch: Branch, session_admin: AccountSession, session_first_account: AccountSession +): + backend = LocalPermissionBackend() + + permissions = await backend.load_permissions(db=db, account_session=session_admin, 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( + namespace="*", name="*", action=PermissionAction.ANY.value, decision=PermissionDecision.ALLOW_ALL.value + ) + ) + + permissions = await backend.load_permissions(db=db, account_session=session_first_account, 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, + session_admin: AccountSession, + session_first_account: AccountSession, + session_second_account: AccountSession, +): + backend = LocalPermissionBackend() + + allow_default_branch_edition = GlobalPermission( + action=GlobalPermissions.EDIT_DEFAULT_BRANCH.value, decision=PermissionDecision.ALLOW_ALL.value + ) + + role1_permissions = [] + obj = await Node.init(db=db, schema=InfrahubKind.GLOBALPERMISSION) + await obj.new(db=db, 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": session_first_account.account_id}) + await group1.members.save(db=db) + + role2_permissions = [] + for p in [ + allow_default_branch_edition, + GlobalPermission(action=GlobalPermissions.EDIT_DEFAULT_BRANCH.value, decision=PermissionDecision.DENY.value), + ]: + obj = await Node.init(db=db, schema=InfrahubKind.GLOBALPERMISSION) + await obj.new(db=db, 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": session_second_account.account_id}) + await group2.members.save(db=db) + + assert await backend.has_permission( + db=db, account_session=session_first_account, permission=allow_default_branch_edition, branch=default_branch + ) + assert not await backend.has_permission( + db=db, account_session=session_second_account, permission=allow_default_branch_edition, branch=default_branch + ) + + +async def test_has_permission_object( + db: InfrahubDatabase, + default_branch: Branch, + register_core_models_schema: None, + session_admin: AccountSession, + session_first_account: AccountSession, + session_second_account: AccountSession, +): + backend = LocalPermissionBackend() + + role1_permissions = [] + for p in [ + ObjectPermission( + namespace="*", name="*", action=PermissionAction.ANY.value, decision=PermissionDecision.ALLOW_ALL.value + ), + ObjectPermission( + 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, 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": session_first_account.account_id}) + await group1.members.save(db=db) + + role2_permissions = [] + for p in [ + ObjectPermission( + namespace="*", name="*", action=PermissionAction.ANY.value, decision=PermissionDecision.DENY.value + ), + ObjectPermission( + namespace="Builtin", + name="Tag", + action=PermissionAction.ANY.value, + decision=PermissionDecision.ALLOW_ALL.value, + ), + ]: + obj = await Node.init(db=db, schema=InfrahubKind.OBJECTPERMISSION) + await obj.new(db=db, 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": session_second_account.account_id}) + await group2.members.save(db=db) + + permission = ObjectPermission( + namespace="Builtin", + name="Tag", + action=PermissionAction.CREATE.value, + decision=PermissionDecision.ALLOW_ALL.value, + ) + assert not await backend.has_permission( + db=db, account_session=session_first_account, permission=permission, branch=default_branch + ) + assert await backend.has_permission( + db=db, account_session=session_second_account, permission=permission, branch=default_branch + ) + + +async def test_report_permission_object( + db: InfrahubDatabase, + default_branch: Branch, + register_core_models_schema: None, + session_admin: AccountSession, + session_first_account: AccountSession, + session_second_account: AccountSession, +): + backend = LocalPermissionBackend() + + role1_permissions = [] + for p in [ + ObjectPermission( + namespace="*", name="*", action=PermissionAction.ANY.value, decision=PermissionDecision.ALLOW_ALL.value + ), + ObjectPermission( + 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, 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": session_first_account.account_id}) + await group1.members.save(db=db) + + role2_permissions = [] + for p in [ + ObjectPermission( + namespace="*", name="*", action=PermissionAction.ANY.value, decision=PermissionDecision.DENY.value + ), + ObjectPermission( + namespace="Builtin", + name="Tag", + action=PermissionAction.ANY.value, + decision=PermissionDecision.ALLOW_ALL.value, + ), + ]: + obj = await Node.init(db=db, schema=InfrahubKind.OBJECTPERMISSION) + await obj.new(db=db, 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": session_second_account.account_id}) + await group2.members.save(db=db) + + first_permissions = await backend.load_permissions( + db=db, account_session=session_first_account, branch=default_branch + ) + + assert ( + backend.report_object_permission( + permissions=first_permissions["object_permissions"], namespace="Builtin", name="Tag", action="create" + ) + == PermissionDecisionFlag.DENY + ) + assert ( + backend.report_object_permission( + permissions=first_permissions["object_permissions"], namespace="Core", name="Account", action="view" + ) + == PermissionDecisionFlag.ALLOW_ALL + ) + + second_permissions = await backend.load_permissions( + db=db, account_session=session_second_account, branch=default_branch + ) + + assert ( + backend.report_object_permission( + permissions=second_permissions["object_permissions"], namespace="Builtin", name="Tag", action="create" + ) + == PermissionDecisionFlag.ALLOW_ALL + ) + assert ( + backend.report_object_permission( + permissions=second_permissions["object_permissions"], namespace="Core", name="Account", action="view" + ) + == PermissionDecisionFlag.DENY + ) 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/backend/tests/unit/storage/test_local_storage.py b/backend/tests/unit/storage/test_local_storage.py index 4a860392cc..f0468136e8 100644 --- a/backend/tests/unit/storage/test_local_storage.py +++ b/backend/tests/unit/storage/test_local_storage.py @@ -3,7 +3,7 @@ import fastapi_storages import pytest -from infrahub_sdk import UUIDT +from infrahub_sdk.uuidt import UUIDT from infrahub import config from infrahub.exceptions import NodeNotFoundError diff --git a/backend/tests/unit/workflows/__init__.py b/backend/tests/unit/workflows/__init__.py new file mode 100644 index 0000000000..e69de29bb2 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/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 451f2ae413..2d15a03890 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 @@ -109,7 +109,7 @@ services: INFRAHUB_SECURITY_SECRET_KEY: 327f747f-efac-42be-9e73-999f08f86b92 INFRAHUB_BROKER_ADDRESS: message-queue INFRAHUB_CACHE_ADDRESS: "${INFRAHUB_CACHE_ADDRESS:-cache}" - INFRAHUB_INTERNAL_ADDRESS: "http://infrahub-server:8000" + INFRAHUB_INTERNAL_ADDRESS: "http://server:8000" INFRAHUB_GIT_REPOSITORIES_DIRECTORY: "/opt/infrahub/git" INFRAHUB_DB_ADDRESS: database INFRAHUB_DB_USERNAME: neo4j diff --git a/development/docker-compose-test.yml b/development/docker-compose-test.yml index d6252a1120..ce26c5587b 100644 --- a/development/docker-compose-test.yml +++ b/development/docker-compose-test.yml @@ -98,7 +98,7 @@ services: INFRAHUB_TEST_IN_DOCKER: 1 INFRAHUB_BROKER_ADDRESS: message-queue INFRAHUB_CACHE_ADDRESS: "${INFRAHUB_CACHE_ADDRESS:-cache}" - INFRAHUB_INTERNAL_ADDRESS: "http://infrahub-server:8000" + INFRAHUB_INTERNAL_ADDRESS: "http://server:8000" INFRAHUB_GIT_REPOSITORIES_DIRECTORY: "/opt/infrahub/git" INFRAHUB_DB_ADDRESS: database INFRAHUB_DB_USERNAME: neo4j 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 026d672808..624f8d4f23 100644 --- a/development/docker-compose.yml +++ b/development/docker-compose.yml @@ -87,15 +87,19 @@ x-infrahub-config: &infrahub_config INFRAHUB_TELEMETRY_ENDPOINT: INFRAHUB_TELEMETRY_INTERVAL: INFRAHUB_TELEMETRY_OPTOUT: + INFRAHUB_TIMEOUT: INFRAHUB_TRACE_ENABLE: INFRAHUB_TRACE_EXPORTER_ENDPOINT: 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,15 +115,21 @@ services: condition: service_healthy cache: condition: service_healthy + task-manager: + condition: service_healthy + required: false environment: <<: *infrahub_config - INFRAHUB_INTERNAL_ADDRESS: "http://infrahub-server:8000" + INFRAHUB_INTERNAL_ADDRESS: "http://server:8000" INFRAHUB_GIT_REPOSITORIES_DIRECTORY: "/opt/infrahub/git" INFRAHUB_PRODUCTION: false INFRAHUB_INITIAL_ADMIN_TOKEN: 06438eb2-8019-4776-878c-0941b1f1d1ec INFRAHUB_SECURITY_SECRET_KEY: 327f747f-efac-42be-9e73-999f08f86b92 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://${INFRAHUB_WORKFLOW_ADDRESS:-task-manager}:${INFRAHUB_WORKFLOW_PORT:-4200}/api INFRAHUB_DB_ADDRESS: database INFRAHUB_DB_USERNAME: neo4j INFRAHUB_DB_PASSWORD: admin @@ -139,6 +149,7 @@ services: retries: 20 start_period: 10s infrahub-git: + profiles: [demo] deploy: mode: replicated replicas: 2 @@ -151,15 +162,16 @@ services: command: infrahub git-agent start --debug restart: unless-stopped depends_on: - - infrahub-server + - server environment: <<: *infrahub_config - INFRAHUB_ADDRESS: http://infrahub-server:8000 - INFRAHUB_INTERNAL_ADDRESS: "http://infrahub-server:8000" + INFRAHUB_ADDRESS: http://server:8000 + INFRAHUB_INTERNAL_ADDRESS: "http://server:8000" + INFRAHUB_GIT_REPOSITORIES_DIRECTORY: "/opt/infrahub/git" INFRAHUB_PRODUCTION: false INFRAHUB_LOG_LEVEL: DEBUG INFRAHUB_API_TOKEN: 06438eb2-8019-4776-878c-0941b1f1d1ec - INFRAHUB_TIMEOUT: 20 + INFRAHUB_TIMEOUT: "${INFRAHUB_TIMEOUT:-60}" INFRAHUB_BROKER_ADDRESS: message-queue INFRAHUB_CACHE_ADDRESS: "${INFRAHUB_CACHE_ADDRESS:-cache}" INFRAHUB_DB_ADDRESS: database @@ -175,6 +187,47 @@ services: labels: 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_ADDRESS: http://server:8000 + INFRAHUB_INTERNAL_ADDRESS: "http://server:8000" + INFRAHUB_GIT_REPOSITORIES_DIRECTORY: "/opt/infrahub/git" + INFRAHUB_DB_ADDRESS: "database" + INFRAHUB_PRODUCTION: false + INFRAHUB_API_TOKEN: 06438eb2-8019-4776-878c-0941b1f1d1ec + INFRAHUB_TIMEOUT: "${INFRAHUB_TIMEOUT:-60}" + 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://${INFRAHUB_WORKFLOW_ADDRESS:-task-manager}:${INFRAHUB_WORKFLOW_PORT:-4200}/api + INFRAHUB_DB_USERNAME: neo4j + INFRAHUB_DB_PASSWORD: admin + INFRAHUB_DB_PORT: 7687 + INFRAHUB_DB_PROTOCOL: bolt + INFRAHUB_STORAGE_DRIVER: local + 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: 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 a3589a6058..7c92f8fbf4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -30,6 +30,7 @@ x-infrahub-config: &infrahub_config INFRAHUB_BROKER_NAMESPACE: ${INFRAHUB_BROKER_NAMESPACE:-infrahub} INFRAHUB_BROKER_PASSWORD: &broker_password ${INFRAHUB_BROKER_PASSWORD:-infrahub} INFRAHUB_BROKER_PORT: + INFRAHUB_BROKER_RABBITMQ_HTTP_PORT: INFRAHUB_BROKER_TLS_CA_FILE: INFRAHUB_BROKER_TLS_ENABLED: ${INFRAHUB_BROKER_TLS_ENABLED:-false} INFRAHUB_BROKER_TLS_INSECURE: ${INFRAHUB_BROKER_TLS_INSECURE:-false} @@ -39,12 +40,12 @@ x-infrahub-config: &infrahub_config INFRAHUB_CACHE_DATABASE: ${INFRAHUB_CACHE_DATABASE:-0} INFRAHUB_CACHE_DRIVER: ${INFRAHUB_CACHE_DRIVER:-redis} INFRAHUB_CACHE_ENABLE: ${INFRAHUB_CACHE_ENABLE:-true} - INFRAHUB_CACHE_PASSWORD: ${INFRAHUB_CACHE_PASSWORD:-infrahub} + INFRAHUB_CACHE_PASSWORD: &cache_password ${INFRAHUB_CACHE_PASSWORD:-} INFRAHUB_CACHE_PORT: INFRAHUB_CACHE_TLS_CA_FILE: INFRAHUB_CACHE_TLS_ENABLED: ${INFRAHUB_CACHE_TLS_ENABLED:-false} INFRAHUB_CACHE_TLS_INSECURE: ${INFRAHUB_CACHE_TLS_INSECURE:-false} - INFRAHUB_CACHE_USERNAME: ${INFRAHUB_CACHE_USERNAME:-infrahub} + INFRAHUB_CACHE_USERNAME: &cache_username ${INFRAHUB_CACHE_USERNAME:-} INFRAHUB_CONFIG: INFRAHUB_DB_ADDRESS: ${INFRAHUB_DB_ADDRESS:-localhost} INFRAHUB_DB_DATABASE: @@ -64,6 +65,9 @@ x-infrahub-config: &infrahub_config INFRAHUB_EXPERIMENTAL_PULL_REQUEST: ${INFRAHUB_EXPERIMENTAL_PULL_REQUEST:-false} INFRAHUB_GIT_REPOSITORIES_DIRECTORY: ${INFRAHUB_GIT_REPOSITORIES_DIRECTORY:-repositories} INFRAHUB_GIT_SYNC_INTERVAL: ${INFRAHUB_GIT_SYNC_INTERVAL:-10} + INFRAHUB_HTTP_TIMEOUT: ${INFRAHUB_HTTP_TIMEOUT:-10} + INFRAHUB_HTTP_TLS_CA_BUNDLE: + INFRAHUB_HTTP_TLS_INSECURE: ${INFRAHUB_HTTP_TLS_INSECURE:-false} INFRAHUB_INITIAL_ADMIN_PASSWORD: ${INFRAHUB_INITIAL_ADMIN_PASSWORD:-infrahub} INFRAHUB_INITIAL_ADMIN_TOKEN: INFRAHUB_INITIAL_AGENT_PASSWORD: @@ -79,8 +83,11 @@ x-infrahub-config: &infrahub_config INFRAHUB_MISC_PRINT_QUERY_DETAILS: ${INFRAHUB_MISC_PRINT_QUERY_DETAILS:-false} INFRAHUB_MISC_RESPONSE_DELAY: ${INFRAHUB_MISC_RESPONSE_DELAY:-0} INFRAHUB_MISC_START_BACKGROUND_RUNNER: ${INFRAHUB_MISC_START_BACKGROUND_RUNNER:-true} + INFRAHUB_PERMISSION_BACKENDS: ${INFRAHUB_PERMISSION_BACKENDS:-["infrahub.permissions.LocalPermissionBackend"]} INFRAHUB_PRODUCTION: INFRAHUB_SECURITY_ACCESS_TOKEN_LIFETIME: ${INFRAHUB_SECURITY_ACCESS_TOKEN_LIFETIME:-3600} + INFRAHUB_SECURITY_OAUTH2_PROVIDERS: + INFRAHUB_SECURITY_OIDC_PROVIDERS: INFRAHUB_SECURITY_REFRESH_TOKEN_LIFETIME: ${INFRAHUB_SECURITY_REFRESH_TOKEN_LIFETIME:-2592000} INFRAHUB_SECURITY_SECRET_KEY: INFRAHUB_STORAGE_BUCKET_NAME: @@ -94,13 +101,21 @@ x-infrahub-config: &infrahub_config INFRAHUB_TELEMETRY_ENDPOINT: ${INFRAHUB_TELEMETRY_ENDPOINT:-https://telemetry.opsmill.cloud/infrahub} INFRAHUB_TELEMETRY_INTERVAL: ${INFRAHUB_TELEMETRY_INTERVAL:-86400} INFRAHUB_TELEMETRY_OPTOUT: ${INFRAHUB_TELEMETRY_OPTOUT:-false} + INFRAHUB_TIMEOUT: INFRAHUB_TRACE_ENABLE: ${INFRAHUB_TRACE_ENABLE:-false} INFRAHUB_TRACE_EXPORTER_ENDPOINT: INFRAHUB_TRACE_EXPORTER_PROTOCOL: ${INFRAHUB_TRACE_EXPORTER_PROTOCOL:-grpc} INFRAHUB_TRACE_EXPORTER_TYPE: ${INFRAHUB_TRACE_EXPORTER_TYPE:-console} INFRAHUB_TRACE_INSECURE: ${INFRAHUB_TRACE_INSECURE:-true} + INFRAHUB_WORKFLOW_ADDRESS: ${INFRAHUB_WORKFLOW_ADDRESS:-localhost} + INFRAHUB_WORKFLOW_DRIVER: ${INFRAHUB_WORKFLOW_DRIVER:-worker} + INFRAHUB_WORKFLOW_ENABLE: ${INFRAHUB_WORKFLOW_ENABLE:-true} + INFRAHUB_WORKFLOW_PORT: + INFRAHUB_WORKFLOW_TLS_ENABLED: ${INFRAHUB_WORKFLOW_TLS_ENABLED:-false} + INFRAHUB_WORKFLOW_WORKER_POLLING_INTERVAL: ${INFRAHUB_WORKFLOW_WORKER_POLLING_INTERVAL:-2} OTEL_RESOURCE_ATTRIBUTES: + services: message-queue: image: ${MESSAGE_QUEUE_DOCKER_IMAGE:-rabbitmq:3.13.7-management} @@ -112,7 +127,8 @@ services: test: rabbitmq-diagnostics -q check_port_connectivity interval: 5s timeout: 30s - retries: 3 + retries: 10 + start_period: 3s ports: - 15692:15692 @@ -126,10 +142,10 @@ 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} + NEO4J_AUTH: ${INFRAHUB_DB_USERNAME:-neo4j}/${INFRAHUB_DB_PASSWORD:-admin} NEO4J_dbms_security_procedures_unrestricted: "apoc.*" NEO4J_dbms_security_auth__minimum__password__length: 4 volumes: @@ -145,11 +161,42 @@ 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 + depends_on: + - task-manager-db + environment: + PREFECT_API_DATABASE_CONNECTION_URL: postgresql+asyncpg://postgres:postgres@task-manager-db:5432/prefect + healthcheck: + test: /usr/local/bin/httpx http://localhost:4200/api/health || exit 1 + interval: 5s + timeout: 5s + retries: 20 + start_period: 10s + + 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: "${INFRAHUB_DOCKER_IMAGE:-registry.opsmill.io/opsmill/infrahub}:${VERSION:-0.16.2}" restart: unless-stopped command: > gunicorn --config backend/infrahub/serve/gunicorn_config.py + -w ${WEB_CONCURRENCY:-4} --logger-class infrahub.serve.log.GunicornLogger infrahub.server:app depends_on: @@ -159,6 +206,8 @@ services: condition: service_healthy cache: condition: service_healthy + task-manager: + condition: service_healthy environment: <<: *infrahub_config INFRAHUB_PRODUCTION: ${INFRAHUB_PRODUCTION:-false} @@ -166,13 +215,17 @@ services: INFRAHUB_BROKER_ADDRESS: ${INFRAHUB_BROKER_ADDRESS:-message-queue} INFRAHUB_CACHE_ADDRESS: ${INFRAHUB_CACHE_ADDRESS:-cache} INFRAHUB_DB_ADDRESS: ${INFRAHUB_DB_ADDRESS:-database} + INFRAHUB_WORKFLOW_ADDRESS: ${INFRAHUB_WORKFLOW_ADDRESS:-task-manager} INFRAHUB_INITIAL_ADMIN_TOKEN: ${INFRAHUB_INITIAL_ADMIN_TOKEN:-06438eb2-8019-4776-878c-0941b1f1d1ec} INFRAHUB_INITIAL_AGENT_TOKEN: ${INFRAHUB_INITIAL_AGENT_TOKEN:-44af444d-3b26-410d-9546-b758657e026c} INFRAHUB_SECURITY_SECRET_KEY: ${INFRAHUB_SECURITY_SECRET_KEY:-327f747f-efac-42be-9e73-999f08f86b92"} + INFRAHUB_WORKFLOW_PORT: ${INFRAHUB_WORKFLOW_PORT:-4200} + PREFECT_API_URL: http://${INFRAHUB_WORKFLOW_ADDRESS:-task-manager}:${INFRAHUB_WORKFLOW_PORT:-4200}/api ports: - 8000:8000 volumes: - "storage_data:${INFRAHUB_STORAGE_LOCAL_PATH:-/opt/infrahub/storage}" + - "workflow_data:/opt/infrahub/workflow" tty: true healthcheck: test: curl -s -f -o /dev/null http://localhost:8000/api/schema/summary || exit 1 @@ -181,12 +234,12 @@ services: retries: 20 start_period: 10s - infrahub-git: + task-worker: deploy: mode: replicated replicas: 2 image: "${INFRAHUB_DOCKER_IMAGE:-registry.opsmill.io/opsmill/infrahub}:${VERSION:-0.16.2}" - command: infrahub git-agent start --debug + command: prefect worker start --type infrahubasync --pool infrahub-worker --with-healthcheck restart: unless-stopped depends_on: - infrahub-server @@ -201,7 +254,10 @@ services: INFRAHUB_BROKER_ADDRESS: ${INFRAHUB_BROKER_ADDRESS:-message-queue} INFRAHUB_CACHE_ADDRESS: ${INFRAHUB_CACHE_ADDRESS:-cache} INFRAHUB_DB_ADDRESS: ${INFRAHUB_DB_ADDRESS:-database} - INFRAHUB_TIMEOUT: ${INFRAHUB_TIMEOUT:-20} + INFRAHUB_WORKFLOW_ADDRESS: ${INFRAHUB_WORKFLOW_ADDRESS:-task-manager} + INFRAHUB_TIMEOUT: ${INFRAHUB_TIMEOUT:-60} + INFRAHUB_WORKFLOW_PORT: ${INFRAHUB_WORKFLOW_PORT:-4200} + PREFECT_API_URL: http://${INFRAHUB_WORKFLOW_ADDRESS:-task-manager}:${INFRAHUB_WORKFLOW_PORT:-4200}/api volumes: - "git_data:/opt/infrahub/git" - "git_remote_data:/remote" @@ -213,3 +269,5 @@ volumes: git_data: git_remote_data: storage_data: + workflow_db: + workflow_data: 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/faq/faq.mdx b/docs/docs/faq/faq.mdx index 1d10f16016..2944ea6eab 100644 --- a/docs/docs/faq/faq.mdx +++ b/docs/docs/faq/faq.mdx @@ -77,14 +77,7 @@ Infrahub allows you to load custom schemas to define your infrastructure data mo ### What is the status of the project? -Infrahub is currently in beta, and the team is actively working towards reaching version 1.0 by the end of the year. The project is committed to ensuring data safety and providing a migration path for future releases. - -Upcoming features and improvements include: - -- Scale and Performance: Enhancements to handle larger volumes of data and improve overall performance. -- Proposed Changes & CI Pipeline -- Resource Manager: A dedicated resource management module for better visibility and control over infrastructure resources. -- Profiles: Support for defining and managing different profiles or environments within Infrahub. +Infrahub is production-ready and has been deployed in a number of organizations as a central component of their automation workflows. ### How much data can Infrahub handle right now? @@ -92,7 +85,7 @@ The current data handling capabilities of Infrahub are still being actively deve ### Can I deploy Infrahub in production? -Yes, Infrahub can be deployed in production but keep in mind we are still in beta so please ensure to have the right backup and safeguard in place. +Yes, Infrahub can be deployed in production. If you are planning to deploy Infrahub in a critical environment we recommend reaching out to our customer success team via [Discord](https://discord.gg/opsmill) or contact@opsmill.com diff --git a/docs/docs/guides/accounts-permissions.mdx b/docs/docs/guides/accounts-permissions.mdx new file mode 100644 index 0000000000..d9efd886e9 --- /dev/null +++ b/docs/docs/guides/accounts-permissions.mdx @@ -0,0 +1,219 @@ +--- +title: Creating Users, User Groups, Roles, and Permissions +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Creating accounts, groups, roles, and permissions + +In Infrahub, managing access and control starts with creating accounts, assigning them to groups, and managing their roles and permissions. +This guide outlines how to create new accounts, accounts groups, and assign roles and permissions. + +For more information on roles and permissions, see the [Roles and Permissions](/topics/permissions-roles) topic. + +## Creating a new account + + + + +1. Login to Infrahub's web interface as an administrator. +2. Click on **Admin** in the left side menu. +3. Navigate to the **Role Management** section. +4. In the **Accounts** tab, click on **Create Account**. +5. Fill in the account's details (name, email, and password). +6. Optionally, assign the account to a group. +7. Click **Create** to create the account. + + + + + +In the GraphQL sandbox, execute the following mutation to create a new account, replacing the appropriate values as needed: + ```graphql + mutation AddAccount { + CoreAccountCreate( + data: { + name: {value: ""}, + password: {value: ""} + # Optional - Assign the account to an existing group + member_of_groups: [{hfid: "Infrahub Users"}] + } + ) { + ok + object { + hfid + } + } + } + ``` + + + + +## Creating a new account group + + + + +1. Login to Infrahub's web interface as an administrator. +2. Click on **Admin** in the left side menu. +3. Navigate to the **Role Management** section. +4. In the **Groups** tab, click on **Create Account Group**. +5. Enter a name for the group. +6. Optionally, assign roles to the group. +7. Click **Create** to create the group. + + + + + +In the GraphQL sandbox, execute the following mutation to create a new group: + ```graphql + mutation AddGroup { + CoreAccountGroupCreate( + data: { + name: {value: ""}, + # Optional - Assign existing roles + roles: [{hfid: "General Access"}] + } + ) { + ok + object { + hfid + } + } + } + ``` + + + + +## Creating and assigning roles + + + + +1. Login to Infrahub's web interface as an administrator. +2. Click on **Admin** in the left side menu. +3. Navigate to the **Role Management** section. +4. In the **Roles** tab, click on **Create Account Role**. +5. Provide a name for the role. +6. Select the permissions you wish to assign to the role. +7. Optionally, assign the role to an existing group. +8. Click **Create** to create the role. + + + + + +In the GraphQL sandbox, execute the following mutation to create a new role: + ```graphql + mutation AddRole { + CoreAccountRoleCreate( + data: { + name: {value: ""}, + # Optional - Assign the role to an existing group + groups: [{hfid: "Infrahub Users"}] + } + ) { + ok + object { + hfid + } + } + } + ``` + + + + +## Managing permissions + +Permissions can be managed through roles assigned to users or groups. +Infrahub supports **Global** and **Object-specific** permissions, allowing fine-grained control over what users can do within the system. +For a complete list of available global and object permissions, see the [Roles and Permissions documentation](/reference/permissions.mdx). + +### Creating and global permissions + + + + +1. Login to Infrahub's web interface as an administrator. +2. Click on **Admin** in the left side menu. +3. Navigate to the **Role Management** section. +4. In the **Global Permissions** tab, click on **Create Global Permission**. +5. Select the action you which to use. +6. Select the decision for this action. +7. Optionally, assign the permission to an existing role. +8. Click **Create** to create the permission. + + + + + +In the GraphQL sandbox, execute the following mutation to create a new global permission: + ```graphql + mutation AddGlobalPermissions { + CoreGlobalPermissionCreate( + data: { + action: {value: "manage_accounts"}, + # 6 is the enum value for "allow" + decision: {value: 6} + } + ) { + ok + object { + identifier { + value + } + } + } + } + ``` + + + + +### Creating and objects permissions + + + + +1. Login to Infrahub's web interface as an administrator. +2. Click on **Admin** in the left side menu. +3. Navigate to the **Role Management** section. +4. In the **Objects Permissions** tab, click on **Create Object Permission**.. +5. Provide the namespace and name of the object(s) you want to interact with. +6. Select the action and decision you wish to use for this permission. +7. Optionally, assign the permission to an existing role. +8. Click **Create** to create the permission. + + + + + +In the GraphQL sandbox, execute the following mutation to create a new global permission: + ```graphql + mutation AddObjectPermissions { + CoreObjectPermissionCreate( + data: { + namespace: {value: "Builtin"}, + name: {value: "Tag"}, + action: {value: "view"}, + # 4 is the enum value for "allow_other" + decision: {value: 4 } + } + ) { + ok + object { + identifier { + value + } + } + } + } + ``` + + + diff --git a/docs/docs/guides/artifact.mdx b/docs/docs/guides/artifact.mdx index 719c5b1a27..baa6a59aec 100644 --- a/docs/docs/guides/artifact.mdx +++ b/docs/docs/guides/artifact.mdx @@ -51,7 +51,7 @@ git commit -m "add tags_config_file artifact definition" git push origin main ``` -The artifact definition will be created in the database, when the Git agent(s) notice the change in the Git repository. The `tags_config_file` should now be visible in the Artifact Definition view in the web interface. +The artifact definition will be created in the database, when the Task worker(s) notice the change in the Git repository. The `tags_config_file` should now be visible in the Artifact Definition view in the web interface. ![Artifact Definition](../media/guides/artifact/artifact_definition.png) ## Accessing the artifacts diff --git a/docs/docs/guides/check.mdx b/docs/docs/guides/check.mdx index 08f4dbd2c4..b71d1c56ea 100644 --- a/docs/docs/guides/check.mdx +++ b/docs/docs/guides/check.mdx @@ -293,7 +293,7 @@ We added 2 definitions to the `check_color_tags_name`: ### 2. Creating a `ColorTags` group -Targeted checks use nodes in a group a their targets. Create the `ColorTags` group with this GraphQL query. Take node of the id of the group, we will need it in the next steps. +Targeted checks use nodes in a group a their targets. Create the `ColorTags` group with this GraphQL query. Take node of the hfid of the group, we will use it in the next steps. ```graphql mutation { @@ -301,6 +301,7 @@ mutation { ok object { id + hfid } } } @@ -333,7 +334,7 @@ We can then use the id's of the tags to add them to the `ColorTags` group. mutation { CoreStandardGroupUpdate( data: { - id: "", + hfid: ["ColorTags"], members: [ {id: ""}, {id: ""}, @@ -415,12 +416,13 @@ mutation { ok object { id + hfid } } } ``` -Take note of the id as we will need it in the next step. +Take note of the hfid as we will use it in the next step. ### 8. Add the tags with the number naming scheme to the group @@ -434,7 +436,7 @@ Add the newly created tags to the `NumberTags` group with the following mutation mutation { CoreStandardGroupUpdate( data: { - id: "", + hfid: ["NumberTags]", members: [ {id: ""}, {id: ""} diff --git a/docs/docs/guides/groups.mdx b/docs/docs/guides/groups.mdx index b532e32ced..61964f49aa 100644 --- a/docs/docs/guides/groups.mdx +++ b/docs/docs/guides/groups.mdx @@ -79,14 +79,13 @@ mutation UpdateGroupMembers { } ``` -![Adding members in group](../media/group_tagconfig_grp_adding_members.png) The resulting mutation should look like this (note that the ids will be different in your case). ```graphql mutation UpdateGroupMembers { CoreStandardGroupUpdate( data: { - id: "TagConfigGroup", + hfid: ["TagConfigGroup"], members: [ {id: "17a8f438-fe39-d85f-3c3b-c51d66d0603f"}, {id: "17a8f437-157a-e023-3c3c-c51f788ddf91"}]} diff --git a/docs/docs/guides/installation.mdx b/docs/docs/guides/installation.mdx index 5b22ce085a..b7eaea2da3 100644 --- a/docs/docs/guides/installation.mdx +++ b/docs/docs/guides/installation.mdx @@ -188,8 +188,8 @@ A first version of our K8S helm-chart is available in our repository. The following are required for production deployments using Helm: * data persistence must be enabled (except for the Infrahub API Server if using S3 storage) -* multiple replicas of the Infrahub API Server and Infrahub Git Agents should be deployed: you can make use of the `affinity` variable to define the affinity policy for the pods -* a shared storage should be available for use by the Git Agents (through a StorageClass that supports RWX accesses) +* multiple replicas of the Infrahub API Server and Infrahub Task workers should be deployed: you can make use of the `affinity` variable to define the affinity policy for the pods +* a shared storage should be available for use by the Task workers (through a StorageClass that supports RWX accesses) * S3 storage should be configured for the Infrahub API Server :::warning 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/menu.mdx b/docs/docs/guides/menu.mdx new file mode 100644 index 0000000000..3aa09e8897 --- /dev/null +++ b/docs/docs/guides/menu.mdx @@ -0,0 +1,177 @@ +--- +title: Controlling the menu +--- + +# Controlling the menu + +Infrahub allows you to control the menu on the left side of the web interface. + +The menu is made up of 2 sections, the bottom section is fixed and cannot be changed. It will list menu items that are related to the working of multiple Infrahub features. + +The top section is the section that can be changed, or controlled by the user. The goal of this top section is to list the different types of nodes that you have defined in your schema, in an order and organised in a way that is most relevant for your use case. + +By default the top section of the menu will contain an IPAM and Other section. + +The goal of this guide is to show you how you can control or change the layout of the top section of the menu. + +## Loading an example schema into Infrahub + +We assume that you have an empty instance of Infrahub started. + +Save the following schema into a file on your disk. + +The schema contains the following nodes: + +- A location hierarchy with a Country and a Site +- A network device with a relation to network interfaces and a site +- A network interface with a relation to a network device + +```yaml +--- +version: "1.0" +generics: + - name: Generic + namespace: Location + include_in_menu: false + hierarchical: true + attributes: + - name: name + kind: Text + optional: false + unique: true +nodes: + - name: Country + namespace: Location + inherit_from: + - LocationGeneric + parent: "" + children: LocationSite + - name: Site + namespace: Location + inherit_from: + - LocationGeneric + parent: LocationCountry + children: "" + relationships: + - name: devices + kind: Generic + peer: NetworkDevice + cardinality: many + optional: true + - name: Device + namespace: Network + attributes: + - name: name + kind: Text + optional: false + unique: true + relationships: + - name: site + kind: Attribute + cardinality: one + optional: true + peer: LocationSite + - name: interfaces + kind: Component + cardinality: many + optional: true + peer: NetworkInterface + - name: Interface + namespace: Network + attributes: + - name: name + kind: Text + optional: false + relationships: + - name: device + kind: Parent + optional: false + cardinality: one + peer: NetworkDevice +``` + +Load the schema into Infrahub using the following command + +```bash +infrahubctl schema load /path/to/schema.yml +``` + +In the web interface you will now see that all the nodes defined in the schema are available in the Other section. + +## Defining a menu file + +Our goal is to define a menu in which we define 2 top sections Location and Infrastructure. + +Under the Location section we want to define 2 items, Countries and Sites. Under the Infrastructure section we want to define 2 items, Devices and Interfaces. + +We can define this menu structure in a menu file. A menu file is a YAML file that has a particular structure (schema). For more information on the structure of the file, visit the menu file reference. + +Save the following menu definition file on your local disk. + +```yaml +--- +apiVersion: infrahub.app/v1 +kind: Menu +spec: + data: + - namespace: Location + name: Mainmenu + label: Location + icon: "mingcute:location-line" + children: + data: + - namespace: Location + name: Country + label: Countries + kind: LocationCountry + icon: "gis:search-country" + + - namespace: Location + name: Site + label: Sites + kind: LocationSite + icon: "ri:building-line" + + - namespace: Infrastructure + name: Mainmenu + label: Infrastructure + icon: "mdi:domain" + children: + data: + - namespace: Network + name: Device + label: Devices + kind: NetworkDevice + icon: "mdi:router" + + - namespace: Network + name: Interface + label: Interface + kind: NetworkInterface + icon: "mdi:ethernet" +``` + +At the top of the file we find some required boilerplate to indicate the type of the contents of the file, so that we understand what content is going to provided and what the expected structure should be. + +In the spec mapping we find a data key which defines a sequence. Here we can clearly find the 2 menu items Location and Infrastructure. Each item in the menu structure has a name and namespace defined and some additional properties to control how it is displayed in the web interface, like the icon. + +The Location and Infrastructure menu items have children defined, the children are menu items that reference a kind that we defined in the schema. This will result in a sub menu item, which, when you click on it, will open the list view of the referenced kind in the schema. + +:::info + +In more complex scenarios where you have to define a lot of different menu structures, you may want to split the menu definitions into multiple files, similar to how we do this with schema files. + +::: + +## Loading a menu file + +You can load the menu file into Infrahub using `menu` subcommand of the `infrahubctl` utility. + +Load the menu into Infrahub using the following command + +```bash +infrahubctl menu load /path/to/menu.yml +``` + +More information on `infrahubctl` command line utility can be found [here](/infrahubctl). +More information on the `menu` subcommand can be found [here](/infrahubctl/infrahubctl-menu). diff --git a/docs/docs/guides/repository.mdx b/docs/docs/guides/repository.mdx index dba099193e..b77a5a2a3f 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 ... @@ -243,7 +247,7 @@ Unlike `Repository`, Infrahub does not automatically update `Read-only Repositor mutation { CoreReadOnlyRepositoryUpdate( data: { - id: "ID_OF_THE_REPOSITORY" + hfid: ["My Git repository"] ref: { value: "BRANCH/TAG/COMMIT_TO_TRACK" } } ) { diff --git a/docs/docs/guides/sso.mdx b/docs/docs/guides/sso.mdx new file mode 100644 index 0000000000..eb013cbd86 --- /dev/null +++ b/docs/docs/guides/sso.mdx @@ -0,0 +1,235 @@ +--- +title: Configuring Single sign-on +--- +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Configuring Single sign-on + +In Infrahub you can configure SSO using either Open ID Connect (OIDC) or can use OAuth2. + +We can enable 3 different identity providers in Infrahub: + +* PROVIDER1 +* PROVIDER2 +* GOOGLE + +PROVIDER1 and PROVIDER2 can be used to configure any identity provider that supports OAuth2 or Open ID Connect (OIDC). GOOGLE can be used if you are using Google Workspace as your identity provider, the main difference with the other providers is that GOOGLE has some predefined configuration settings, which limits 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 setup PROVIDER1 as an OAuth2 identify provider in Infrahub. Configuring the first provider with OAuth2 uses environment variables with the `INFRAHUB_OAUTH2_PROVIDER1_` prefix. For PROVIDER2 and GOOGLE the prefixes are `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" + ``` + + + + + ```toml + [security.oauth2_provider_settings.provider1] + client_id = "infrahub-sso" + client_secret = "edPf4IaquQaqns7t3s95mLhKKYdwL1up" + authorization_url = "http://localhost:8180/realms/infrahub/protocol/openid-connect/auth" + token_url = "http://localhost:8180/realms/infrahub/protocol/openid-connect/token" + userinfo_url = "http://localhost:8180/realms/infrahub/protocol/openid-connect/userinfo" + display_label = "Internal Server (Keycloak)" + 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"]' + ``` + + + + + ```toml + [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"]' + ``` + + + + + ```toml + [security] + oauth2_providers = ["provider1", "provider2"] + ``` + + + + +## Setting up OIDC in Infrahub + +In this case we are going to setup PROVIDER1 as an OIDC identify provider in Infrahub. Configuring the first provider with OIDC uses environment variables with the `INFRAHUB_OIDC_PROVIDER1_` prefix. For PROVIDER2 and GOOGLE the prefixes are `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_OIDC_PROVIDER1_SCOPES | `Array[Text]` | The scopes to request from the IDP | `false` | +| INFRAHUB_OIDC_PROVIDER1_DISPLAY_LABEL | `Text` | Display label for the provider on the login screen | `false` | +| INFRAHUB_OIDC_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" + ``` + + + + + ```toml + [security.oidc_provider_settings.provider1] + client_id = "infrahub-sso" + client_secret = "edPf4IaquQaqns7t3s95mLhKKYdwL1up" + discovery_url = "http://localhost:8180/realms/infrahub/.well-known/openid-configuration" + display_label = "Internal Server (Keycloak)" + 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"]' + ``` + + + + + ```toml + [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"]' + ``` + + + + + ```toml + [security] + oidc_providers = ["provider1", "provider2"] + ``` + + + + +## Configuring the redirect URI in the identity provider + +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-menu.mdx b/docs/docs/infrahubctl/infrahubctl-menu.mdx new file mode 100644 index 0000000000..6cd9312414 --- /dev/null +++ b/docs/docs/infrahubctl/infrahubctl-menu.mdx @@ -0,0 +1,40 @@ +# `infrahubctl menu` + +Manage the menu in a remote Infrahub instance. + +**Usage**: + +```console +$ infrahubctl menu [OPTIONS] COMMAND [ARGS]... +``` + +**Options**: + +* `--install-completion`: Install completion for the current shell. +* `--show-completion`: Show completion for the current shell, to copy it or customize the installation. +* `--help`: Show this message and exit. + +**Commands**: + +* `load`: Load one or multiple menu files into... + +## `infrahubctl menu load` + +Load one or multiple menu files into Infrahub. + +**Usage**: + +```console +$ infrahubctl menu load [OPTIONS] MENUS... +``` + +**Arguments**: + +* `MENUS...`: [required] + +**Options**: + +* `--debug / --no-debug`: [default: no-debug] +* `--branch TEXT`: Branch on which to load the menu. [default: main] +* `--config-file TEXT`: [env var: INFRAHUBCTL_CONFIG; default: infrahubctl.toml] +* `--help`: Show this message and exit. diff --git a/docs/docs/infrahubctl/infrahubctl-object.mdx b/docs/docs/infrahubctl/infrahubctl-object.mdx new file mode 100644 index 0000000000..342b89558f --- /dev/null +++ b/docs/docs/infrahubctl/infrahubctl-object.mdx @@ -0,0 +1,40 @@ +# `infrahubctl object` + +Manage objects in a remote Infrahub instance. + +**Usage**: + +```console +$ infrahubctl object [OPTIONS] COMMAND [ARGS]... +``` + +**Options**: + +* `--install-completion`: Install completion for the current shell. +* `--show-completion`: Show completion for the current shell, to copy it or customize the installation. +* `--help`: Show this message and exit. + +**Commands**: + +* `load`: Load one or multiple objects files into... + +## `infrahubctl object load` + +Load one or multiple objects files into Infrahub. + +**Usage**: + +```console +$ infrahubctl object load [OPTIONS] PATHS... +``` + +**Arguments**: + +* `PATHS...`: [required] + +**Options**: + +* `--debug / --no-debug`: [default: no-debug] +* `--branch TEXT`: Branch on which to load the objects. [default: main] +* `--config-file TEXT`: [env var: INFRAHUBCTL_CONFIG; default: infrahubctl.toml] +* `--help`: Show this message and exit. diff --git a/docs/docs/media/group_tagconfig_grp_adding_members.png b/docs/docs/media/group_tagconfig_grp_adding_members.png index 3180f373c8..c8e09d5d1e 100644 Binary files a/docs/docs/media/group_tagconfig_grp_adding_members.png and b/docs/docs/media/group_tagconfig_grp_adding_members.png differ diff --git a/docs/docs/media/group_tagconfig_grp_new_grp.png b/docs/docs/media/group_tagconfig_grp_new_grp.png index e7eceeba49..0147f1b678 100644 Binary files a/docs/docs/media/group_tagconfig_grp_new_grp.png and b/docs/docs/media/group_tagconfig_grp_new_grp.png differ diff --git a/docs/docs/media/high_level_architecture.excalidraw.svg b/docs/docs/media/high_level_architecture.excalidraw.svg index 79125a302f..71e055c600 100644 --- a/docs/docs/media/high_level_architecture.excalidraw.svg +++ b/docs/docs/media/high_level_architecture.excalidraw.svg @@ -1,6 +1,6 @@ - + - + eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nO1daW9cIkuy/d6/wur5Mk9cdTAwMWFqcl9GenrCNtjQ3jDG29yRxVJcdTAwMDbM2oBcdTAwMTc8uv99XCKx21x1MDAxNEVtmCoo91x1MDAxYqTbt1x1MDAxYmxIqvLEOVx1MDAxMVx1MDAxOcu/v+3sfJ9Mh/b3f+x8t1/q1W67Mao+f/+bef7JXHUwMDFljduDPrxEZv9cdTAwMWVcdTAwMGZcdTAwMWVH9dlPtiaT4fhcdTAwMWZ//3uvOurYk2G3Wretp/b4sdpcdTAwMWRPXHUwMDFlXHUwMDFi7YFVXHUwMDFm9P7enti98f+ZP0+qPft/h4NeYzKy5lx1MDAxZpKxXHUwMDFi7clg9PZZdtfu2f3JXHUwMDE43v2f8O+dnX/P/nSsbmTXJ9V+s2vPfmH20nyBXFy7nzxcdTAwMTn0Z2vFmFCBOKf04yfa4334uIndgJfvYcn2/Fx1MDAxNfPU95LeLXee1TM+sotcdTAwMGad4mHx7GzvbP6p9+1utzyZdmerXHUwMDFhXHUwMDBm4MvMX1x1MDAxYk9Gg4591W5MWr8um+N5v99cdTAwMWFcclx1MDAxZZutvj0231x1MDAxZX88O1x1MDAxOFbr7cnUPIfQx7Nvl+BcdTAwMWY782deZt9fWVhRXCKVpFx1MDAxMjPNPl41v6+0tFx1MDAxNOeCKClcYiZcdTAwMWG71rU36MJ9gHX9hdxrm7H5ymrVeqdcdMvrNz5+ZjKq9sfD6lxi7tb8555/fWOELIThk/jHSy273WxN4DUpLaw1wUrOX1x1MDAxY9uzu4A1U1x1MDAwNG5cdTAwMTGbr8x86rDQmG2If82v/VxitlLB/Er/sdt1XsB+4/1cdTAwMDL+2jjzrUPfn/lz/rXMz+fcW8657Vx1MDAxY9th/ye6urav1eFcdTAwMWU7frDtYqfYr+U+vvrCXHUwMDFlndgvk+9cdTAwMWYv/Pn+t/nyXHUwMDFmh43q277DkmiplabwXHUwMDE3+fF6t93vuL9bd1DvzLfqN8dcdTAwMTdZgsjs4z3QQYlcdTAwMWY6hEBCXHUwMDFhiERcdTAwMDZH8NVIJThcdTAwMDRmXHUwMDE2x1x1MDAxNFx1MDAxM7jgiGLKXHUwMDE3wKGxsjTnhCpcZtBJXHUwMDEyXHUwMDFjWEiLcq6JgNXAZVdqXHUwMDE5JHRcdFx1MDAxYpQoXHUwMDAyq8JyfWgsvLCEgTi36XxVg/6k3H41N4WohWfz1V67a27A/Fx1MDAxYs/2L1xcxXZcdTAwMWa+TOuxllx1MDAxOTc631x1MDAxN17NdttNs6O/12HV9mhhs0/awCxcdTAwMWY/0Gs3XHUwMDFhTq6ow0dW2317VIhi41x1MDAwN6N2s92vdi9cdTAwMDJXXHUwMDA018E+/HXnsOUwerXq2Davmq8tXHUwMDAyMVx1MDAxYkhrXHUwMDFhuZ/8oDUhzIZcdTAwMTZyflfCkFspP9VJvjTJqVxc73DauOtePFxc3aZcdTAwMWK5SsBVxYyTr0lrXHUwMDAwXFxcdLhxSI/0sNpcdTAwMTV63NP309bJeYF2MvvTl0Jm7/BLsZqkfuCQcKtcdTAwMDTYdFx1MDAxZVx1MDAxOVx1MDAxYsFXI53YUMpSilHJKFZcdTAwMDQ5pNOGWY0xS0iuKVVUKo1cdTAwMDRZxohcdTAwMDerMYpcdTAwMTRCRK9cdTAwMGaNr8Zq9Uk3IVJcdTAwMGKx8H6ktrCgxDlcciPlftZBakhoIXV0UnvNXHUwMDFjXGaG+bvsefO+/lq87lx1MDAxZpzc9C7SXHJcXFxmWs5SUnFccrrbsNqiXHUwMDFlVVpYLFx1MDAxMnLvkaojlFxmq6n5mn/5aFxiJDRcdTAwMDPBkUZcdTAwMWZtMsjyKsqz0S5plFx1MDAxYaelfC3/+LXYXGZ2hS8sXHUwMDE4Z1xunHtcdTAwMTXdS1x1MDAwYr5cdTAwMWUphVx1MDAwNVx1MDAxM1x1MDAxNkeIUVwihITNL1x1MDAxNlx0XHLhXHLBXHUwMDAyM2LBLteMYKp83DS5XGZcdTAwMGbBNDh1XHUwMDAyxSD2vlxuo9VGg+exPfqj/9f7XHUwMDEx/KTdb/xPQsxcdTAwMTZi5t3M9r6wXHUwMDFkr3VFXCI4gdchOOl+9lx1MDAwM8mSXGKsXHTi8+1cdTAwMTOGZESaXHUwMDE1Uj0+t1x1MDAxZq5cdTAwMDRcdTAwMTOXP8bX4+x1upEsJbWExuhcdTAwMTe/LXptXHUwMDE4XHUwMDEzXHUwMDEwrlx1MDAwMoNJe0PyVrw2T35TlCHF08hvclI5yD9cdTAwMWSNy1eKZKdPt9f1/HMjXHUwMDEyv/0t6G0vXHUwMDFlTjqC8cfSyeF17bq3J24qnZH321ZHgKmo71s9K7RcdTAwMDet5n71JpuHt8+h3dfXON730j6QpENKjUI3226j6/L+j8JBXGbva+cznfOzcvZgfzrOZn4+nPPLh+NcdTAwMTjet3NQ5PXW/lWzzOza1eBouHdX6sfwvme115/13PA0o8s/ru2D2vS5//BcdTAwMTLD++JTNcKj03Jb3z31RKmafejVLqO9b4iOUowoSZjDS01IR2nfWDdcdTAwMDZ0XHUwMDBiSuXc4ITZ3mDUpdP2amVcdHCrXHTXnGom5mrkzfZSulx1MDAxOduLObKwxiZcYiNcdTAwMDRcXHfPyNmSXHIm5vZw5oikbV5DXHUwMDExXCKZ4its0/U0VOHdXHT/o589K+yU7dGTUyfFKqJCpIRbRFx1MDAwNa8scVx1MDAxOaWY+8lfQNagn8CYOIg6XGbIR/3dqSpcdTAwMWOSdp71XHUwMDFl6/nMSfn1iKVcdTAwMWPIXGZbXHUwMDE0a61hN3qIKFx1MDAwNDh3ukPrXHUwMDAw+S/3s8e6XHUwMDAyinFccqtJo3qqXHUwMDE3zopcctSyn88uXuhtdjpRr3tsffXUOjlcdTAwMTB3uXxlX1WHx4/ssHpP6WNcZix80ytdNI4m+YfBXeHy/qFyJk5qhXhYXHUwMDE4XHUwMDAxXHRyzoRcdTAwMTPGicTmg1x1MDAwZa6E4Fxc0egxvuDbl070XG5hXHUwMDExajIyXGLyomFEY0NvXHUwMDEyNIxcdTAwMTnRsFnENs+cQVx1MDAwYqy2Ub9cZlx1MDAwZoewUcp4mCr/eD3iXHUwMDA0XHUwMDBiTdBcdTAwMWNYYVjWtsrz7PSk0lD1w1x1MDAxZlx1MDAwZplBMStTnltcdTAwMDX70KJiflxi7VxisM3egFx1MDAwMUuC2CaKI0yCwIy1xHWyqXhcdTAwMDYzwXpzLJdCQu5cZmu1i/xpq3TP7i4yz6XzSrfSW5+Q13Djw4hcdTAwMTPcV85cdTAwMTBP3H2lMiB4SDFsL7ZcdTAwMDJzXHUwMDA2X+d0ok0oi3NEuVRG+1xu7Vx1MDAwNlx1MDAxYt9cZtiMpyxcdTAwMTjVoFZAgSMmlzHnQZxIXHUwMDEzKqhT3GyYOEHhwTVTSK+wUdcjzoNRddja2d/9o//Xvj1gXHUwMDBmSVx1MDAxZFx1MDAwMYQwh5s1XHUwMDAzlpU4ZTLsXHUwMDFihFx1MDAwMmklXHUwMDE0gz1cdTAwMTL9XHUwMDA04JKM0FCdPe3dtnZ3z55cdTAwMWbGg/bUTjeIQdpaimkqiafzmlLGxKDLkVCYxFx1MDAxMH6KnTJf5WWmlENPXHUwMDA3Zyi/N8iqfubE9nFcblehzNz+qDKuX1x1MDAxNuzKWD7dXHUwMDFjXYxsze1olFx1MDAxOfi+SUXq14h8R6J4lTzFM+KbuKbB82GE8Ogh6uBtkU7jIJjFXHUwMDA0XHUwMDAwXHUwMDFmca6FkktyelNcZi+FJbUpPYD7XHUwMDBlvFx1MDAxZM03ht/SnHDCtlx1MDAxNqM2XHUwMDE0LynCmzvnP4blVpv2zu7jXHUwMDE46HRUrdXak53ez6SYPoTw3ExcdTAwMWa+uuRcdJ/611x1MDAxZiFccvdcZsk5XHUwMDA1hkG60Xx+KZVGXHUwMDE3j6Wbg8y+vilmO93TdENaSmRRylxiXHUwMDE2Xlx1MDAxOW2p5XulmYb/Ulx1MDAxObI+3a10aZbu9c6aRfRcIjpV1r1fn+6TOvD/XHJcdTAwMGWOmfA/OdZcYlBMNVohZlx1MDAxZHj/Ulx0YkWEhZhAXHUwMDFj4MqlO6F8c7yslMVcci1ruCM++eRenreUSlx1MDAwMPK3llC+eVreq9ZbtqE8u9FcdTAwMWUnRcYhbOQmY781JU7BRHD3s7/gy7niXHUwMDFjczq/sKFFjs0huVx1MDAxMrq+f50rndUqXHUwMDA3pZvjUS3d6MVcdTAwMDTwSVx1MDAxMVx1MDAxOCkqvTiYam1xqcLhu3b6rJZcdTAwMTbmhDIqvViYWJJpx2OJlLGQlJr0lVx1MDAxNLLy7fHes2jR7Dl6XHUwMDFknz7tTZTIPPt4tauwMin1J6dyP1/PtZ461ed8vzEtluJiT8kwXHUwMDEyq+SzfIo9KfE/JVJaXG5cdTAwMDRriO7VXHUwMDA2X+eU4lx1MDAwZmtcdTAwMGKYXHUwMDEzXHUwMDEzXG62xji3LvrEXHUwMDEx6XNt/Fx0U/mlNVx1MDAxMLimSirsXHUwMDExuebLWlhr4C6maVxmYnjL/IlcdTAwMTaeXHLgz3N7OFx1MDAxOJtcdTAwMTZcdTAwMTTTnTL8aWjrJF/eXHUwMDE5jHbgU6rdpFx1MDAxODWEXFzcjFx1MDAxYX2VkTiWsTU4XHUwMDE2+1x1MDAxZU5cdTAwMTFQjsToteggt2vq7JSfdi4qXHUwMDA3I/vicnRC7p7GKVx1MDAwNzlgytKEM+Hn6Fx1MDAxMuA+xrhQKjCvY32SXHUwMDA1kFx1MDAwYmbKPzkyj3lI0VmYzMHyXHUwMDFhb9j1I1x1MDAxZlSLuJBMqHRRrSfVOPZNfe9SVCU+zPHq0ytmte5xq3hcdTAwMWSVaks/Lorjfdks/CCX5Gf5+eSw2vI5eV49t2rlk7dPUS0h/kfEXFxwpFx1MDAxNCHRUyO9r2bKUcilpcHUXHUwMDAwwVx04oFCYrKrNkG1hFiKcu2pdJdrnjE41VxuLIfeKsdu+HS4PdkxXGaWXHUwMDEwl4ZwyNKh8NJq4ih39o0oSd+oMFNcXHLhLIVcdTAwMGXD6fNZXHUwMDA3oWZzl1x1MDAwZk+QXHUwMDE4ilwiespO99OOU6BDrvRH5pSr8Vx1MDAwZUNcdTAwMTanklxiI1NcdTAwMDMrOiNElHxzmFx1MDAxNVx1MDAwNTWstPJ2Rz1AXG5cdTAwMThlgtI4XHUwMDBlgT9cdTAwMGJSXHUwMDAyq1V0lYjneiA9spt2v+FcdTAwMDPRrn0/XHRcdTAwMDDoZDD0Q+fCet1QdH9m4n1cdTAwMDd4QFkm7FSNiODRXHUwMDBirHs6oyp9cVqZdFx1MDAwYsX24VPhqdLNp1x1MDAxY46UaouKWVx1MDAxZD/2oE3wXS0tlVx1MDAwMMOUcMtcdTAwMTDA5Cyw61x1MDAwMUiGLXBcdTAwMDJBk3o0iTNcdTAwMDfj4Fxco22d11x1MDAwNLAq2DHpSPJOSPlcdMc9XkpcdTAwMGVEoCxcdTAwMTBD0c9cdTAwMTnH4yuda+TZiFx1MDAxZVx1MDAxN69cdTAwMDbHe1U8vEh5Ki4oP2xxXHUwMDAzV+8gp9nCwDVcdTAwMDS9+19rbWFfSjH9O4C2vI9cdTAwMTmZR2GxZiBGhdji0UQ8jILFwrNcdTAwMTF63ex8MMJcdTAwMWb9wWhnPFx1MDAxZE/s3maJZnkpO1x1MDAxZSuJRD+Ur0E/3Fx1MDAxN7tkVs9m+k1Exu7Tz261/tRTlf3q4Y/z/nHdzvZpyrFcdTAwMWJGP0JbTFwiJWhITcza54ufY1x1MDAxZiRcdTAwMDUnXGbLXHUwMDE4UPxF2Yf5ujOgnChCnIvIXHUwMDFiWNcm2cp977HWeM1cdTAwMWZcdTAwMWRcdTAwMWOV8FGlmPJmhG/kI1x1MDAwNPLu2zTbwFSYqmtcdTAwMWFcdTAwMWP8W8edwYRbRC9s0Fx1MDAwMOpcdTAwMDHLolx1MDAwNGNcYv9/pJ5cdTAwMWEssWs3/uhcdTAwMDdcdTAwMWH+jVLQ+5K2TUX+cVx0XHUwMDAxW8tkba/QOfH052goOo1i/iV39vhaVuri6jnlQFx1MDAwZWFcIlxmL0uuNXl3hHyBvG788HNMRICJqIrlpO5rXHUwMDEykVxmaJWmXGJYSMzYXG5uUOFggoqvXHUwMDFkfP9SuzjMjc5cbj9vuinfwIxcdTAwMTNLM/bRXHUwMDFjwGNcdTAwMDOLmI6hXHUwMDAymFxir+JcdTAwMDVJLFx1MDAxOIctvXUmXCIrbM/1mIiOXHUwMDFhO1x1MDAwMP3JNFx1MDAwNV6Qx1q2wj1EXG73s1x1MDAxZjFxc2RcdTAwMDNcdTAwMDYkelx1MDAxMG5azZSeUXFSLLxWcoedp0mh6mgzlUroaiUthTVcdTAwMDJ6mSF3sclhajOl9axeg8RcdTAwMTHCiD0p6+CqQTp6byo0O8ZscHlfat48rJ+UNZ10Rb7btHMvfF9li8WXXGZ6eo0hVXqN3mjvf4szg+ZTXHUwMDA0TLRvb1x1MDAxZUI55XiVVqXBty+dINbUXCJEaVxmPlx1MDAxZqZCUTeIN1XBpLSFNdewXHUwMDE27ted3iNcdTAwMWOJqJZcdTAwMDJ+d3tMvOlM6YvquLNzXFztV5uzbqXDkX1cdTAwMGYklVSCV1xiLbmpOXRxceROv5lcdTAwMTJcdTAwMGYsO6tdXVhWXHUwMDEyXHTDXHUwMDEx0Z3B3YfCzajzWq1Me8NKM5dcdTAwMWLX769TXo0owVx0w1p9pE0vliprLSxcdTAwMGXKMbzrsDk05upzUpqBO+ry9T5cdTAwMDCsiWUs6lx1MDAwMrrnSNbGzsRRirjMymQ9Vlx1MDAxZeuDelx1MDAwYnDQ2H+4Z1e3g9bg5KZcdTAwMWSJlUNYToJwXHUwMDAyk8s+lcMynoBcdTAwMDbebfdcdTAwMWLtftP9K3a/MX/FcT3eR5NF6d0zs0T1R3NxkIVcdTAwMTnVXFyBM8aNnZdcXDp+qllcdTAwMWSae2tRupiZMFx1MDAwM8mL3ThcdTAwMWK0+5P3XHUwMDA1Lt2HbnU82Vx1MDAxYvR67clk8SdcdTAwMTe/ZtZAvmVXl+47fFHna27bMDTvuHh//+m4bch5XHUwMDBm0cff//U3z59e3trm4djU8zf45vz/yu3Tfd2KWVE/12yFw/3gvZtOMyaZJZFcdTAwMTk2g1x1MDAxNdHc0ZHp7fdNplx1MDAxYZNcbpsk3cTsmGl6Zlx1MDAwZbKUQMps7LktXHJcYlxmaJNcdTAwMWQk1FYngXzeoHwqMDDrTVI6+qN/nitfJCRCQqjYs13K8pLiiFx1MDAwN/hKXHUwMDBm4i89sFx1MDAxNMBtdFx1MDAxNe1cdTAwMTHcjTGVoNUmjCf8xltpsJLI9O5cdTAwMGJNZlhcdTAwMDezYDf8q7VgXHUwMDA1YC7QQoLrL/FhnFxiXHIoTyRcZr2m+Lg9yVx1MDAxZNDDOznqXFzsXHUwMDFktkYnXHUwMDA3Q1KpxCQ+XHUwMDE04fhzQcTNiY9cZrJM+Vx1MDAwZuwpbKZDMbhPwvFjb+pcdTAwMDNTS7KFuWW/nfrIeOxu83Ds67j0h39yoSnTQMjpb4eWv1x1MDAwNW7fVJqyr6s/wJBcdTAwMDFcdTAwMDS4XHUwMDE2MXhRn1x1MDAxNyDghrJ5OON3XHUwMDEwICF8nFx1MDAwNlx1MDAwMeI8jXJcdTAwMWZGaMyx4CtcdTAwMWNGiIeJPPx5Xz47v33It1C3YrNxymNcdTAwMWbgXHUwMDFhgSNGKCbE61x1MDAxY1FrZlx1MDAwMZw5XHUwMDBlTdBfXHUwMDA3tFjR5Vxih1OBaOSSJ79OXHUwMDE0XHUwMDE1XHUwMDE3IELiOFGMXYDsnozZ9VP/6IXxm8db1VQ/RyNcdTAwMWWPXHUwMDAw0SZVXaVfgGCKuUJcdTAwMTKZZodSXHUwMDEw5iFAXHUwMDAwz/S3jn5kvDa3eTi2dVxcXHUwMDAyxLf+nmlgXFwzUS2yKVx1MDAwYt69qTRlX1d/zCa4cuex98blx1x1MDAxYSYltfIjhI43KD+CO8f5T0pgXG6ZXHUwMDE2SCR6Ui2Xtyen5Ux+cFLM3Vx1MDAxZT83LuuHQqRcdTAwMWK3XHUwMDE4aMJSfJ5cdTAwMTDhSmVcIrOEXGKNWVhS7ZZGfFx1MDAxM86FRDyOw9TYXHUwMDEzI5rn7ctC7/z2Ym/c40M+uea1R5+2Mlx1MDAxYupWXHUwMDEz+L5rlOZcdTAwMDe+71x1MDAxYYlcdTAwMWMhYmxj3XWYf3MrXHUwMDAyXHUwMDFmr0BcdTAwMDSj6OGF4H2RUjPBpIVMlaZcdTAwMTCANmdC+5uZXHUwMDAwfofnJTdSM9GBKsKkVmIzaFx1MDAxNaSE5lx1MDAxZX03lmuKmVx1MDAwMi1AYsme+nzGXHUwMDA1XHUwMDA3XHUwMDBit7mxZrOkhqvBqJPYXHUwMDE0lVx1MDAxMLLzzLJwLyjxomPGfSNcZrBNXHUwMDE1+NZCR09VtitcdTAwMDWCj1ilr8r9p+lL+aiz/zPl81xiMdLcMs33kDe/Y8ktXHUwMDAy5I5QSK59UnPMXHUwMDAyuFx1MDAxZDOlicmAiWGaQ+zkXlC7w9NG42f1/PkuU3lcdTAwMWNccjI3x2h9ck8q6zGpPvP/nbDqXHUwMDEwI0oq6XA6k1x1MDAxMiP+laxYY1x1MDAwNbRIafSwafA+TqdNw1x1MDAxOFlCS8y9xVxi1sKijGpTjp4+MYIllYzzrTY4eVMjm1x1MDAxYu6WvFx1MDAxYVx0oeZ0qFx1MDAxMYX8XHUwMDEzLoBcdTAwMWRcdTAwMDX42Ssgt3/z/DRtv9RcdTAwMWIjodqkbO89Ptv9dCM3bJRcdTAwMWKhymJKhI9H3naTXFwmXHUwMDE4onh7rVCCKI5ccquX5Ydq/e6s0+pOXHUwMDBlrvV5u0LWVyaJTkVlXG6L5JlTXHUwMDA39W+hkiDCWfRcdTAwMTbzwdc5nfiTyFJcdTAwMDJJKjQx40WJazBcdTAwMTR8/2jjydfv3CdcdTAwMDB/oPtN6iE2k1GXUbjcJFx1MDAxN5aOXHUwMDE143FcZoz4PHOuulM9mTNyj9zT2lx1MDAwMzDKR+fZMt2pPcK7J1Y7XHUwMDExQipuXHUwMDFljbC8OLri+mZcdTAwMTBo/7xcdTAwMWZJXGKZnTZFXHUwMDA2dLCFSyWgwWpZYLbUL+/ehWfMmKVRhELktVx1MDAxMlxitDFcdTAwMWGULFxmbvtAMabCXCKEXHUwMDEzkDZcbpx8x7SpjykwXHUwMDAyXHUwMDE0O+XJXGZKXTOPQOVcdTAwMWVf2fToufNw3eE/XHUwMDFh1dZdMXdcdTAwMThHXHUwMDFlgZlcdTAwMDRcdTAwMGVfmcSRR1x1MDAxMEOtXHUwMDA0N01JXHUwMDE1RmR2XHUwMDFiXHUwMDE3Mlx1MDAwMt5yXHUwMDA1mIWxXFw5VzFSUkOwyVlYptBAXHUwMDFjcN1cdTAwMTg3/TvnbVxiPpapLNeY7UjL/FopXHJcdTAwMWVwM48loM3f6Zvz/ytcdTAwMTeb+lpYPlx1MDAxYneBVzj5XGKGUyotLJemzVx1MDAwMmNcdTAwMTSurFx1MDAxOYzhdli0hVx1MDAxMMJcIizUsFZ5XHUwMDFhs8RsVFx1MDAxZnyAoOBcdTAwMWYtW1mynFaJJEeaMrS1MIPU8PHYudyk81x1MDAxYVqTyXCckDBcblx1MDAxMVx1MDAwN25h5FpKJFx1MDAxMYQ/mUYpXHUwMDAyZn/AdsGSO5NLQlx1MDAwZjlcdTAwMDKHeKdcdTAwMTKjUmgzOYtzb1x1MDAxNUSQtoDbJEpSXHUwMDA1XHUwMDExpiyp/VqDmVmXb36OR1x1MDAxOSk4W0pcdTAwMGJG58tOg1x1MDAwMFxuXHUwMDFiXHUwMDAz8NpcInu391x1MDAxZDYwtrGzVztqnXbzyU50jyCA5lqBXCIwf1x1MDAxYYHPqFx1MDAxOeaULOc/gllcdTAwMDVpTFx1MDAxN3h0UZog0qyQ6vG5/XAlmLj8Mb5cdTAwMWVnP0ZcdTAwMWSEy5z5YsxANyBcdTAwMGKKXGKRQkqyXFyLiqnlOlx1MDAwNnMvxntcdTAwMDD311x1MDAxNjNeqDFcdTAwMGYnXmLSMcx/vCA301x1MDAwNcFccok+XHUwMDEz3Hvzp9pIXG4hLKo1Z1x1MDAxYSuqiZhzxpuRlKZcdTAwMGVfstCma2slaFx1MDAxMkuBLOBcdTAwMWGjN+gtW0qPzlVcdTAwMTJTgrnDVdp01OfdVM1XlniCpsmE/Gt9OmzZo6RcIj0hPL+UoOm9pETrQ4T279nLXHUwMDEw3Fx1MDAxNL7CcUnwRPRUQlZcdTAwMTFcdTAwMTAuSjDv3lx1MDAxOKZqxOJSUIqTjO5wsNBi0fd36Fx1MDAxYUEtT00jJWeUXHUwMDEwnMbq1Hbl/ufLbjF31+yWXHUwMDBmOrVqtizq1ZiCOrAtMfpUeDhiUMdbkjg0g7NcdTAwMDZcdTAwMTVcXEBFQGMxyShcdTAwMTbco1x1MDAwNIRbsLFWb4FcdTAwMTEpsFx1MDAxMzxHfHGpcPGEJlK+xdc19VgqseBcdTAwMDK7Wln8dsFcdTAwMWRcdTAwMGa0mccvnMWlhfzHpWum4S7wXHUwMDE1Ri1cdTAwMDfDKZ2GlTJcdTAwMGJR8LiMX8BcdTAwMTFzZ7MqaVFHtUpcIobVXHUwMDFjhZsut7OWqaZccu2ydV2O6oCrXHUwMDBm0lhtr5+0XHTqKC70ur3Domuh7HEpIVx1MDAwNVx1MDAxNKJcYtxcbmhhIYlcdTAwMDZ0zFxmJF/hXHUwMDAz0FSYrlx1MDAxMHRcck79SyU+MVLIXHUwMDAyXHUwMDFiaFoneFalcG0qY1x1MDAxOddCc45cdTAwMTNcdTAwMDIok5aQM9d0Nr/Rw1kxxTNgITCnjGHTMWV+2z6661x1MDAxMCyws0VhXHUwMDFhdFDoiMf8ffY1d4Yyr63b+9sprzzcnFxcraaDPp1gXHUwMDE2UVx1MDAwN1x1MDAwNSd8uXRcdTAwMTChQlx1MDAwM3tcblwizXlcdTAwMDXly+dbIITcRyrbkEGcmpGKXFzM+sBcdTAwMGLFltapf23J31dcdTAwMDP54s48llx1MDAxMVx1MDAxN5MkcrRNd0tcIsKlXHUwMDAyS4Oj+5re+Em1yVWMXHUwMDA0SlwiqVMqiYiCZS+0XHUwMDBm27wkXCLMzPn4XHIkUYhW2Kok8s/do5JcbkrRXG7x24uHk45g/LF0cnhdu+7tiZtKJ+Wdy5Umllwi1KdRKlx1MDAwNp1hKZBLicZvXHUwMDE5OOpIXCLqnekjuFx1MDAwNVx1MDAxZbxnr1x1MDAxMFieXHUwMDEyXHUwMDAy9Nx8j39cdTAwMDVcdTAwMWS0+3Cc0WftycHepNFjbDyVvcbriiUhIGVj1kFcdTAwMGJhXHUwMDEzsJVIMSa5kTjUQ9hQXHUwMDBiLXX5SOKQXHUwMDBiVsOIJoLBx1x1MDAxOU1OMPNcXFxy44tcdTAwMWLIvZxG8/mlVFx1MDAxYV08lm5cdTAwMGUy+/qmmO10T7/4MZdcdTAwMDduzMOJmJhkXGb3zdYxrFxyhoGi6P1cZry3f6qtJMhGS2OTJVx1MDAwNlx1MDAxN1x1MDAxN1x1MDAxZe58XHUwMDFkc1x1MDAwMqVBzUmeXFy+jkmiXHUwMDA2USBAXHUwMDEymNC8w5dcdTAwMGaQMcrMQlLOu7OFU65PXHUwMDFiq0/JmPPcfqGckJBcdCF4t5BxLSVhKSN8j6KpXHUwMDE0YjaEMTJGg7tBpFx1MDAxMqOYMGxxXCJcdTAwMTBcdTAwMDPTXHUwMDA3KOFuKaNcdTAwMDBAJnUsrOfImkl1QJxcYs9cYksxpD2cXHKMwNn07L6qhKZcYtH59/pcbmqmNURHZ3Ymc/j4Mrnmd8Nh0cbN1bptUK1cdTAwMWTD62LO2EFgl8Hz5Fx1MDAxY8GdXHUwMDExoHKX5IOwhFx1MDAwNGc8QD14dyVYWcxcdTAwMDBfU1x1MDAxMEOwUVx1MDAxMXhcdTAwMTdcXMm5XHUwMDEx33FcdTAwMWNIcZNcdTAwMDAr/Fez31x1MDAxY5Iroev717nSWa1yULo5XHUwMDFl1b62lvHHjXnMXHUwMDExXHUwMDEzk57xN5WCa0KVoHN2XHLtUO25/1NuKiW3uFx0+Vx1MDAxYu9JYu3K2qGcWlhyzXCSYVx1MDAxOWRcdFx1MDAwML5imFx1MDAxYYh6lkx6XHUwMDFjVVx0hSRGcXh8n1x1MDAxNjSft1efXHUwMDEyNCf5pORMXGLLu+XMwkKSXHUwMDE1MzhgXHUwMDE2XHUwMDE1ZkiAXHUwMDE5XSFwXHUwMDFh3IMqnVx1MDAxMKVcdTAwMWNcdTAwMTkvXHUwMDBl/Spqdp9VKVxy11xcXG6OJeOUOmY0x5umXHUwMDAzbCR9SrDAKCMuTFx1MDAwNFtjqT1bXHUwMDEywIuGc1OlZ962RI9kbmnn7mB0X7iuVp6PVeNw9Fx1MDAxNFe2TtKnVMFNknZcdTAwMTZjOdJcZoqGTaRcdTAwMTEwXHUwMDFiXHUwMDE3XHUwMDFlKTDKYsvBnJiOqeyaOjvlp52LysHIvrhcdTAwMWOdkLunsddSkcVgLyFcdTAwMDVrVcjsbLK8UGxEwm8+WmdcdTAwMTlys2fdYItLXGL5mllcbm5cdTAwMTQ4jdFcdTAwMTN2glx1MDAxMZVOI8u4XHUwMDA0K0pcdTAwMTCXkpiQiqtxXHUwMDA0+JFcdTAwMTaSsN1USP+5tUo8QNtSrCUlQsxcdTAwMDazzb9/UCFcdTAwMTZHiGGOyTZcdTAwMWLMXHUwMDEyTZ3p7UlcdTAwMGKhZnuSkFx1MDAxMFxuXHUwMDExXGJuIbSwkKSFkK+rwkC7c4x09POp4DZg6YQo0sr0XHUwMDEzXHUwMDE3Wlx1MDAxMUaIXHUwMDE2rlx1MDAxM2SqmIWZXHUwMDEwiUZ1KFx1MDAwMXdcYjFwi5SipvLNS1xyoaWc13m2jnH3wXKnUFx1MDAwN9Fm/qg+uJyI7m71Wl/yRmm8d1x1MDAxZYtcdTAwMGUyxzZcXMsks5aDR3Iu6iDj80qlXHUwMDE5OFx1MDAwZVJRijxqpZSVTKpOZLlcdTAwMDZbSMJcdTAwMGXSsF00Z5J4XHUwMDFjvJmjnY1013fbJJdcdTAwMDZa+OVYXHUwMDA1kC/YzCPjxFlMXHUwMDFhyL9cdTAwMTRcdTAwMWRzXHKuXHUwMDE1JSu4msF4SqWJ/aq16MT048fzrLv/VqKvNd44WFx1MDAxYmyzXHUwMDEyXTNfhFxuqU1RXHUwMDAzjZ63XHUwMDFjPKg9lVx1MDAwMFx1MDAwNXfPXCJcZtDBvFx1MDAwYtFcdFx1MDAwMd7gnIc1211cdTAwMDehXHUwMDFhzK4yRlx1MDAxOUiUYaE9mlJioUwyn/BcdTAwMWFqzCRj4E7xNEqgbGHU2ZPNZuahqK7zu6+DfodE67lcdTAwMWImgVx1MDAxMGZgV8WnJoAlULhFwGEg3Ey2R2ZQvGdcdTAwMWWNxqs3uokkglbRatRkXHUwMDFmSYGYXHUwMDAw9nHMkN1xRIJcdTAwMTRdODNISlx1MDAwNm0tXHUwMDBl5Fx1MDAwYjjzcEItJlx1MDAxNeTg0aXsXHUwMDAxxFx1MDAxNVx1MDAxNzi6jVxyRlQ6bSy4mUxLJYmciaD5u7y3PONmpFx1MDAxYSMgU1xiwcy9sG225FFcZjFA9dZcdTAwMDJB5thcdTAwMTa+ssPof2VcdTAwMWRcdTAwMTSiXHUwMDBmtqmDMEa+5Vx1MDAwNJJJpVx1MDAwNMfRx1x1MDAwZVx1MDAwNPdYTyVITVx1MDAwMZdcdTAwMGKXjidiPfhyrCUwXG4rXHUwMDA109xZxJFcdTAwMWVdc3lcXCmw2tn562Sa3duv5navy1dXsehcdTAwMWFGJFxiXGJGndt+W4VYpqKPgitcYmrAxEKlx7GRa1RcdTAwMDXBn1Mwq0gtk2xcdTAwMDSfXHT+qZRcdTAwMTKL5ZaCwlwimlx1MDAxMmVcdTAwMDbbKFx1MDAwZYtfvtdfSaxkXHUwMDFjeJn9mzh+ZS1Z4j9IzaRcdTAwMDAgMyYpssVcdTAwMGJcdTAwMDZEKi2e1qZAylx1MDAxOVN+M3tcIlx1MDAxObMnsOVdhbFs+jiWsMHp9lx1MDAwNlx1MDAxY86sXHUwMDEw4nRzNePv01x1MDAwNFx1MDAxM1x1MDAxMlx1MDAxZSF87DPacEPSQ/iXjmtzMsOip1x1MDAxNlx1MDAwN89gSSVcbpd1XHUwMDA3IclcdTAwMDCQU8C7MGn+iDOQdMtIxFJcdTAwMTlPhDNT0Fxi63Do/o/R50JcdTAwMGJwnlFcdTAwMWFFyVGdXjdbXHUwMDBmu6Xj8qXIkfr91TXKxCRKlFDqc+204lx1MDAxNyVw81x1MDAxOFx1MDAwMTPNzOxcdTAwMWXw1Fx1MDAxM1Ml0evAsYXg+sCDKsZBfXjVq1x1MDAxM4tg0zyfMyxMXHUwMDE3ffG1ZYlcdTAwMWaczGNcdTAwMTlIMUmWgJaAcFx1MDAwM6TiK1SUXHUwMDA2wyWVxjJcclx1MDAxZFx1MDAwMTmzvDuLLfdcdTAwMDHE6i3cXHUwMDEyRy+NNVx1MDAxYVx1MDAwMWrGPtWzNLWNXHUwMDAwQ2g+XHKNXHUwMDAwMfKf44ipplQ701x1MDAxMEOxXHUwMDFhPFx1MDAwNC6VWMXOXHUwMDFmeS9lTCigwpXFXGI1kVWEqFx1MDAwNlwi8pA2xHRqwCZcdTAwMTlcdTAwMGXsg3RUx8xcdTAwMGK+XHUwMDE13Fx1MDAxMqVxXGaHvrFLm+5T/aJ/XFyalC+PRabS7txk2es0rlRcdTAwMWG4Jjol0oZwhLCm5myCmtUtqVxiaXFcdTAwMGVcdTAwMGVcdTAwMDNcdTAwMTKm5Vx1MDAxOdJELV3XSMrGu2Tbc0lcYlx0XHUwMDAxqlx1MDAwNjYvXHUwMDEyXHUwMDE0c608pFxy6C2FtVx1MDAwMMo3XHUwMDE0zPWy3vpS0sZcdTAwMWZQ5rFcZqWYxI1/JTg3SeQrXHUwMDE1glx1MDAwN1x1MDAwMyaVXHUwMDA2M1xyheDMTORcdTAwMTDec2+X4zRcZkSYisdcdTAwMTlcXKNcdTAwMDKcilxyXHUwMDFlXHUwMDBmJVlcdTAwMDFcdTAwMWVC8nFWgH97XHUwMDA3//fqcFiewFx1MDAxNf2wlXBcdTAwMGLbXHLX0K/vT237edd/XHUwMDFifXvHuoGVPeO/P7/9+Vx1MDAxZl+hOVAifQ== - Graph DB(neo4j)3rd party containeror systeminfrahub open-sourcecontainer or systemLegendinfrahub-bundledcontainer or systeminfrahub-sdkGraphQLGraphQLRESTinfrahubctlGraphQLRESTbrowser (frontend)infrahubAPI ServerGraphQLRESTinfrahubGit AgentGraphQLRPCmessage bus(rabbit-mq)cache(redis)repository store (NFS or local)git repoGQL (Cypher)AMQAMQREDISREDISGITNFSobject store (s3 bucket)https \ No newline at end of file + infrahub-sdkinfrahubctlbrowser(frontend)InfrahubAPI ServerInfrahubAPI ServerGraph DB(neo4j)Message Bus(rabbit mq)Cache(redis)Repository Store(NFS or local)Git RepoLegendinfrahub containeror systeminfrahub bundledcontainer or system3rd party containeror systemTask Manager(prefect)GraphQLRESTGraphQLRESTGraphQLRESTTask WorkerTask WorkerObject Store(S3 bucket)httpsGQL(cypher)AMQAMQREDISNFSgithttpshttpsGraphQLGQL(cypher)REDIS \ No newline at end of file diff --git a/docs/docs/media/high_level_architecture_ha.excalidraw.svg b/docs/docs/media/high_level_architecture_ha.excalidraw.svg index 7894d8d129..c36c5f73c7 100644 --- a/docs/docs/media/high_level_architecture_ha.excalidraw.svg +++ b/docs/docs/media/high_level_architecture_ha.excalidraw.svg @@ -1,6 +1,6 @@ - + - + eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nO1daXPiWLL9Xr+CqPkyb6KtufvSXHUwMDExL17gXHUwMDA1XHUwMDFiylx1MDAxYsZ4ez3hXHUwMDEwIGPMWiAveKL/+8uLXUZcYm1cdTAwMTjJluc1XHUwMDExXW0jLF2ke/KczJs389/fXG6F7+505Hz/vfDdeWravU5rbD9+/828/+CMJ53hXHUwMDAwXHUwMDBlkdnvk+H9uDn75K3rjia///OffXvcddxRz2461kNncm/3Ju59qzO0msP+Pzuu05/8j/n30O47/z1cdTAwMWH2W+7Yml9kw2l13OH45VpOz+k7XHUwMDAzd1x1MDAwMmf/X/i9UPj37F/P6MZO07VcdTAwMDftnjP7g9mh+Vx1MDAwMIXQ/ndcdTAwMGaHg9lgMdeCXHUwMDEzroh6+0Rnslxy13OdXHUwMDE2XHUwMDFjvoExO/Mj5q3vVb1Z6z6qR7zvVO66lb3K8fHW8fyyN51er+ZOe7NhTYbwbebHJu542HXOOy339td987xcdTAwMWb2V+Phfft24EzM18dv71x1MDAwZUd2s+NOzXtcYr29+3JcdTAwMGZ+L8zfeYLfOOJcdTAwMTZcdTAwMTdaa8mUxEyzt6Pm7yUhXHUwMDE2XHUwMDExWEklXHUwMDA1wURj37i2hj14XHUwMDEwMK6/kVx1MDAxYu0wNlx1MDAxZlnDbnbbMLxB6+0z7thcdTAwMWVMRvZcdTAwMThcdTAwMWXX/HOPv74xQlx1MDAxNsJcXFx1MDAxMP526NbptG9dM1xuaWGtXHRcZmN+cOLMnlx1MDAwMtZMXHUwMDExTimbj8xcXHVUbs1mxL/m935cZnOpbP5kcN/reW/goPV6XHUwMDAzf82c+dyhr+/8Of9a5vM7/jnnnXee6bD9XHUwMDEznV84XHUwMDE3am+LXHUwMDFk3DlOpVtcdTAwMTk0dt6++sIkdZ0n9/vbgT9ff5pcdTAwMGb/ftSyX+ZcdTAwMWSWRCtEkZBcdTAwMTjPXHUwMDFmZa8z6Pq/W2/Y7M6n6jfPXHUwMDE3WcLI7PJB8GAkXHUwMDE0XHUwMDFlXGJJgrRALDE8ou9HPuFBsKW0VkxIiiimfFx1MDAxMVx1MDAxZYxaVGCmXHUwMDE0pjJTeGAhLcq5JoJjXG6PXqllmNAldFCiXGKMXG7L9cGxcGBcdFx1MDAwNWlO1Pmohlx1MDAwM7fWeTZcdTAwMGbFY4PNuyW73+mZXHUwMDA3MP/Gs1x1MDAxOVxmd7EzgC9ze9/YmLS631x1MDAxN45cdTAwMTZ7nbaZ09+bMGpnvDDd3Vx1MDAwZZDL21x1MDAwN/qdVstLXHUwMDE3Tbik3Vx1MDAxOTjjclx1MDAxMis/XHUwMDFjd9qdgd07jVx1MDAxY1x1MDAxMdxcdTAwMDdn79eTw5bH7DXsiWOOmq8tXCJRXHUwMDFiyWyUIP+7b9DVkitcZnMpObPVa1x1MDAwZk1Sqro7aqe/N21d907vzq/yXHJdKZAltNKISmqYzVx1MDAwN928M1x1MDAxYiBXSlwiKc0hsZ2j+y19M709PCnT7sb29Km8sbX3pYhccox5XHUwMDE4OsBegsXnJDmvRd+OfIJDKWAuJZmmWFx1MDAxMeSh+Vx1MDAwZuY1xiwhuaZUUVx0WFx1MDAxNWRcdTAwMTkkXHUwMDAxvMYoUlxiXHUwMDExvT42vlx1MDAxYa813V5GtFx1MDAxNmPiw2htYUDZs1x1MDAxYZX+d99wy0CRcqnmzyRcdTAwMGW3z1x1MDAxYrvDUem6eNK+aT5XLlx1MDAwNruHl/3TfONcdTAwMTZcdTAwMDOTWIrAt1x1MDAwNFFcdTAwMWHEashSiYB7g1RcdTAwMTOhbFjN81xifrlpXGJTXGZcdTAwMWFa5tFNc4dFbqNcdTAwMTJcdTAwMWJvkla1dVQtNUr3X4zNWLjWQ0QxrPBcbrCIvlx1MDAxZjmFXHUwMDA18FxiY1x1MDAxYbwjhiVMfuGDXHUwMDA1/yBYYEYsmOWaXHUwMDExTFWInyaX4SGAh1x1MDAwNVxi1lx1MDAxNMTeVyG0xnj4OHHGf1xm/n4zhk86g9Z/ZURsMWbeT2yvXHUwMDAzK1x1MDAwNI0rXHUwMDExv1x0/H5+wyiU37BkXHUwMDEyS43IXHUwMDFjVnFIRqRdJ/bBiXN3Lpg4+zG5mFx1MDAxNC/yjWQpKXhtXHUwMDE4aUzocjxcdTAwMTJjolx1MDAxNpD8KV5bIL8pMMKK55HfpFvfLT3sT2rnilx1MDAxNKdcdTAwMGZXXHUwMDE3zdJjK1x1MDAxMb/9XHUwMDE2ddpGhYxcdTAwMWX7N6UnfkN6/auzi+rIuVx1MDAwZT6tPVx1MDAwNkwlPa99XFzuXGZv29v2ZbHE+P1cdTAwMGXafH5cdTAwMWWncN4zZ1eSLqm2yr1ip4Muats/yrspnNfFzWG3tXfOz1x1MDAxZvuXe5P2ydWBcFI4b3e3wpu32+ftXHUwMDFhc1x1MDAxYefD/dHWdXWQwnmPXHUwMDFizz+bO6OjXHJd+3Hh7Damj4O7p2TnjdM7jChccu7knMUy0js6PCpccihcdTAwMTTgJMxcckOcjYxGRz5tpFaWXHUwMDAw75dwzalmYn6/X2wkpVx1MDAxZmMjMUdcdTAwMTbWWHPOhYD7XHUwMDFlXHUwMDE44VqylcQ8XHUwMDFlzjxcdTAwMTGvXHUwMDBm1jpw+1xikUzxXHUwMDE1pul6Wqf86iv/MShcdTAwMWWXXHUwMDBiNWf84NUzqYqdXHUwMDE4yveLneiRZS53PM/AXHUwMDBmZKFcdTAwMTVcdTAwMTdK6eRqZ3+wOVXlPdIpsf59s7RxWHveZzlHMsNcdTAwMTbFYauvoIKUxTgnNFx1MDAwNST/7Wb2WlfpMK5hNHmUOc3ycaWFbp3H49MnelWcuup5i60vc25cdTAwMGZ3xfVOqb6t7NHBPduzbyi9T4GGL/vV09a+W7pcdTAwMWJel89u7urH4rBRToeGwZdcdTAwMDNjrIVcdTAwMTfHWdCwXGaPOlxieHGuPNG6OPRGP758oldcYotQUP6EoCBcdTAwMWVGNDX0ZsHDmFx1MDAxMVxyk0V82uqwIWJKOVtlon5cdTAwMTlcIo5ho5xcdTAwMTExVeF5UFx1MDAxOFx1MDAwMUVotkLcQTuqxIvTw3pLNfd+3G1cZitFmfM8KJiHXHUwMDE2XHUwMDE1jFx1MDAxM/WyWkzmqJidgFx1MDAwMUuC2iaKI0yiwIy1xE3yUYFcdTAwMDdmoupm+SyHhNxcdTAwMWQ1XHUwMDFhp6Wj2+pccrs+3XisntR79f76hJyVv52tX0xcdTAwMDSjmfvFVEZEXHUwMDBmKYZpy1Zg5Ojnl09cdTAwMTRcdTAwMGJlcY4ol8poak9y5yuI+ceA2Ljg8MA1qCBQ9ojJZSxcdTAwMDdcdTAwMTAy0oRcbupcdTAwMTVNXHUwMDFmvlxuXHUwMDAw5lx1MDAwNP7VK0zU9VxieXdsj25cdTAwMGLbm39cZv4+cIbsLqs1gFx1MDAxOEbys3HEsDKnYoZDo1tcdTAwMDRxXHUwMDA0jlwiPKLEID4jYzRSx1x1MDAwZltXt5ubx493k2Fn6uRcdTAwMWLEIJktxTSVJNApzilcdTAwMTNj0ElIKExSiGulTsXP8myjuoNcdTAwMWV2j1Fpa1hUg41DJ8TZXFyFine2x/VJ86zs1Cfy4XL/dOxonlx1MDAwNlx1MDAxNWdcdTAwMTWqz5bikeYq86V+RsJcdTAwMTPXwKNcdTAwMDKVzpPHvqOnRT6Ng2BcdTAwMTZcdTAwMTNcdTAwMDB8xLlcdTAwMTZKLsn0j2J4KSypzfZcdTAwMDPFwVugyXxu+CvNXHQn7NOC34biJUWelPCsKf5cdTAwMDCGa7edwub9XHUwMDA06HRsN1x1MDAxYVx1MDAxZLfQ/5lcdTAwMTXTx1x1MDAxMJ6f6eNHlz3h03DfW8CTXHUwMDEyMJ/nIZI4TLfaj0/V6vj0vnq5u7GtLyvFbu8o35iWXHUwMDEyWZQyglx1MDAwNVx0yNTOLeErzTT8l8tY+NFmvUeLdKt/3K6gJ9G1We9mfb7PaslcdTAwMWZcdTAwMWapMVx1MDAxZVx1MDAxZtU6+vqhL6p28a7fOPtaS9JMhK9Ja4S0pFx1MDAxYa1cdTAwMTBcZo98frlcdTAwMDSxXCLCQkwgXHUwMDBlcOVcdTAwMTJ5YPGxxKyUxVxyL2t4XCIhXHTlQa63lEpcdTAwMDDyPzGj/KN5ectu3jqG85xWZ5JcdTAwMTVcdTAwMWLHsJGfjcPGlDlcdTAwMDdcdTAwMTNcdTAwMTG+XHUwMDEyzTFhkq+wzbE9XCLnQje3L3aqx436bvXyYNzIN3gxXHUwMDAxeFJEQzZLYaq1xaWKR+/a+bNaWphcdTAwMTPKqFxmXCJhYkmmPa8lTsZCUmryYnJIyldcdTAwMDdbj+KWXHUwMDE2T9Dz5Ohhy1Vi4zHEq12FlO+extuHW/c1fHHYuZ406Y8tXHUwMDFiP6ZFnlJS7DHlWcWtPVbLjz6lpUBIrODVRt/nnOJcdTAwMGZrXHUwMDBiiFx1MDAxM1x1MDAxM8ph+oNz62NPnJA918afUHAhrYG/NVVS4YDINV+WwlpcdTAwMDN1mb1kX54+0cK7XHUwMDEx9HnijIZcdTAwMTNTh2JaqMG/hrVcdTAwMGVLtcJwXFyAq9i9rFxiNYZcXPyEmnyUiSiWsTUoXHUwMDE2hy5OXHUwMDExXHUwMDEwjsTIteQgd1x1MDAxYer4iFx1MDAxZnVP67tj5/RsfEiuXHUwMDFmJjlcdTAwMDc5YMrShDNcdTAwMTHm51x1MDAxMuA+xkzaW2S+yPokXHUwMDBiIFx1MDAxN0xcdTAwMTmSReY19468O5M5WF7jXGb7PvJGtYhcdTAwMGLJhMpcdTAwMTfVXHUwMDA2Uo1n3jS3zoQt8d5cdTAwMGW3XHUwMDFmnjFr9Fx1MDAwZW4rXHUwMDE3Sam2+uO0MtmW7fJcdTAwMGZyRn7WXHUwMDFlXHUwMDBm9+zbkFx1MDAxNe2Vc7ZWX3l7XHUwMDE31Vx1MDAxMlx1MDAxMr5EzFx1MDAwNUdKXHUwMDExglx1MDAxM6Mw+G7mXHUwMDFjhVxcWlx1MDAxYUxcclx1MDAxMJwgXHUwMDAxKCQma+sjqJZcdTAwMTBLUa5cdTAwMDOV7vKeZ1xmPrVcdTAwMDLLoT+VYz94dbjjXHUwMDE2XGaDZcSlMVx1MDAxY7K0KLw0mjS2O4dcdTAwMDaUZERlKiSVYCB3XHUwMDEyXHUwMDAz9fG4i1C7vclHh0iMRFx1MDAwNT1cdTAwMTSn23lcdTAwMDcq8CFX+i0la1x1MDAxMaiaIYtTSYTRqZF7Olx1MDAxM0SUQpOjXHUwMDE1XHUwMDA1Oay0XG72R1x1MDAwM1BcbiBlgtI0VoHfvZOTMPhhXHUwMDE1p209lO47bWfQXG7BaM+5cSNcdTAwMTDqXHUwMDBlR2HwXFxcdTAwMTivXHUwMDFmi/5rZl54gEdszISZqlx1MDAxMVx1MDAxMXyukeLg2NdcdTAwMWKqPlx1MDAxMEd1t1eudPZcdTAwMWXKXHUwMDBm9V4p53CkVJuiIGYnP1x1MDAwZeBNcF4tXHJmSXKRcc1cdTAwMTDA5CywXHUwMDFiXHUwMDAwSIYt8Fx1MDAwMkGUXHUwMDA2VIozK+PgXaPPWq+JoFWwY1JkvkQhPM/YP4VcdTAwMTlnXHUwMDE0XHUwMDE00VxuS1x1MDAxNJPJud5pldiYXHUwMDFlVM6HXHUwMDA3WzZcdTAwMWWd5jzHXHUwMDE3viW2uIFrcJTTTGHgXHUwMDFhgl5cdTAwMWSwtaZwKKVgLC2greBlRrZcdTAwMWNbQZqBXHUwMDFh9Vx1MDAxNqr8ooyCxcK7XHSK3Vx1MDAxNN5cdTAwMTjhj8FwXFyYTCeu0/9YollcdTAwMWVKIWAkieiH8jXoh4di1yRcdTAwMDUqaSpOJMbuw8+e3Xzoq/q2vffjZHDQdIpcdTAwMDOac+zG0Y/QXHUwMDE2k0hcdFx1MDAxYbPZZu31xfexXHUwMDBmkoJcdTAwMWJcdTAwMTSlgOIvyj4swp9cdTAwMTFUIeGt51x1MDAxNbvDpOFcdTAwMTbrN/37Ruu5tL+7X8X79UrO61x1MDAxMb6wj1x1MDAxMG+lLVx1MDAwMmYwXHUwMDE1ZkM3jVx1MDAwZf+t489gwi2iXHUwMDE3ZmhcdTAwMDT3gGlcdTAwMDE/kyH86dxDVpieKXFPXHUwMDAzhthzWn9cZlwiLf+HctDrkD6bi8IjXHUwMDEzXHUwMDAyppbJ255fM7Z24tHP8Uh0W5XS087x/XNNqdPzx5xcdTAwMDM5hoowXHUwMDFjllxca/LqXHSFXHUwMDAyed1cYuL7qIhcdTAwMDBcdTAwMTVRlcpa3ddkXCKJQ1OoiYJHylx1MDAxMU4upfCTvP+xd7z3vHV4u3nQwz/Pi+e3OZ+/jFx1MDAxM0sz9lZ1IGD+ipTWoVwiiFxir+JcdTAwMDVcdTAwMTFcdTAwMDTDxFgotv6s/SpMRMetXHUwMDAyQN+d5sBcclxuXHUwMDE4y6dwXHUwMDBmkcL/7i/szlx1MDAxNm3AgCSPwk3tjeojqriV8nN9Z6/74JZtT6WpXFxiVytpKaxcdTAwMTF/Lf+5WOcwt6nSerZjg6RcdTAwMTHDSD0ta/e8Rbp6ayo0O8BseHZTbV/erZ+WNXV7otRrOztPfFtcdTAwMTUrladccvTwnEKu9Fx1MDAxYeXRXn9KM4fmXVx1MDAwNEw8W/r8XHUwMDA0TDnlXHUwMDE4cJxcdTAwMTjE0Y8vnyDW1FwiJi9cdTAwMWR8PkyFon5cdTAwMTB/1Fx1MDAxZSalLay5hrHwsFx1MDAwMvVcdTAwMDHxSES1XHUwMDE0WP0/SpU+tSfdwoE9sNuzgqWjsXNcdTAwMDMklVWKV1xmLfmpOXZwaSRPv5iSXHUwMDAwLLNwLHNcbpJNKpU8c3rzrnw57j7b9Wl/VG/v7EyaN1x1MDAxNznfj6iYXFzoMrG4WVlhZGHK3lxcwfBcdTAwMTVcdTAwMDWzaszV+7Q0XFxDhJIxsUhoTFx1MDAxMmljZ9LYjLjMyiSelaNcblhx+ErvSjSZuCBTNzuDVmfQ9v+JM2iFXHUwMDFj6dlcdTAwMTN3a9jvd1xcXHUwMDE4xvGwM3D9n5idt2hgcOvYS/dcdTAwMDLO7D3mx8vInHFRicx/Ksyn1OyXt5//9Vvwp5dcdTAwMWW3eXlcdTAwMWX0/Fx1MDAwNN+8/19cdTAwMTnb3iyGpTqbwqRcdTAwMDdKkVx1MDAxY9zRhdTyXHRuji0q4JZcdTAwMDZWXCLQWltcdTAwMTLDVEXZglx1MDAxYqyHXFyoKzBcdTAwMDc3t1x1MDAxOJeIelx1MDAwZv5cdTAwMDK3IWlNtcokzLMmuJVJ9k1cdTAwMDHcnoG99lx1MDAwMXxh0MHl48O089RsjYXqkJqzdf/oeEvnXHUwMDAy3zfvzSg3wItcdTAwMDFXisKAQFBwXHUwMDAyukJ5Pta2R2bUwlRXW6irNpuvT4uWYumWLFibsKFGl1x081xmXHUwMDE1UFx1MDAwZjPQrF2D90dcYubzXlxu85GCrvRJuUQj/VrWz49cdTAwMDfz8iBhRetcdTAwMTe9NTuixCFcdTAwMTeMYSRR8q3ZXFxeXHUwMDFkXHUwMDFl1TZKw8PKztXBY+usuSdEvi0gxlx1MDAxNFuKz1x1MDAwM1x1MDAwZb5YIZlcdTAwMDVcdTAwMWNgWsYtWn1SXHUwMDE3LcK5kIin4aykXHUwMDFleGifdM7K/ZOr061Jn4+4e8FcdTAwMWL31fVcdTAwMDNcdTAwMGZr7Fx1MDAwN4s871x1MDAxYcnvkeddI1DyXHUwMDBi4GFU81H711j47lGCXHUwMDA0V1x1MDAxMmmUfG07el7k1EyAXHUwMDFihExcdTAwMWGkXHUwMDEwgDbvgvGLmSBcdTAwMDSIXHUwMDE28Elfuu1lZiawMGtcdTAwMTdYU1xmXHUwMDE3XHUwMDEymlx1MDAwN+xsWU7aZeCjMpJKdPL9XHUwMDExXHJcdTAwMGVcdTAwMTbu4yqSz4JcdTAwMDbnw3E3s/qnMWRcdTAwMTdcdTAwMTjF8Fx1MDAwZijzrF7Gw5NSXGIlXHUwMDEySbZcdTAwMDK/O/UywfusPlC1wcP0qbbf3f6Z81ZcdTAwMDJcdTAwMThpboFipMH9xDCW3FwiQO5cYsWsZWdVgTyq9zNTmlx1MDAxOE84hXqJqZN7WW2Ojlqtn/bJ4/VG/X483Lg8QOuTe1arXG5ZVXLLYVx1MDAxM5PI82ZcXImGU555VXZcdTAwMTaeKlxubqxcdTAwMDJapDT58kr0PM6nTcPYdP6VmFx1MDAwN4tcdTAwMTGshUVNujuKWV/5XHUwMDE0MYIllYzzT91B9KJGPq4se/ZqJIaa86FGvFsrl9xcYixcdTAwMTShKHnhV3l583h22a6WTtjZ4ejo+KTemOR8XTSuXHUwMDA2O6HKYkrENzb65Co0YOmVMKs+OdQl+0/ocPhz46B/N7VcdTAwMGY2R9ulPXu8sb4uybabXHRZKJ6WVVOx8JbgQFx1MDAxOdJEXZMvdkTf5nyiT5rewUhSoYnpXG5CfHWXXHRnydqKrb8xXlx1MDAwMPpA9MNQXHUwMDE0XHUwMDE2Xlx1MDAwZiyqXHUwMDA2jaJqVpFcdTAwMTGtj7r391BddaZcdTAwMDbyZuJcdTAwMWE0R4074JO3yi41Wmjcw9kzy0yI4Vx1MDAxND+LJlx1MDAxOF5cdTAwMWFVZ0LXLzVcdTAwMGX37qlcIlxmUa6SXHUwMDEzarSJyyWkgUYtgshbs3B/M1XGLI1cdTAwMTIk+q6zfmnUNpr577M6MqAtl9GMKbNcdTAwMDRh2JRPZ9hb1PlcctxcdTAwMDJsMOXZ9DlJsJpcdTAwMTnFfWrn/plN91x1MDAxZrt3XHUwMDE3Xf6jZd9eV3ZS61x1MDAxZv5ug5JwqXSF9Uduan8oXGZDXCLwnKR3ifFl+ZFZXHUwMDE4S2+kppDaMmm05VlcdTAwMTim0EAgMFVcdTAwMThXXFxyvLxKqixfl6xEw/xKa6RcdTAwMWLhoDOvJbjNz/fN+/+V8zpDSyTwWW1JvMJcIkg0qHJpbbk0W1x1MDAxYVx1MDAxOKOcaFOF0u+8aMs8XHRcdTAwMTFcdTAwMTd1WMfYgkFcdTAwMTezuvhwXHUwMDAx8EBUgHJcIlx1MDAwMVx1MDAxOZ0wTTRl6NNcIlx1MDAwZVLD5bF3uFnvrbh13dEkI5VcdTAwMTQjXHUwMDE0/CrJN5REilxiR2+hXGJVRFwiotAmUrOETY+yjsNodCeuXFxiVFxubapUc1x1MDAxZayIXGLSlpoldWSoiFxiU5bUYbtwTWOJXHUwMDE3r2c5qUubPVtcdTAwMWGoLVcqKK7kXkOh/t2BfXpzvHu05/S7U3TRuF21tVxuxu/aeFx1MDAxNa6C5oKBXCKwflx1MDAxYYHvq1x1MDAxOfb2WvBcYlx1MDAxYlx1MDAwMSqZLuRcdTAwMWMt6pPgtsuJtc58MKZ2OnBcdTAwMDVcYj9cIoWUc0oteJK8fFx1MDAwYmL+wVx1MDAwNDe7+tqKJlxiNOblhUtKMoZFdJdXVFxiY1x1MDAxZlx1MDAxMtvI4Mmfa1x1MDAxYimEsKjWnGmsqCZiTlx1MDAxOS82UlJcdTAwMGKbTkNx25vXsZGCWFxuVFx1MDAwMddcdTAwMTi9QG/ZUFx1MDAwNm1cdTAwMTJFpmoxuPafJWTWslXvXHUwMDEyMrvV/T9cdTAwMDZ/b05Ht844q7BPXGbPL9VJXGZcdTAwMWVSXHUwMDFhe0PDhY1cdTAwMGWvj8NcdTAwMTBRnK+w5lx1MDAxOd1+LJeYVVx1MDAwNISLXHUwMDEyLHhcdTAwMWJcbiaEWVxcXG5KcZaRXHUwMDFlXHUwMDBlJlosRlx1MDAwMDy6RlArSNNgKTmjhGTTU37N0E6nfvPzabOyc93u1Xa7XHK7WFx1MDAxM007ndCOKW+JXHUwMDEx88Ij5dBOsCbxiFx1MDAwNm9cdTAwMTY8uICKII6ZZFx1MDAxNFx1MDAwYr6sgTC3YGItXHUwMDE0wiykXHUwMDE23olu2rU4VLh5Qlx1MDAxM2lcdTAwMDZcdTAwMDO8r2nAUIlcdTAwMDU3mIlVI1FfSlx1MDAxMFx1MDAwNaHNvH7hLC0xXHUwMDE0XHUwMDFlQlx1MDAwN29cdTAwMDSeXHUwMDAyX6FmYDSc8mlYKbNcdTAwMTBl1FRcdTAwMTdcdTAwMTBcdTAwMWMxf2Krklx1MDAxNmVcdTAwMTJmXCKJTmxdx7CaZXFTUFx1MDAwNiu4hKn4smxdl6M64OqDNlafV7rJXHUwMDA0dVx1MDAxNFx1MDAxN3rd5bDkWqh4UM1IXHUwMDAxxShcdTAwMDK/XHUwMDAyWlx1MDAxOEimXHUwMDAxXHUwMDFkU284VPiYMtGYrlx1MDAxMHSNzlx1MDAwMswlPjFSyFx1MDAwMlx1MDAxYohcdTAwMTVcdTAwMGLeoMK1XHUwMDA1qodxLTTnOCOAmk3AcuabvoTbXHUwMDAz5Fx1MDAwZsVcdTAwMTZYXGLMKWNma6unhfOv4Fx1MDAwZWgggb3VXHUwMDAw8qCDYvsplG6KzzvHaOP59urmasrrd5eH56vpoHfnmiXUQdG5Xz5cdTAwMWREqNDAnoJIs15B+fIqXHUwMDE3XGKh2Vx1MDAwMtgnyyBOTf9cdTAwMDIuZiXXPHWR3sapf03J/1xcXHJcdTAwMTSKO/NaRlxcSpJIhOdcdJmdiWBpcHJfM1x1MDAxOD+5NrmKkUhJJHVOJVx1MDAxMVEwbFx1MDAxMHSfKYlcYjMlNf9cdTAwMDMkUYxW+FRJXHUwMDE0XHUwMDFhXHUwMDBiUlJITlx1MDAxNV4hflx1MDAxYtnyNp/41MRShIaUJMEgMyylWHx5yrVcdTAwMTaiwU9HvspcdTAwMDSeUFx1MDAxMLfMzsyF9Nq3XHK7XG70gPQsXHUwMDA1f1x1MDAwNVx1MDAxNTRC9c12u3pxdzvaXHUwMDFkjXZ7ra22fbPaXHUwMDEyl5Las/Sa6lx1MDAxMtcsaFx1MDAwMpZcdTAwMTIpxmD6g2KgXHUwMDAxsoZcdTAwMWHx41x1MDAwYu9kscZcdTAwMDWjYURcdTAwMTPB4HJGkVx1MDAxM8xcdTAwMDJH469s4Vx1MDAxZk5wa9mvLWhcdTAwMDJgY15ewKQkYnh4O1x1MDAxM1BUXGY0ZfJk5+DZn2tcdTAwMWKpTZ9LbFx1MDAxMsXg3sLLn6xj1p80SDnJs0vWMfnUoFxihGnlhMFcIs89wlxiXHKjwbIrXHUwMDEwmfNcdTAwMGZ/wlx1MDAxMte7bdW7NMzJzna5lpGKiaH3pXaai0PJWMdEtZSXXFxgjFTyzcnRZSFyXHRSk51ocVwiXHUwMDEwXHUwMDAz01x1MDAwNzDhfiWjXHUwMDAwQSZxLK74yJopdUCcXGLPXGJLMaRcdTAwMDNcXFxyjMDVRDpAznCEXHUwMDE1OOc0hU1cdFx1MDAxZihn7i+d9s20sSNP7b37VvFC7Zamo1x1MDAxNetuYC+DpJyxg8Ayg+PJOYJHI0DlLulcdTAwMDdhgdD3VVx0WpRcdTAwMGbB9Vx0VlYzQNhcdTAwMTTUXHUwMDEwzFSE4fkryVx1MDAwMlxuSFx1MDAxMYub/FdcdTAwMTE+muC2vl9azIRcdTAwMDPHvOaQSUnQhFx1MDAxYktKKGhMukJiY/D8z7mtlNzis8aQQmKJtS9rh3JqYck1w1lGZZAlqFx1MDAwNvxjaiBcdTAwMWG4ezIo/9g0jVx1MDAwM+v+mWk77zdY79I0h6WsXHUwMDE0TVxmz/tcdTAwMTXNwkCy1TM4ouyzKa9cdTAwMDd2dIXAaXQ5qnxiXHUwMDE0bJDx41BwR1NcdTAwMTPIhHsuXHUwMDA1x5Jx6i1cdTAwMTWRbppcdTAwMGXQUUhBSWOVXHUwMDExXHUwMDE3JoKtsdSB1Vx04KAh3VxcXHSalynRJ1x1MDAxYle0e707vilf2PXHXHUwMDAz1dpcdTAwMWI/pJWtk/UqVXS9pMJiNEeankwwiTTSXHUwMDA05klAXG6MsthyOCelZarovslcdTAwMGLiiMFcXEJcbsaqkJnZZHmg2KhcdTAwMDS9el7RV5JCy5CbvetcdTAwMDdbWkoo1MxScKRcdTAwMDSTyVx1MDAxM3aiXHUwMDExlU8ja/q7XHUwMDEzQCuXkpioiq+IXHUwMDA0eJJcdTAwMTaSMN1UTCm6tbZ4gLil4KRTXCLES63mREKII8QwxySFXHUwMDE0gPcvUGnT2urDhFC742YkhGJcdTAwMDSCX1xiLVxmJGshXHUwMDE06qtoilx1MDAxNVx1MDAwN4U+/0BsXHUwMDEzm8iKYPmEKNJcbtxm4Fx1MDAwNkVcdTAwMTghWvhWkKliXHUwMDE2ZkJkXHUwMDFh16FcdTAwMDT8IVx1MDAwNG4hVYoqXHUwMDFjmLKM0FLO6zxbx/j7YLlzqINou7TfXHUwMDFjnrmit2lf6DPeqk62TlLRQYxoTFx0S3kr1oK4iO5+saiDjNMrlWbgOEhFKVxu2CylrGxSdVx1MDAxMss1mEJcdTAwMTJmkIbpojmTJGDpzSzurC7V3iGA/DbJp4FcdTAwMTb+OFVcdTAwMDFcdTAwMTRcbjbz2vDiLCVcclx1MDAxNL5cdTAwMTVcdTAwMWRzXHKuXHUwMDE1JSu4mtF4yqWJ/ap70Vx0eFx1MDAwYlx1MDAxNM+z7v7aib5WJ6FobfCZO9E1XHUwMDBiRaiQ2mxqoMnzlqN7ouVcdTAwMTKg4O5ZhFx1MDAwMTpY8EZ0QoA3OOdxdXfXQahcdTAwMDazq4xRXHUwMDA2XHUwMDEyZVjogPqUWCiTzLfYp/NXmTvJXHUwMDE4uFM8j1x1MDAxMqhYXHUwMDFld7dku71xV1FcdTAwMTelzefhoEuSld+Nk0BcYjOwq1wiy/Ylq2zcXCJcdTAwMDKmXHUwMDA3N03kkOnJXHUwMDE2mEmTWfOSVbRcdTAwMWE1+UdSICaAfdR8Rlx1MDAxNzyRIEVcdTAwMTdcdTAwMTZccrKSQZ9cdTAwMTZcdTAwMDdcblx1MDAwNZx5eaGWklxu8vCoP1x1MDAxMmSaYnCBk9vYaETl08aCm8m0VJLImVximp/ltfxcdTAwMTm3TJFPXHUwMDAyMoVcdTAwMTDM/Fx1MDAwM/vMkjyKIVx1MDAwNqj+tECQWbeFr+wx+l9ZXHUwMDA3xeiDXHUwMDBm0EGRdX+xdy3Gn7FMwFwiKiWTJ/pEN7/KJVAlXHUwMDEzXHUwMDE2oVhi4Fx1MDAwNiOGXHUwMDE2XHUwMDFiomrjXHUwMDFlwrFf4aDwPmufXFz3XHUwMDE3M7NcdTAwMDFcdTAwMWZjlMlW9jVcdTAwMGL/spF9Vruzm9fH3dueu3uhTzp1kkhcdTAwMTH9XHUwMDE2ddo1XG7/Rp43ultg5HljXHUwMDE1XHUwMDFjRVx1MDAxMiu0ioJ7XHUwMDE3+2JcdTAwMTLu41x1MDAxOFx1MDAwMz/bwJJcdTAwMWPX0Vx1MDAwZjCfuJbCouDOXHUwMDAxkZloL14kYI3lQpg3Q1xcUzFLtTX7+ZjGglx1MDAwNqTaLldcdTAwMTRWXHUwMDAymY2jLFx1MDAwNTS/v6AwY9LTvCzrgsL7Q7tV2LR7NsxS00x4r1hcdTAwMTiNh0/TrCrLxHCVn5hcdTAwMTNcZi+NgsJhgFaeXHS8hGfwsFxmbSRfW52Ud11Uee7im6fG6d7O+Lj887KXbzybLFxiSzNThY6FdVx1MDAwM+RcdTAwMWHr2ILCXHUwMDBl0CTF71x1MDAxM9RKWpxcdTAwMDXXzlsuXHRlZFx1MDAwM5fePfBcdTAwMWaPYcLgh4+rXGJVm05cXKdfXHUwMDE4wFxymPwxMHf2flx1MDAxNFx1MDAwMt+ec+NGgNdcdTAwMWSOwpC7MHo/TL0jKPhcdTAwMDeQRlx1MDAxOahwxlx1MDAxNf5356XbwK9WnLLkXHUwMDAw5ft77vP23c/aRrGit9qbXHUwMDBm52dHV/lcdTAwMDYoR9SSYt7Ny1x1MDAxZlU0XHUwMDFlr0hQ3nJcdTAwMWR8XHUwMDA2XHUwMDE1+F5OJFx1MDAwM0mAmKnt9tWBmbzLzT8+XHUwMDE2hf9YXHUwMDExczGda0JX2nS4yEWzLiirlH0+3T+/rPzcvrdcdTAwMGaGJ05p94hcdTAwMWNeXHUwMDFk0Hxjzlx1MDAxNIW0XHUwMDEwXHTP6kTcYiZrksb0zchcdTAwMWV0WFx1MDAwYilme87+Qt3XRlx1MDAxZOOhKURcdTAwMDSsvzItXHUwMDFmXHUwMDEyg67bKk32qzvqx8n24KraXHUwMDFh3Mk9fZRv0HGkLKA4LV4jRj6iY1xiRKJKsDMse8yBXHUwMDFlJrPk9r8g98Uh58mK9ENOY0ZW2r1w+nNydu5Q6bpcdTAwMDdyWpveXHUwMDFkn15VT/NccjlcZoCyiGJcdTAwMDArXHUwMDEyxHPMQFIqs5fns3lcdTAwMGXIXHUwMDE4XFxx6vlKf2Hu17zNJeZcInJlw7uyUdOTzZRcdTAwMDBKTnSR3WjziTrkaWj2smzpeSPVfUGesUQmqUtcdTAwMDHY99a4yk/ax9lBvcxcdTAwMWHHJ8/utLi1be9sXtTO59W0XHUwMDE2ptzKma9g2ryezCfWqTNcdTAwMDVcdTAwMGYpXHUwMDExMPkpjFxiyYBdNb6m3lx1MDAwNC/d10RcdFx1MDAxZatkopjN2HBNRbE0q3XLfZeERTQlikpcdTAwMTOIgcEvP+uvlMux4cHL7Hfi+ZNv3v+vKjRkqM7ASlx1MDAwYmQ6XHUwMDFiJ9/KXHUwMDFjjYhcXJo8rU1cdTAwMDE571x1MDAxMuyL3Vx1MDAxM9nYPYGt4DJVy7aPY1x0M5zKz0vLMGaIKr3uitBcbv1cdTAwMDXG9ui2up/R+k9cZiEvdVx1MDAxNvBcdTAwMGYm22ZJKlR6XHUwMDEwxJU2O92T79OJ7lifSyAua1x1MDAwZkKywSBnXHUwMDAx2mNB6LyKXHUwMDBmU7xMIIbyKD72m/SifXu3WT2onYlcdTAwMWTSvDm/QMlaK8eLXHUwMDBmpcxG2nyIXHUwMDBmxrQ02aZYwXQglHiyXHUwMDE4U5ZcdTAwMWbJ6+FiXHUwMDBiwZVm6+XgemkpXHUwMDAzq7AopqkkL0uXVMilQX0p/eHBjHlcdTAwMDFaUtJcdTAwMWZcdTAwMTH9j+BxYyGwSG72okGRS7OXh/5HnFnBbVSWl7hNgSmTW5pG4fD3l4TDINg+UJV8QNejXHUwMDE4ws5D1yOMRHhGisloZoqh5FjFR2qMx0e1jr5+6IuqXbzrN87yjVXs/chr6caMwiNUWYxQk0aOXHUwMDEw1UpcdTAwMDVtXHUwMDE4JqYsNTY7/6UpKcSWivxjcH2lUFx1MDAxYaewwy11XHUwMDAx03tonlx1MDAwZVx1MDAwZapu7exAbNQ73ctcInuepiRgNFx1MDAxMsizXHUwMDFk4VNcdTAwMDVcZrh7jCqFhFx1MDAwNFuO1LJUkFx1MDAxNueCwVx1MDAwN0x/XHUwMDE3pIlauq+J5EtwhdrAIVx1MDAxMZAvXFyIWctcdTAwMTZEsFxm2MlcZppKYS04oVKButLLmupLyZdwPJnXMpJSXHUwMDEyN+F1b00heVO+b4XCt9GAyaXBzEPhW5PtT1x1MDAxNrdcdTAwMTRGXHUwMDA0XeCJmG5eabh875c3XHUwMDFhnJmP21x1MDAwYpNlxdtcdTAwMTiST7Pi7bdX9H+3R6OaXHUwMDBid/TNVsIj7LR82cnfXHUwMDFmOs7jZvg0+vZcbnZcdTAwMDMrZ8Z/f3778/9cdTAwMDCARWMpIn0= - Graph DB(neo4j)3rd party containeror systeminfrahub open-sourcecontainer or systemLegendinfrahub-bundledcontainer or systeminfrahub-sdkinfrahubctlbrowserinfrahubGit Agentmessage bus(rabbit-mq)cache(redis)repository store (NFS or local)git repoLoad Balancer(HA Proxy)infrahubAPI Server*System needsbackup**object store (s3 bucket)* \ No newline at end of file + infrahub-sdkinfrahubctlbrowser(frontend)InfrahubAPI ServerInfrahubAPI ServerGraph DB(neo4j)Message Bus(rabbit mq)Cache(redis)Repository Store(NFS or local)Git RepoLegendinfrahub containeror systeminfrahub bundledcontainer or system3rd party containeror systemTask Manager(prefect)Task WorkerTask WorkerObject Store(S3 bucket)httpsGQL(cypher)AMQAMQREDISNFSgithttpshttpsLoad Balancer(HA proxy)System needsbackup****GraphQLGQL(cypher)REDIS \ No newline at end of file diff --git a/docs/docs/media/infrahub-readme.gif b/docs/docs/media/infrahub-readme.gif index d19b082383..d0d9b2d8f3 100644 Binary files a/docs/docs/media/infrahub-readme.gif and b/docs/docs/media/infrahub-readme.gif differ diff --git a/docs/docs/media/infrahub-readme.png b/docs/docs/media/infrahub-readme.png deleted file mode 100644 index 0adbea45a8..0000000000 Binary files a/docs/docs/media/infrahub-readme.png and /dev/null differ diff --git a/docs/docs/media/overview-interfaces.excalidraw.svg b/docs/docs/media/overview-interfaces.excalidraw.svg index b68036834d..c14395f270 100644 --- a/docs/docs/media/overview-interfaces.excalidraw.svg +++ b/docs/docs/media/overview-interfaces.excalidraw.svg @@ -1,6 +1,6 @@ - + - + eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nO1daXPiSFx1MDAxMv3ev8Lh+bJcdTAwMWIxaOo+JmJjw1x1MDAxN1x1MDAwNrft9n3NTDhkXHUwMDEwIHOIXHUwMDE2wtiemP++WbRtXHSQXHUwMDA0XHUwMDE4ZMvu5YNcdTAwMGZJQKqqXr6XWVmlv7+srKxcdTAwMDZcdTAwMGZdZ/X3lVXnvmK33KpvXHUwMDBmVn81x+9cdTAwMWO/53pcdTAwMWQ4RYb/97y+X1x1MDAxOV7ZXGKCbu/3335r237TXHS6LbviWHdur2+3ekG/6npWxWv/5lx1MDAwNk6791/zc99uO//peu1q4Fvhl1x1MDAxNJyqXHUwMDFieP6P73JaTtvpXHUwMDA0Pfj0P+D/lZW/hz8j1vlOJbA79ZYzfMPwVGigkHj86L7XXHUwMDE5XHUwMDFhizlFlCuC2MtcdTAwMTVub1x1MDAxM74vcKpwulx1MDAwNjY74Vx1MDAxOXNo9VCvXHUwMDFmN1x1MDAwN2qAd52d2+ZOaefgYOMg/Nqa22pcdTAwMWRcdTAwMDdcdTAwMGatoVk9XHUwMDBm7iY811x1MDAwYnyv6Zy71aDx3G6R40nv8r1+vdFxeub2w1x1MDAxYvG6dsVcclx1MDAxZcwxhF6O/miD31fCI/fwXHUwMDFm18rCilx1MDAxMqkklZjp8GbN+5WWluJcXFx1MDAxMCVcdTAwMDXBROMxuza8XHUwMDE2dFx1MDAwNNj1XHUwMDBiqWmHsdCyXHUwMDFiu9Ksg3md6ss1gW93el3bh+5cbq9cdTAwMWI83zFCXHUwMDE2wvBN/OVUw3HrjVx1MDAwMM5JaWGtXHRWMjzZc4a9gDVThFPKQsvMt3bL1eGI+Ctse1x1MDAxZsZS2byl02+1olxy2Kk+NeDzyFx0x1x1MDAwZX068k94W+b6rfExXHUwMDE3XHUwMDFkd5HhsPlcdTAwMWSdXzhcdTAwMTeqtMH2blx1MDAxZGenudO52Xq59ZFBXHUwMDFhOPfB6suJf57+XG7N73er9o9xhyXRXG5RRrhmYVe23E5z/N5aXqVcdTAwMTlcdTAwMGXVL5FcdTAwMWKZwMjw6+PgwVhcdTAwMTI8XHUwMDA0RYpLScnM6EhvjlxcokNgZnFMMZGmzTHlI+jQWFmac0JcdTAwMTVcdTAwMDbsZIlcdTAwMGUspEU510SANeCzlJpEXHSdXHUwMDAwXHUwMDA3JYqAVVgujo2RXHUwMDEzXHUwMDEzIFjmOFxyrfI6wbH7aDqFqJGjRbvttkxcdTAwMDeEdzxcdTAwMWPA0IpuXHUwMDA3bqbRvyn0qs3VkbNrLbduhvRqXHUwMDA1rHb8kdFcdTAwMWW4wC0vXHUwMDE3tN1qNcpcdTAwMTZcdTAwMTX4StvtOH55XHUwMDE2J+/5bt3t2K2TVIugXHUwMDFknNJzz2Er4vVu7J5jzprbXHUwMDE2qaBNJTZKyPjRZ+QqzFx1MDAxOSfzIPf0+K5CiofBltpql1x1MDAxZarXrZPb86t8I1dcdGhVXGb3+TF5XHKAKyX0XHUwMDExzSGtnaP+hq49NPaPyrRZ2Hy4L1x1MDAxNzZKXHUwMDFmitYo4knggKGCXHUwMDExgz5BM6MjvT3yiVx1MDAwZaUspVx1MDAxOJWMYqNw8VxiOt6Q11x1MDAxOLOE5JpSRaXSSJBJlMTwXHUwMDFhXHUwMDAz7YFcdTAwMTDRP1x1MDAxZq9VglZGtDbFxyfR2ohB2bNcdTAwMWHV40dfgFx1MDAwYlx1MDAwM1nAsFx1MDAxMLNcdTAwMDP3sbDtdYvXa0f1WuVx56KzvX/ZPsk3cDGoOUtJxTUmQ15cdTAwMWJVpEpcdTAwMGKLzYTcXHUwMDFhUlx1MDAxNYSy4bWI73xcdTAwMGXTXHUwMDEwiGgmpMxjmFx1MDAxNnhr3EZF5q+T6mH122Hxptj/YHzGXHUwMDEyxVx1MDAxZSaCwlx1MDAwYpPZs1x1MDAxOOntkVNYMGFxhFx1MDAxOCVCSFx1MDAxOPxilNBcdTAwMTB+I1hgRixcdTAwMTjlXHUwMDFhXHUwMDE0XHUwMDA0VVx0gZqchIdgXHUwMDFhwjqBliD3Plxuo9343qDn+H92/lXz4UqnU/13Rsw2xc2PM9uTYStxds1EcFx1MDAwMr+e4DBcdTAwMTLjR5+RLIXgclx1MDAwZVx1MDAxOCNSPyX23pFzey6YOPvau+itXeRcdTAwMWLGUlJLgP5+JrfRoFxyY1x1MDAwMqpVYKaeYPwuQVssuSnKkOJ5JDdcdTAwMTmcblx1MDAxN+92e8fniqw93F1dVIqD6kzk9mvax9pcdTAwMDdl12vUN+3LtVwi4/0ttP746Md/rO1cdTAwMDOg5iNNJlx1MDAxONdR0GVBmpomIY1gsEFiydTMYEtv5nyCTStLQFx1MDAxMFx1MDAwNW6fU81cdTAwMDRcdTAwMWRcdTAwMDNcdTAwMWKlb1x1MDAwMzbMkYU1hoCTXHUwMDBiwVB8pmRcdTAwMDJ0hFKpOYtkTj49Y5afQq4/O2tcdTAwMDfllWPHv4uy4lIpc1xud4xTZrplmZNmRGSNq18hXHUwMDA0cFx0XHUwMDEysyN5t7P+oMol4lx1MDAxNlm7XylcdTAwMTb2j1x1MDAxZndZzpHMsEWx1lx1MDAxYTxWXGZtXCJcdTAwMDB6VP0uguRfasPXopRcdO5cdTAwMWSsySNfVspcdTAwMDc7VdRwXHUwMDA2XHUwMDA3J/f0au0hUI9cdTAwMWJscb5s7G+L663i6aayu3t9VrJrlPaXxpdUISajeMuCL5Nnylx1MDAxNVwiQjFGZs+8pLdyPkEmhEWomSonKI4uXHUwMDExXVx1MDAxYciyoEtcYkw1jFx1MDAxNfFcdTAwMTPNXHUwMDA1viFfTiGNnPEl5clZVE6lxIjrMFxmnTo5eFG6lqR0duht3dQqXHUwMDE1eo9dv5VvLFx1MDAwM0SlxeAuKdNqMotcbkE4t0BcdTAwMTkzLpRKXHUwMDA188LpXCKgZi2YmYnhyLxCr1x1MDAxMp0l5FpcbkOtY5c8Y1x1MDAxYnFzJzySXHUwMDFiyFx1MDAwZp96e+tHa5f8Tlx1MDAxNFx1MDAwNe2VXHUwMDA320H/yLNcdTAwMTfn08OvJzu9TVkvfyVn5PvxYL9kN9qz8Wnq53YvXHUwMDBl1nona93BVadWXHUwMDFm3DhV+6B9vCyeXHUwMDA2euCCRXGeSTJYJE9uXCJcIrniclx1MDAwZXSn918+0c0wt5hEXHUwMDE0QldMYtCthGVKY1x1MDAxNEeYZIpuQixcdTAwMDXOlspcdTAwMTiOnpzWxJJjJoVeXHUwMDA2jl/P0VoqMY+WXFyMo7fdYOXI6XpZTWqmc9M4L09ak/mMpoxcYvcxtHIkqVx1MDAwNFx1MDAwMppcdTAwMWSsXHUwMDBmduFwgHaCnfLj6VapeVx1MDAxN5TtSFYul2A1lVxiXFxwozuGKd/RiVx1MDAxYlxmMLVcdTAwMDQ0gUBTsIq1xFx1MDAxNfJmKV8twFx1MDAxY1xie5ZQgrB0yt0+r5Km3nhcdTAwMTCa7WHmndVcdTAwMGXrl7eLU+5D0Fx1MDAxMsVW3dm655tqbWfnvoDuXHUwMDFll0C5maaSzdDCJFxu50xC42TKZVxcwfhVc1x1MDAxNJGnd18+QayppY2CZcC4VCg6XHUwMDBlYvk2IMZKQ2zMNVx1MDAwMW5PqrmLmb6BeF5cbnjvO9Lu3ON0Mdo9sXvNlT27Y9eHM7Bd36lcdTAwMDFJZTVcdTAwMDE7hZbGWXiqcctcYpB/uJJcdTAwMTgsg1x1MDAwMEqUz0ZZSqr07JWz67flS7/5aJ8+tLun9a2tXqV2Uc43mKWiXHUwMDAwXCKlXHUwMDExjauc1VpYXFyhXHUwMDE5SilcdTAwMTRcdTAwMTOUq9dlk5m2KGJCxVx0Z00sTsfgXHUwMDFkQllrTlx0W8Ks0CQtk8Vouae3K1xyXHUwMDAwQnXztsbOr7yGt3/pvleZUaTXbD9YdztVt1NcdTAwMWZ/i9OphmdcIu3xtOZqltTX0Fx1MDAxNVX6pnGQhVx1MDAxOdXcMFx1MDAwNDeOXnJcdTAwMTm5qm53Td9alOphNiT6XHUwMDAx7r1TPfDcTvBk4EQ/tOxesOG1225cdTAwMTCMXjl6m2tcdTAwMDbzXHLHnuh3uNHouXHn0DWfONq/f0S6XHJF+1x1MDAxML38/devsVdPXHUwMDBlbfOKXGbq8Fx1MDAwM75Ef8+rSZLr/5lcIppSxGf3YulDN59eTDJLXCJTQo/hdjlcbr/5x/tNaM6kwoJMWVx1MDAwMLCIXHUwMDFiM1NcdTAwMDZKaqZcdTAwMDRSZlxch670xZuxXHQ9os2SXHUwMDA1oT5+dTNcdTAwMTYjR9OyXHUwMDAwvt1tXHUwMDFj7v7ZOdo6PslIhExh4olUQLxJM0lcdTAwMGbKXyc9KEmcyJZcdTAwMDRGIZtcdTAwMDez6TOOucSs5sRkw1x1MDAxM9bsaPCRyEx8PSXls4EsuFxyzFx0ZbE5O7BcdTAwMDC8XHUwMDA1XHUwMDFhSeg9S1x1MDAwZlx1MDAxM0NoXHUwMDAweSaT2lx1MDAwYkqPq/2tbVq6ln7zZKPU8Pe3u+T09CeSXHUwMDFlXHUwMDA1ZCFmNFx1MDAwN8NmwVx1MDAwYoN+XHUwMDEykct+aFx1MDAwZkwtyUZcdTAwMTZjfTrtUYhcdTAwMTnd5lx1MDAxNVx1MDAxOdfLUlx1MDAxZsnFdVxiOlx1MDAwMIkoXHUwMDE5TnNl6cM3l67s48pcdTAwMGZwZFx1MDAwMFx1MDAwMa7Fh6+sy5f+mMLHudBcdTAwMWY4eVx1MDAxOVx0pVx1MDAxOFx1MDAwM2zx7Fx1MDAwMkTcXHUwMDA2svS9dnxwdHVbbKDWqcN6OU99YFx1MDAwMKZcdTAwMDX+XHUwMDExXHUwMDEzwmLqXHUwMDAytGZcdTAwMTbgXHUwMDE5oleaoVx1MDAwMsGKTiY4olx1MDAxMkSjMX3yhFxcqbhcdTAwMDBcdTAwMTWCMpmTWFCBrO/32MVdZ/ee8cv+laqr777Pfy5cdTAwMDWCKeZcbknEqKBSXHUwMDEwXHUwMDE2o0BcdTAwMDDQ9FMnP1xucYPbvFwiw3pZXG4ksVxcUVBkSmnmXHUwMDEwIOmjN5eu7ONcbpDhqnSzs8z/9ccy9cdcdTAwMTQ6fkP9kb4hXHUwMDE3Ta5eokRC9IDU7MDl8mr/23Gh6O3vbF3tXHKqZ5WSXHUwMDEw+Vx1MDAwNi7cXHUwMDFmspghXGJcdTAwMTmnQTDQiCWYXHUwMDEwjNH02sR32rmEcC4k4suYTV16ZUT9yD0rt4+uTjZ6bd7lwVx1MDAwNb/pXHUwMDFm5rcyXCLbYkSBUfaVXHUwMDExgiTPplxuRSghs6/MSe+9nIJZc0uA7NNcZlx1MDAwMyZcdTAwMTDWY2DmxJJcZiBGf+xDlFx1MDAxOZix0GBcdTAwMDdcdTAwMDQuXHUwMDE4vkhoXHUwMDFlU2c8WZDIlFnVsZRcIqdcdTAwMDVcbiPmXHUwMDFjpnFMPGdhxLnnNzNbKjCFkmKLIcZccsq8KlHgxFx1MDAxNVx1MDAwMkQqs1x1MDAwYt1cdTAwMWNrY53TMsG77LSjjjt3XHUwMDBm98e7zc3vOV9cdTAwMWKLITqAgI0wnUDCiFuAJYWQeJ8ldWlbYjKliYA3LmGpz9JcdTAwMTm4rNa736rV7/bR4Lpw2ve9wuVcdTAwMWV6V1x1MDAwNs5cclOmJN80xERcdTAwMTDEzVG2n97OOcVcdTAwMWPXXHUwMDE2xYyyXHUwMDA0riTSQnBcdTAwMWVPq1wifFx1MDAxN67EXHUwMDEywlbOKXnPtPmnI8sp1PFmZJmYMseCp+TMkaJcdTAwMWPocvZ1semKP5+wNdW9hDCBOfxcdJ5qXGa1kkA0y6FcdTAwMTkkQ1x1MDAwNEVcXNgyXHUwMDEzTWBcdTAwMDLm3LA2x1xuy1xiXHUwMDA2XiDLlYVcYiFcdTAwMWE8qYJcdTAwMTA7slnLy265iFFcdTAwMWF1svlJn3895Y/t6rdL75vb2C24Z1x1MDAxN1x1MDAwZpeovqT0+eudxlj6PClJnq54V6JcdTAwMTWCQlx1MDAwYoXN7DxETEKHdbYrzylyYUlKJCOCYWlWb9Lox0xNlc+U009fMzRirlwiXHUwMDFjgVx1MDAxOViA99eEq1x0cymMOVxi7DhcdTAwMDOdqDRV85n7kTL7iVxiNK/CXHUwMDA0+MKP+1x1MDAxMv09t2hK3C2LgFxyWPE59stKx1g+fS/j4Hs5QVxcQsMqXGJVRp0vZcJCXHUwMDEyxKOakl5YxPdcdTAwMTJsXHSKtTRb74lhvDLpfMlEllx1MDAxZt6ChttGvKdcXFwiOMpXWWf5626QkUyaXCJcdTAwMWLGZdKIITPJI/zKilx1MDAwMqySy5BBLCBcdTAwMTiYeI7ljalRXj5cdTAwMTGKiLa42c1cdTAwMWLuVZhcdTAwMDE/ilBcIqhcdTAwMDUxPJ66NGrB1Vx1MDAxNFxuI1xunCpcctnL0EuE03DaXHUwMDAykaZ4XFxpoybIrPWgeawroPXibsU7XHUwMDBiRGvdvtBnvHrY2zhamjB67ZqsXHUwMDE5hVH6uqiV0epcdTAwMDFcIkFDa2aEj6JcdTAwMTRNrp3AXG56mcxXOzCTIJpDv0mpXHUwMDE41TBcXMzWbITyXHQjuUXk5y5xSFx1MDAwNJt5XHUwMDE1ojhbklx1MDAwNkouXHUwMDFhx1xmSYSEmKNqPFx1MDAxZE+5dLFcXGJLXHUwMDEyxoDOQYTQsUJcdTAwMDdCtYXAuWbrYJklTMkk4E9cdTAwMGLKVEhpKVxuyOxWTkEzv6NcdTAwMDB64zKH4dO/MpJAU7TBuFx1MDAwNFx1MDAxYTMlW1x1MDAxMcRSNijkWEom53jaRPpy+VxcXCJUKWHKzlH8ulx1MDAwZUxMXHUwMDA0wYBcdTAwMTembZ22XHUwMDEwRDG4XHUwMDAx+Fx1MDAwMs0kXHUwMDAzfoqUJ0Q1XHUwMDEwQWp0+eHLVKhkXGbCKZ5HXHK0VvabXHUwMDFisl4v3O6oi+L6o9dpktnmVHKggdJ3XHUwMDFhXHUwMDFk1UBcdTAwMTBcXFwiXHUwMDAyWEFgXHUwMDE5RFx1MDAxNjhy1ZNcdTAwMDRcIpbMRlx1MDAwM80l1SjEwVJIanJYXHUwMDEykUlcdTAwMTWE8fi+QJ9PXHUwMDA2JeFteDKCtCWpIJ6YXHTCSkgtXHUwMDE4m2NcdTAwMWaddETl0sdqs8mrhniayKFcblx1MDAxYX2iXHUwMDBmxoybwnZGQKdcdTAwMTCzXHUwMDE5ZH50kFlcdTAwMGXEOFx1MDAxNT9RvWeWQmiKPnhcdTAwMDMhlL73JEtcdTAwMDYqNpO/Yp7CbOdGXHUwMDFkfOPfmien275zcubvk+u7Xr6BXG5cdTAwMWFHW4yGT1xcXHUwMDFkf4KPtIA1YECKrFx1MDAxZlWyjL0nTSWMkEyoTHbceHWxSSxbRMZNZeNM2Fx1MDAxMpe2uH33iNlNa6+xc/FcdTAwMDZ7T05cdTAwMTdcXExoXHUwMDFkkZ+Z7Vx1MDAxMZn4XFxXXHUwMDEwL0KY3MXMIIxvzHyDkGFlwVx1MDAwZlx0d6uojpY4vjxcdTAwMDBPh2WZXHUwMDE5glx1MDAxMGJcdTAwMWbOpCZm8Vx1MDAxNlx1MDAwM1x1MDAxYcSTIIwrNkFArlwiatg7cKaUlM2zm2lcdTAwMWNnzl5sUneDlUrLXHUwMDFkacXl1pqkc0nMJMqEPVmXmiRTJ6JacDVcdTAwMGZq071YTlErhUUlkvHUqU0hXG5WXFxpXHUwMDAyQKI8m1JcdTAwMTNsUY2leeQ4XHUwMDE1XHUwMDE4XHQsYjJcdFx1MDAxMllcZlS25lJA7Fx1MDAxMVsspoXZhzaTXHUwMDEyzVx1MDAwNdNcdG1SuKLN622/Vr6wT1x1MDAwN3uqWvLvlpVOoIigV+0uO2M6IVx1MDAxZMQr0blcbmON4mZcdTAwMTZSQcxcIiOjaSVcdTAwMTKna0LNPqxcXFx1MDAxMST4yKzL25ebICzNw+5cdTAwMDVcdTAwMDUgMKJjplekRdXwsXOfOLOQXGZA81x1MDAxYYde+Glfor+XV2hcIkxcdTAwMTFcdTAwMTBVczxhJlx1MDAxZGA59bxcdTAwMWa20Fx1MDAwNJulvqY28T3zXHUwMDBin6bSZIpqWF6lyZcn/K/a3e5xXHUwMDAwrfniQqH73OqzfHzq7tU711x1MDAxOawnj6UvT4g34HKGrPnPl3/+XHUwMDA3XHUwMDExXHUwMDFkXHUwMDFhyyJ9 - infrahub-sdkGraphQLGraphQLRESTinfrahubctlGraphQLRESTbrowser (frontend)infrahubAPI ServerGraphQLRESTinfrahubGit AgentGraphQLRPCGITgit repogit clientGIT \ No newline at end of file + infrahub-sdkinfrahubctlbrowser(frontend)InfrahubAPI ServerInfrahubAPI ServerGit RepoTask Manager(prefect)GraphQLRESTGraphQLRESTGraphQLRESTTask WorkerTask Workergithttpshttpsgit clientgit \ No newline at end of file diff --git a/docs/docs/media/overview.excalidraw.svg b/docs/docs/media/overview.excalidraw.svg index c861ab2ca0..39666a3bc7 100644 --- a/docs/docs/media/overview.excalidraw.svg +++ b/docs/docs/media/overview.excalidraw.svg @@ -1,6 +1,6 @@ - + - + eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nO1daW/i2pb9Xr9cdTAwMDLV+9ItVXzPPDyp1cpcdTAwMWNSmeekq1Uy2Fx1MDAwMSeAiTFJyNP9771cdTAwMGZcdTAwMTkwYFx1MDAxYlx1MDAxYnDi2+9GureqMMHbh7P22vP517dK5Xs46Lrf/1n57j7X7ZbnXHUwMDA09tP3XHUwMDFm5vVHN+h5flx1MDAwNy6R4b97fj+oXHUwMDBm39lcZsNu759//NG2g3s37Lbsums9er2+3eqFfcfzrbrf/sNcdTAwMGLddu+/zf9cdTAwMGbstvtfXb/thIE1usmK63ihXHUwMDFmvN7Lbbltt1x1MDAxM/bg0/9cdTAwMDf+Xan8a/j/iHSBW1x1MDAwZu1Oo+VcdTAwMGV/YXhpJCDGXGZPvnzgd4bSYqowXCKaKvTxXHUwMDBlr7dcdTAwMDE3XGZdXHUwMDA3Lt+C0O7oinnp+7FeO71/Uk94z929u9/d2T06Wj9cdTAwMWHd99ZrtU7DQWsoV8+Hx1x1MDAxOV3rhYF/7156Tth8X7jI60m/XHUwMDE1+P1Gs+P2zPOPXHUwMDFlxO/adS9cdTAwMWOY19BI+NdF+Gdl9Moz/ItxZTGkXHUwMDE1w4RKzDT7uPr2+9hCXHUwMDEyMUk4JphoPCHYut+Cr1x1MDAwMlx1MDAwNPtcdTAwMDe51S5jI9Fqdv2+XHUwMDAx8nWcj/eEgd3pde1cdTAwMDC+sNH7nt5cdTAwMWVcdTAwMTlrblx0iZXk6PWHfryl6XqNZjhcdTAwMTR2dHt3+C1gzVx1MDAxNOGURr5Gc9Nu1Vx1MDAxOW6J/1x1MDAxZK19XHUwMDAwm6lqfqXTb7WiXHUwMDBi2HHeXHUwMDE28H3rjDZcdTAwMGZ9e+XP0VOZ929Obrroxotsh41cdTAwMDd0eeVeqZ11tn/nurv3u53a5seTj+3S0H1cdTAwMGW/f1xc+PPtbyPx+13Hft13WFx1MDAxMq1cdTAwMTBcdTAwMTNK4shcdTAwMTK1vM795LO1/Pr9aKt+izzIXHUwMDE0SIa3j8NcdTAwMDfSXCJcdFx1MDAxZlx1MDAwNCGtXHUwMDE15ig7PtJcdTAwMTeknPiQxOJCwnMyhSimTE5cdTAwMDJEfFx1MDAxNkCEtCjnmlxijqlBippcdTAwMDZcYp1cdTAwMDJcYiWKgFRYLo6PsVx1MDAwYlNASNmrSFxiyvPs1ZFUfic89V6G202Nvbplt72W+Vx1MDAwNkZPPNzEsIpeXHUwMDA3XHUwMDFlptmvrfSc++9jV1dbXsNs6+91kNpccsZ2fOhcdTAwMDHBfLyh7TlOlDLqcEvb67hBNYui91x1MDAwM6/hdezWWapEsFx1MDAwZe7O+zeHLcIjW6PnmqvmsUUqcFPZTVI0+epcdTAwMDe5aYaoQIyPkDRcdTAwMTO8+079JWyc/Pbua7XQv31+auOSg1dcdTAwMDB4teZSKlx1MDAxOU9uyKKwXG5aXHUwMDE2jl3JLKTpXHUwMDA3t2UkN6EpvJWTkdzlIbd90T8n9uHlmmg/oJeOLXjQsZdEblxuXHUwMDE0XHUwMDFjVVGgXHUwMDE0QW5cdTAwMTIn2n5KU0Ekz2H6pS9HOdGhpCWNJSFcdTAwMTRWXHUwMDA0RUyot9/nn4VcdTAwMGXGwPTjmlJFpdJIkGl0TDNcdTAwMWIjXHUwMDEySaBdsjg45ma23Fx1MDAxYnU5zFZcdTAwMGZbXHUwMDA1XHUwMDEx21xmJZ9EbGNcdTAwMDJcdTAwMTXOa1x1MDAxOJNcdTAwMTSrlIFJXHUwMDA2Ns9oUWdB96W58+TX6lx1MDAxNy/e0/pt8FBzXHUwMDA2et8rN3QxXHUwMDE2lmKSvnttXHUwMDExXHUwMDEwmFx1MDAwZlBSWJpRjsF7lanQvUWqjtBcIsRGLKpe/bZp0Eb051x1MDAwN6VxXHUwMDEwXHUwMDFh68hcdTAwMTOWh9JcdTAwMDbYXHTOn+6rXdnzSfdku9q1T8+XQ2lUaiYjXGZfmL+GeVwiMlx1MDAwNNeY5OG09PUoJzCYsjhGkmottYlcZozjQuPPwlx1MDAwNdJcdTAwMTbiWlx1MDAwM3FcbiaYYmxcdTAwMWFcdTAwMWR8XG5cdTAwMWQgtNDAaUuw9+alNE6Q0Fwiz0aNpTQ09mpcbqVVXHKDVPZcdTAwMWLt8FfnzPdbvYKIbYaSnyS2kViVXHSpMrFcdTAwMWJj87ObXCKJXHUwMDEw1uCwXHSgt+xcdTAwMTAmzt320Sa9+Ln91Fx1MDAwZXZrLzveQ71XblxiU00tsL61XHUwMDEwsVx1MDAxMUks4LImkoLtZTC8iFX6j9vhzzR8wU+zXGZq3z22SPjkXHUwMDAzvphrS2nGKSaxYUljhLAyem6oe1V72qDb96jWvpWnpzs3V09H8TRnXHUwMDA3gf9cdTAwMTThuVx1MDAxZmmf2zukW1x1MDAxYlx1MDAxN+i4i8XBJtpTx2d7zbtM9Jn6sfZR1fObjVxy+3p1i/H+Jlp7eVx0sok7k5aZ1JwhXHUwMDFjXHUwMDA1d1x1MDAxMbRMkpNcZoiAn8U0XHUwMDE2mSGdvsylhDSX1JJcXFx1MDAxMlxmzKs5/G9cdTAwMWPShCCwZk1URFxuUlx1MDAxNKQxXHUwMDA1uFx1MDAwMiNTzcGh1TxbXHUwMDA0XHUwMDA2yFx1MDAxOFx1MDAxMVA5YnFcdTAwMWPP7WRcbkYwZjn2aFx1MDAxYyNHniCjk1lcdTAwMTBcdTAwMTHPYKQkXHUwMDBmMydcdTAwMDHTdFx1MDAwMn5VXHUwMDE0sa4lS3QtXHUwMDE1wpowzLNDtbPx0Do61e2nrng+Wr9C3t79QcnzgVx1MDAwMuiVXHUwMDEwQpPygVxcWVx1MDAwMpuwpEhnX8VcdTAwMDTlaj6oUpNcYuTSXHUwMDE4z0opglWca8lcdTAwMTPJlzJcdTAwMTOzXCLLwOw095LZ3JuatVx1MDAwMydsLuO6XHUwMDE32kG45nVcdTAwMWOv01x1MDAxOFx1MDAxN+wtXHUwMDA1niUxMVx1MDAwNH+9b6RcXDF5XaJcdTAwMTjnYKTAt1xuokXe1bC7Q8VscVx1MDAwNl+AiizycNs+u86R73XCN9mnlsTtOLNFTVdcdTAwMDRcdTAwMTFRkYVcdTAwMTQnSnJcdTAwMGVcdTAwMGUslUagKVGBQfKJ2LJ74brfbnthOP7O8Vx1MDAxNV81iqLp2lO7XHUwMDA0XHUwMDFlMXptUqN0zSeOW2Kjv1VGmFx1MDAxYv7j4+//+yP23cl4MD9cdTAwMTEkjD7oW/TP/FpQUzb58rtcdTAwMTbkXHUwMDFhaFx1MDAxYUmWPb7mUXfb3WmsNladi42djeCOrpCS+yCMUlx1MDAwYlx1MDAwYik4fU1cdTAwMWONIPv6+0paWitwlZUqTFx1MDAwYmIqLIbBMlx1MDAwNIdcdTAwMTDuw2JcZlx1MDAxNlx1MDAwMSDWWGpcdTAwMTGNvr1pQaKIXHUwMDAwS4dcdTAwMTVcdTAwMTJoW0xcdTAwMGJKjYlcdTAwMWPJtXwtuNdZXHUwMDFiqOpcdTAwMGXxtli7X99aOTh92WOxWlx1MDAxMFlMYHA2wVxyXHUwMDAwK1x1MDAwZuzTqFx1MDAxNnlVLcrCXHUwMDA0fyTvXCKW2JeoQoA5XGJcdTAwMDJ7XHUwMDAzlDKKuFxyI6XNLU3oXHUwMDFjSvuvpFx1MDAxMVx1MDAxM7FhfqKoyKlcdTAwMTHT01x1MDAwZbDsiX5cdTAwMWOhXHUwMDFjrFx1MDAwZUSyh2aqa4fdrf3aXHUwMDFl9p3L7vVxzzm9PC15xpBQYjEwgz/U4kTaXHUwMDAxI4soSlx1MDAwMUi04PCqQJZGYKVSybT5iYnPxHhzXGIzplx0KWPuQTSCzqbvrL/o/Zurzp6NN/jvqMqqLJJ7UITlqr9JxUhy7iFSXHUwMDFiNVx0XHUwMDBmwZWUlOQoN0lfkHLCXHUwMDAzdiXlXHUwMDE4NFx1MDAwMShcdTAwMWaN0HhCXHUwMDFkzIXPQlx1MDAwN+JcdTAwMTbmWjMtKGJaKT2NjpiYJVx1MDAxMYBoJMRcdTAwMTLgMX/2XHUwMDAxXHUwMDE2XHUwMDA2zWVcdTAwMWHMlX04XGabblA59c9cblxudszQ8ZPBjlx1MDAxOHEyRTuwXoDVKNKTL3+kXGY1NlViNHvE42Vl2+9u/V49adzWX3avOttcdTAwMDfX7bNyo5ZSXGZcdTAwMTZcdTAwMTPYd4SpaVtcdTAwMWZsXGJLU6zeXHUwMDAzXHUwMDFlxaGWXHUwMDEwZYGO/KA0XHUwMDFkkzKMSaiDUYKZkLKMXHUwMDA10KG/ym20xYI14lx1MDAxYzuHx1u1rf7O4lx1MDAxOYG1u+p1cP9in1x1MDAwZtrd88bmZq9+e1VdUkaAKmPgiCj+XG4hS1wiXHUwMDEzs3zG8cRcdTAwMWOoXCI77NJcdTAwMTe6nLBcdTAwMTPcQuB8SaSUQkSNXHUwMDA3XHUwMDFhlUafXHUwMDA0O/D+LE61YkiAR89ITO3ZdKJcdTAwMWVcdTAwMDNVUviaXCJ1nF+RqddKklx1MDAxYzt1Ma48XHLh9n+s9np9WM66+6uzYYd2Qbw5g0YmeXNCtMq4ZIVn7KMtQlNYpkjBXHUwMDE2zlx1MDAxMS5LXHUwMDBmXpRcdTAwMTLLhFx1MDAwMJbBjqBgbMYlXHKoXHUwMDAyb40xPitcXLZIfo8gZIFnKkimOjTGNVW4jJxZr1x1MDAxZe06qOk+XHUwMDFknT3Tm9VBqF7WszmCX8aZTGrwXHUwMDFk8mQo5+JM8JxcdTAwMTJhxsDfkVx1MDAxY8vs/mX6OpdcdTAwMTNmQliMMilcdTAwMDRcdTAwMDP3UtFcdJQxujSUpTMmllx1MDAxNjEuLlx1MDAwMc9cdTAwMDBTovA04uRcdTAwMTTiXGJmRMNmXHUwMDExX9iJRFx1MDAxNdUg8KKUmbleu/qWvP7VOVx1MDAxZHTqXHUwMDA1ceVcZr6IrWyLkSlcdTAwMTNLXG6citzEhJLQiT4m5txk9GBXZ4Zuui4rJXSpKdhcdTAwMDbzUktcdTAwMTbDkFx1MDAxYWxhXHUwMDAz6NmhoUVcdTAwMTJKxMJcIokhwVxilkii2Fo20+fJKYnEU8uSSaJKXHUwMDEwrOfCc8ZMUrohWlx1MDAxOWVmsFx1MDAwNYpFM6G0YFx1MDAxY3EwL1wib/rIzFxm+3bfmsBcbskk5cl8IUZcdTAwMTBnVEtcIkCHI1x1MDAxMSOxiViafpzCM0mTquMzk0nTuDA/XHUwMDExRIw+4Fv0zzl7V1wiNsqUXHUwMDE1oylcdTAwMTGKRXzhmc5CSLdbZ/LwQa7hXHUwMDE3fI7bfXfjvtyqUJtdXHUwMDA1242QOFU4rO/VXFxjxmi6XHUwMDE5k8HxT1aFXHUwMDE0jWd1XHUwMDEz6ntcdTAwMTnB6j1cdTAwMWNcdTAwMTfVym+aUZFhRVgpm1lcdTAwMTYom011JIqqXHUwMDFlvlBcdTAwMGZh49K/urzc9mz8sv1cdTAwMTKsstPlJMD4RKV4UTE9nlg6iIlcdTAwMDTDXSCWXHUwMDFk2enrUU5kXHUwMDEzZVx1MDAwMSRcYqWMXHUwMDAxa0Til2/IVsUjXHUwMDFiXHUwMDAwaSltwlx1MDAxMIpcYlx1MDAxYz8lIabxxniwXHUwMDA0qa9svMm9SWN9k8zhPL92XHUwMDA3PFXphX7g/ur8XHUwMDA3fLTdqvhB5ZRWan24Tfif8/orod9NdlbS+WrSWckjZOFhPkxUYsxcdTAwMWW4hFPEXHUwMDAx4dm9XHUwMDE4uumpvVx1MDAxYdu6WVx1MDAxMe4h78q77eZqyVx1MDAwMY6VJTk1fYQxxcGm61SwUWfOQjH75OJgyS1JI4b0SMuMqFx1MDAxYuSkZGi7vdnak4Bn0tRFYPU3cy/yubGMXHUwMDE4uWPPdpm/xi82fFx1MDAxNjavz9eP3fZJLVdkkSBJpIxivJDIYvIkXGJMOUFYMZ09slx1MDAxOP/U5Vx1MDAwNjZnXHUwMDE2gFtTziS4hGh8yJFS+DOAjUxjn1x1MDAwNmCSYVx1MDAxNjwmSlx1MDAxMZeIMyPKvrZlXHUwMDE2nFx1MDAwNNBGn1e04rjdlj8wglV6g56Zllx1MDAwN9TY8YOOXHUwMDE3/KjYnZ5Xa7k/KsDRgX3rXHUwMDA37Vx1MDAxZlx1MDAxNTesW4Ww+VxmXG6bZPNFXHUwMDA1L57hXHUwMDExTU4xcME4+OY5XHUwMDE0gafvXHUwMDFiXHUwMDFiJ359p1x1MDAxZe5cdTAwMWbs3FZcdTAwMWTeuNovuVwioMziRFwiXHUwMDEyOzFJM1NaS7XBXFzRw86E9VrfmdyAXHUwMDBiwiikuP4omJlSXHUwMDBlWlx1MDAxM8PzkeRreVhcdTAwMWVd/9zv6b2TXHKydvBzvXqx3jrc+blcdTAwMWN/l5mZb7hw1lx1MDAwNLQkdonAXHUwMDBlkUzSqF6ehZb0XHUwMDA1KSdamLSwXHUwMDA2etRcdTAwMWNzqVx1MDAwNJ9Ai7ZYpEukyFx1MDAwMUrIXHUwMDEyUivONdA0+L0xpWOxXHUwMDA1n1RLQPlcdTAwMTLgMT935t2qi3HnK9usXHUwMDE03OM6Q/FP8mKiUMVcdTAwMTd/4pTWdGZcIihcXOvsibnz08c62TpcdTAwMGU31WZ7Z+D8bp3dXd6UXHUwMDFiwlx1MDAxOJbA0mJcdTAwMTSOnsTwX4rxXHUwMDAwTGBcdTAwMGJLuoSytKVcdTAwMTPeJeqv69tB8+CkSu9XNlx1MDAwNs/VlfVsxaClITxcdTAwMWOZ9TaJXHUwMDE2uEa5XHUwMDA0IzEzWtJcdTAwMTekpGghyNKcgF9cdTAwMWVXtfmJjMeRhdKrNuNcdTAwMWHDKVJcYkWCrP/v+e5j8uybe1VcdTAwMTDhzVD8ifNwp6T6jHaH5GJNYFx1MDAwMKVcdTAwMDHI2Ys18blbV85cdTAwMDU6Od7fv+TVg3vpbvbLjeFcdTAwMTnzlShcdTAwMDVkXHQkZ/Y2Yy1xncyZfyVyJtlpbJnaMvKeflx1MDAxZHHau/2K4apilFx1MDAxNzLo4d8miPtcdTAwMTareGyfuqpD+/X7u6Pw2m5sgIJfXHUwMDEyO5tBWpG5hEVcclmSyd6oYKa0Qkaa4WfGblLXo5TAZpJbgCmJXHUwMDA1xqZcdTAwMDWRTFx1MDAwMFx1MDAxYihcdTAwMTP8cSn0jPTrXCLANtNjJGBVSsbAK42tNZuO4krEwcL+0s7D/Jt0MWKu2/WmSWlcdTAwMDau4/XmXHUwMDBlzs6g5Vx1MDAxOew0SctJMlx1MDAxNVx1MDAxZnelXCJcdTAwMTm7VFAzqCZH6cTu+mCv33hqPTjPm7/PJX7CwY1TbuzOIGVmwrKMI4XLT8pcdTAwMWNMqLEv9G9Snvtzt3j7cv9e3emHQfX8Tqyc9bvXS1wiZUrAXHUwMDExKbwmiorEklx0TpVcdTAwMTY4MndqdoQ4dTlKiWvGhMWAdCmhhFx1MDAwMzQmcc2BslGGkqhFcG18dlx1MDAwNVhFXHUwMDFjpNBmKmkmTlx1MDAxNrBFxDJcdTAwMDaYzk3JlGKBPs9XboO4dsOt1PomL1x1MDAxOdi1mlx1MDAxN1baXHUwMDBmRZHzXGaWmiTn2dJcdTAwMTVO09ExK5OzXHUwMDExQZ1okqdyeTVcXKPy8njgXFxcdTAwMWV1Se1lr7ryuFby8TeCmvlLZtpMbOVyZs95ocnE2LQ54o+D0mJ6r0xcdCSnfFjRXHUwMDFlW/ykKKLYzGP9m6JcdTAwMTf43FnFT+Awar/R7Tw1j+qD1d7GwaZC7XxtlXTspIuiotoyOSBm8udE5UlcdTAwMDHFP3WpYa0wtoRcdTAwMDJcdTAwMGZUUPhPkylY8+JhjZk0jrNSSDNjXHUwMDBm4Fx1MDAxOL85JoMrMOhcdTAwMDL6tdVPuXfpYizd73i3nutcZmuCgVx1MDAwZosoa5pBTZPMnChR8cFsgVx1MDAxMpuJXHUwMDA0WNbETFx0zFxm3f1ms7mz5qygXHUwMDFlWf3ZXHUwMDFlMFxcvbZRuaErmMGmQPFcdTAwMDXJXHUwMDE4fFmLKqVokW4zRtJiXHUwMDE4fdAtiuslXHUwMDEy2npcdTAwMGaITVx1MDAxNC2/Q5lysCo0139cdTAwMDezXHUwMDE3/9xGrX12zPyNfTu4tIOfV+6Dd3+1rGC21uBGXHUwMDE1TcpcdTAwMDIl1mUoXGbbhOc5uTF9OcpcdGwuXHUwMDAx2Fx1MDAxOHBcdFx1MDAxNlxiV0iNXHUwMDAzm8FlsGLJzENAXHUwMDE2XHUwMDAxtqJmxqhcdTAwMDbcXG5pbpMtlM20ocRl5KTmZmTTf/yJg4FcdTAwMWGB3W1WnJop5nV9dje3w1xc88PQbyfS8lxmfpqk5Vx1MDAxNLGKXHUwMDBmaLPkgVx1MDAwN4Rcblx1MDAwMdubZsfvqXtcXHu8wnX74kBcdTAwMWY+N6u1weFlyU9elVx1MDAwMllSJtVcdTAwMTGb8LBcdTAwMDXWrimdTMfvQlxyXHUwMDA1poVbRFxc5ThiZsqK9Fx0ITRVUsXNcHz9t6u8+Of63vZJ/7T1++65eXDc2fDxplx1MDAxYSwrms1hXHUwMDFiiTwqby5WZiy5d5+D2lx1MDAwN0NcdTAwMTNlXHUwMDBmZ6evRylhrVx1MDAxMTWw1lx1MDAxOKhcdTAwMTdcdTAwMTFcdTAwMTZpm3qDtS5cdTAwMWXWUliSm2OEpFx1MDAwMPpcdTAwMTcqZi7+tKdMkFx1MDAwMGnNZMWv5OW8m3RBXvbCSuB2/VwiXFzkXHUwMDE5lDTFxVOiLMM3djy77XecOKTK5LpmXHUwMDA27EtcdTAwMDTKXHUwMDExqiY3x9Xn40O/22Ttjeb1XHUwMDAz2d1cZks+KkxKbCGiXHUwMDAxXCKmxJvpiVlhVFx1MDAxMctMoKFgiDCRXHUwMDAyVFx1MDAxN8iR4nljWsjiIELCxKHpXHRhXHUwMDE0IfhmRCHdOlx1MDAxOeZcZqVxV+32d2e109hsrj3uumtcdTAwMGZ7v1x1MDAwZmvVJUzlW4BqZ3Oi0ETxws+2XHUwMDAzVyyRXHUwMDEzNdNS6TylXHUwMDFi6etcXEqoXHUwMDAx11kmxKRcdTAwMTWliCk63kBcdTAwMDAg+Fx1MDAwNKhxXHUwMDA2SMNcdTAwMWEoXHUwMDExJDFZ07hcdTAwMDag6eZZxl+Hvn/p6XZCXHUwMDBisuhcdTAwMTHqOPPpdr16021cdTAwMTc1tnZcdTAwMDZRTLLipCzZOJHPy4mwXHUwMDA3XHUwMDEzq6w0R1x1MDAxYXOawymtrq1tefq3fOzJk1x1MDAxM3f7oHV7eVxc+vk0xFwijFx0JMzcuFx1MDAxOFLElplcdTAwMTDJNCqQXHUwMDE0zVx1MDAxMdSEy49cdTAwMWGqmFx1MDAwNO40NWLjolxuSYuZNLUgN9ohenxurbV/XHUwMDFmXFzs6v4lXHUwMDEy2+vHzSX5dcZOXHUwMDEzhXNYNIE2XHUwMDE5rZFcZoG3k4PC0pejnMBcdTAwMDCfilx1MDAwMlNccstcdTAwMGKRklx1MDAxM+ezwlxuXHUwMDE0XHUwMDBmXGZcdTAwMDUsSuBcdTAwMWXIXHUwMDFjz0pIrFs3jVx1MDAwYlxmtMtcdTAwMTFdRinD/FxmZvboom5ddlx1MDAwNmu4wDd26Fx1MDAwN0VcdTAwMWSVPkOzT/l2MfJcdTAwMTTNZFEra6pcdTAwMTGPK8VzlVwind2R3sb+xu9DZ4M01n5W9fpcdTAwMTEvOWJcdTAwMTVXXHUwMDE2NlxycDiWysDktlxikUJcdTAwMTeJWFwiNFi2MnrQXFyUwyz+Pl1xXHUwMDE4fFx1MDAxZH1cdTAwMWRcdTAwMWbFXHUwMDBi2oxnk8VkPOfmtFllPcxzf1x1MDAwZX5fP9Sv2NrT9UX3/Lh+dpSP07CEb6ZoTlx1MDAwM/tcInGWXHSsvFx1MDAxNoLmaIeJf+pSQ0RT2ILAWuBcdTAwMDWDMpBy0i9cdTAwMTPMolJcdTAwMTRcdTAwMWNcdTAwMDIhpm6fKsWITFx1MDAxYWpcdTAwMTTDalRcdTAwMTMt4LZf6pfl3aWLsdqw29dM/bFD2KhFUdtcZk0/SW2JQlx1MDAxNcxv0dOcpuiNgX2keI6SvLXt6vr1cVNXty7lelx1MDAwM9uD0z4u+bh0wKVljFFweuLCl4wpS5owh1x1MDAxOUtelvAlwFx1MDAxY2tEcCGpwlx1MDAwNV00dtTv7VxmVp/O11x1MDAwZntcdTAwMDHG9vXu4HRZXHUwMDA1MVxca0XzTD+bi844Tlx1MDAxZa4rwEnjSuRgs9TlKCVcIlx1MDAxNFx1MDAxMqbzinBCqClcdTAwMWWYaO5k4lx1MDAxM1x1MDAxMMHxsLnTVL9LXHUwMDA1bFx1MDAxNjd0ISbIqFxynWn1lVOGcu/Rxcis3nTr90Vx2FxmdT7V1DkhS8HUXHUwMDE1nY831clcdN6IUFGzZuZAoSOmxONg/0bxXHUwMDE23+tsdvrHXHUwMDFkuVSgOnav6S63dI1cYstMhk+IMlx1MDAwMlx1MDAxNCwuqChV6o1JQsCVK6Zpc0HuoquPd3utm6C7y8OrzYP6tr7fbWfirlx1MDAxZmlcdTAwMWZbcOpNYpwnqTFcdTAwMTcnRvMmU/P3NMNcXOSZR52+zqUkRWFcIlx1MDAxMJRzM44oJvVG8SdAbb7UXHUwMDFiM4N2XHUwMDE1+tJyXHUwMDE0Ialmi7ZXliTzNoMoPiPzlnj4lam2SMKp6dBAXHUwMDFj5zhcdTAwMWUyXW+Vk1x1MDAxMlx1MDAxNUKW0DixTUNcdTAwMTMwXlx1MDAwNZtZNrZcYk5cdTAwMTm3tFx1MDAwMdywXG5cdTAwMGJjXHUwMDE5cy5cdTAwMDR4lYwnXHUwMDFjgqVAasK+rDYlrf9cbixccjZX+iHjXHUwMDE5WOlZ7crYmVLmyF5KhkO5NXhkPHpw1OuZUsKCL4GbrFx0IUKIUZi+srRDsNKVwbjAZuaGlKbnXHUwMDBlS1x1MDAwMVtzSl7wq7RcdTAwMDRccj9cdTAwMWNcdTAwMWNcdTAwMDdPhnPJO8dRWGNXP/NcdTAwMWOslUSIvF5cdTAwMWShY/RJ36J/5taMhOBEXHUwMDBihiBGXHUwMDE1k3mOzEkviC6lXHUwMDA1XHUwMDAz+LXAPmGIvtbJT1gwZswh0Vx1MDAxOea+LHQuIDHamVxik3lcdTAwMDUjhsVcdTAwMTgwmFx1MDAxMUvzt7bzaeeBMUzBxy6fblSIiPnO1MmqXHUwMDFinbvto0168XP7qVx1MDAxZOzWXna8h3ovTtWY4/ZAXHUwMDE0iczUXHUwMDE1XGbmqI4qkrfj9qiFJpy25SnF9Fn940pcdTAwMTFcdTAwMDPqKFFcdTAwMTRcdTAwMThPmcEk01pcdTAwMWNcdTAwMTOLvo2iLfxswC9TiInAMD8rY5jIqVx1MDAxMWdcZmVGidltjYQpYs+uXHUwMDEzb8VhqyZcdTAwMWa8Q1x1MDAxNax5XHUwMDFiXHUwMDE3br+7XHUwMDExXHUwMDFjlFsnYqyZhcEoZibaKKZPXGLEyGLD2Y9SkFwiXHUwMDBmOpbYXHUwMDAy59GMu3pNY8eUpLApbahcdTAwMTWghi6jI3+uhqG0XHUwMDAzxlx1MDAxNFx1MDAwNb2TQ1x1MDAxOaZu4eQhyYnjJDinQsC3SjPv3uOjNbsuNlx1MDAxYpf6Qlx1MDAxZjflXW1wtnZZcmdcdTAwMDd2XHUwMDBisVx1MDAwNJJcbtyZuORcdTAwMTVcdTAwMTjFXHUwMDE2MmeJzVxu1S/C6cpcdTAwMWOiqWAvxqac6fSuxWD8qqXU3s9cdTAwMTmPXHUwMDEwnFx1MDAxOFJcXPhcdTAwMTidzIdz77lccjdcdTAwMWHDXHUwMDFli0e03Nvx3Z25NWZM3sm4w+Q9M8VcdTAwMWSImFx1MDAwYogkeVpcdTAwMTNHZqpcIpHZgYhEwzn+SY/5Q/X6iTz0tX9ElluXX1x1MDAwNFx1MDAxMKUpUIJcdTAwMDeNz1wiXHUwMDBmJ/8viUeSI/FYWpIkXHUwMDA04tlUXFxcdTAwMTBcdTAwMWNNLMBs/bqBLkvCYfa44Psw8MpcdTAwMDeOfnX84O2EqM+F57QolVx1MDAxOEkygZamXHUwMDA3XHUwMDBiZ1x1MDAxYzRJkpPdSnGMpMyRRENbp8HlnX5+vHFudlYvnbP12lnnL25cdTAwMDKCacZcdTAwMDRYZXRcdTAwMDZ0M8x/WL5cdIhB1yhcdTAwMDGo/6pzp77cXGJMa1xuo5hSYkZqZN7Aq1xydCVPuzd711x1MDAxN/ZWVdzt/l7dWe4s7i8gXHUwMDFmXHRcdTAwMTbaknZwSvkht4hcdTAwMTYqzlx1MDAwNpzmXHUwMDFlJVx1MDAxOUI6Opjii7gnT7/HkrinXHUwMDA2XCK2XFznVydV838qXHUwMDA3vYn01VxcXHUwMDA0cE2CskRagzeXo2ns+eLZv7lcdTAwMTe3j+u1zkNHXHUwMDFjdevX+62/Nlx1MDAxNVx1MDAxMUosZoJDmKRcdTAwMDM5w8yDXHUwMDAyqFxifEhm4nj/tvFcYi6Tj/RcdTAwMTSUXCLwXHUwMDAwVHYqulx1MDAxOez3boJcdTAwMTOPoaub1nqbXHUwMDA2R6fPy21cdTAwMTX5fCpcIpRZekk7OIWKcFx1MDAxZT+ImUFrnNCvq4/4dC6igVNcdTAwMDHsh4NcdTAwMTI4QjGyXHUwMDE0xT6JxU0qpcXLXHUwMDFj1iG0XHUwMDE4XHUwMDE5mbNwe6D0oHXsbzclulrdYs1DpMLljtD6fNxSZIrkXHUwMDAxUmJcdTAwMDZuXHUwMDE362AhVjbzXHUwMDExa5PBVGRcdTAwMTmzsv4qmO333GClXHUwMDFi+I+eM7TUXHUwMDFj91cn9Cszjlx1MDAxNy1cYrTTwlRiZVlcdTAwMDZqUyqASfJMWkVcYjezWDPDluuD+5u7tW5r83lFVzd7jd72/knZ7UWlLVx1MDAwZdhcdTAwMDXrXHUwMDE4duN0sVx1MDAxM0KWOalcdTAwMTfjXHUwMDAyi1x1MDAxMlx1MDAxNbKEXHUwMDE5Nvt+TMtoQ0dcdTAwMDBsMVx1MDAxMZPNN+ZcdTAwMDD8rlwi5St1+iSLUSWnXzHiUjPGI9w0a1x1MDAwYq883lx1MDAxY5/csNb54cnOxtXdyZrn2MstSvlcdTAwMDLmMdNcdTAwMWSJkFx1MDAwMGecyjxCMuzo+WvYqVwi8e3FMfTDiVx1MDAxMkJcbv5vRD9DXHUwMDFk/0dcdTAwMDX2kNv71XlrxFx1MDAwN91fXHUwMDFifFx1MDAxMVx1MDAwNUVcdTAwMDWqzJBnXHUwMDE5NOS2Wl63XHUwMDE3XHUwMDFmuEhpRFGaKi7yzKVqbfv4p4OuV/HzPutd2c+PjWrJZ6NjrLVFKGdEmnqjmFx1MDAxOazY0nBVzFwiokVAXGb3wJrTWFxmU1x1MDAxME9PhCffQ5HAQIgu5fShcnNQylx1MDAwNmYkpVxuXGJzRGmOMqD67Ynf23p5fOg6R4PG9tX10fauW+79XHUwMDBi7oQlYP/i+Fx1MDAxMYZATdk6XHUwMDFlv2D7XHUwMDAy2CjskmV0dyx9+1xuqUmeavG5TCiMXCIzhCadXHUwMDAwhTVYwTq7XHS1/fts7+TlufNzXTz598+76/tH/nJcdTAwMGZKL8CEQoRbiIFDXHUwMDFjP6GFXGJhUamQ0lx1MDAwMszzgrYvx1x1MDAxNifZx06YXHRjWqtldLD/VSwoXHUwMDFiXGafW7tcdTAwMWUmNetcdTAwMTZkJ8XcNldcdTAwMGLSt7ea1e92t3tcdTAwMWHCXG5+aFxu+Mo8Z2JU8PdHz31aS943395cdTAwMDD+fWi3mY/689uf/1x1MDAwN3ZcdTAwMThPXHUwMDFmIn0= - unified storageobject store (local or S3 bucket)git repo(github, gitlab, etc.)Other SoTsState / AssuranceDataInfra MgmtToolsGraph DB(neo4j)infrahub-syncgeneratorstransformationsschemachecksartifacts3rd party containeror systemuser-providedcode to infrahubcode-generatedoutput from infrahubinfrahub open-sourcecontainer or systemLegendinfrahub-bundledcontainer or systemmessage bus(rabbit-mq)cache(redis)infrahubdeployment systems(ansible, nornir, terraform, etc)nornir-infrahubinfrahub-ansibleschemainfrahub-sdkGraphQLAPIinfrahubctl \ No newline at end of file + infrahub-sdkinfrahubctlInfra MgmtToolsinfrahubOther SoTState/AssuranceDataInfrahubSyncobject store(local or S3 bucket)deployment systems(nornir, ansible, terraform, etc.)nornir-infrahubinfrahub-ansiblecache(redis)message bus(rabbit mq)unified storagegraph db(neo4j)git reposchemageneratorstransformationschecksschemaLegendinfrahub containeror systeminfrahub bundledcontainer or system3rd party containeror systemuser-provided codeto infrahubcode / filesgenerated by infrahubartifacts \ No newline at end of file diff --git a/docs/docs/media/release_notes/infrahub_1_0/1_0_ui.png b/docs/docs/media/release_notes/infrahub_1_0/1_0_ui.png new file mode 100644 index 0000000000..9cd91c9843 Binary files /dev/null and b/docs/docs/media/release_notes/infrahub_1_0/1_0_ui.png differ diff --git a/docs/docs/media/release_notes/infrahub_1_0/permissions_1_0.excalidraw.svg b/docs/docs/media/release_notes/infrahub_1_0/permissions_1_0.excalidraw.svg new file mode 100644 index 0000000000..a7effa552b --- /dev/null +++ b/docs/docs/media/release_notes/infrahub_1_0/permissions_1_0.excalidraw.svg @@ -0,0 +1,21 @@ + + + eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nO1daXPbTI7+nl+Ryn6dcNBccvQ1VVtbtnzf8W3vTrl00LZkXdbha+r974tWXHUwMDBlUVx1MDAxNGlJNpVQ9VpO4liUpWY3XHUwMDFlPFx1MDAwMFx1MDAxYUD/59Pnz196z+3wy78+f1x0n8rFerXSKT5++Yd//iHsdKutJl+Sg5+7rX6nPHjlba/X7v7rn/9sXHUwMDE0O3dhr10vlsPgodrtXHUwMDE3691ev1JtXHUwMDA15Vbjn9Ve2Oj+j/93r9hcYv+73WpUep1g+CFfw0q11+p8/6ywXHUwMDFlNsJmr8vv/r/88+fP/1x1MDAxOfxcdTAwMWJcdTAwMTldJyz3is2bejj4hcGl4Vx1MDAwMFx1MDAwNZCIP73Xalx1MDAwZUYrNGhjSejhK6rdXHUwMDE1/sBeWOHL1zzocHjFP/VluVej9m3nas+s11x1MDAxZVx1MDAxZk9PlO7eXHUwMDFmXHUwMDBlP/e6Wq9cdTAwMWb1nuuDcXVbfDvDa91ep3VcdTAwMTeeVSu9259cdTAwMTNcdTAwMTd5Pu23Oq3+zW0z7Pr7XHUwMDFmXHUwMDBls9Uulqu958H9wa9nv0/Cvz5cdTAwMGafeeKfyLhAXHUwMDAzXHUwMDE4XHUwMDAwjfxcdTAwMDXDd/G/j4RcdTAwMDGisEpYY5FcdTAwMDSZ2MBcbq06L1x1MDAwNVx1MDAwZuy/5LVcdTAwMGKJhkMrXHUwMDE1y3c3PL5mZfia68Fj+JrHXHUwMDFmtytcdTAwMTBcdTAwMDJphVS/rtyG1ZvbXHUwMDFlX1I2ME46bY1cdTAwMDL/XHUwMDE4jq9cdTAwMWJcdTAwMGXWgZwlJSiyjv5T25uVgUz8ezj5XHUwMDFklqZN/1x1MDAxYs1+vVx1MDAxZZ3BZuXHXGb+lJ2h9OCPZ/5cdTAwMWHeln/9alxc6qKSN1wifb3wqffrfiOSst7ZuSv3KiuXpY1y6WT3Zu2oe1X58ut1f/3433D4/Xal+F3whEHgLymlcPrX9Xq1eVx1MDAxN7+3eqt8N5TVT5FcdTAwMWJcdTAwMTlDycg4I1x1MDAwMHFcdTAwMTLT8EFCoFx1MDAwNCvU1PBIvulcXMNDgVxuQCskXHI831x1MDAxMJmNXHUwMDAxPJRcbqS0iFoqnFx1MDAxZjxcdTAwMTiiqIRTjj9LXHSl5ThMUMVxISxZI7V178fFyIUxXHUwMDAwZCmjw1G1mr2j6otfXHUwMDEwaUeeXSs2qvXnkVx1MDAwNVx1MDAxZFxi70C++Fx1MDAwNr+MPL1Ur954Of5S5uGGnVx1MDAxMVx1MDAxMe9VmVJ+vaBRrVSiJFHmzypWm2Fnc1x1MDAxYdXe6lRvqs1i/Th5KHzn4cbPtVx1MDAxMkFE0ZWK3dBf9TdKr0L0dVwiXHUwMDEzRsaf/kVkaMGwXHUwMDAwwfRILXVt84CeNnbXN3FZXrnnjbPtjXwjlSxcdTAwMDSCmEmMMVx1MDAwMlxcRO7971x1MDAxM/NcYsNcdTAwMTiZxYzzZJaOVFx1MDAxMfqv15FakteyVEogMmlcdTAwMDOrlTbIaFx1MDAxNaxcdTAwMTKGwv9cdTAwMGKqRlx1MDAwNShJ8iDGMEtSglx1MDAxMVxuh4NbXHUwMDA0Lqvs32yfa71ll69vj4/1TulAdZ5zyGVcdTAwMDLwXHUwMDE1Y09cdTAwMThFQpqpIZJ817mGXGJzR+CsXHUwMDE2ilx1MDAxY6FCbUchYlh2+Zo1fibeXHKRNDJTJuCpdsZpQVJcdTAwMTkrxlx1MDAxMTJOZlpqieBcdTAwMWO+XHUwMDFmXHUwMDE4i0Jmh62ops+UyyZo9ziXjY7kd1BZOkxcdTAwMWRcdTAwMDJcdTAwMDE6nFx1MDAxZafqfPNkXHUwMDEzi0f3NfVolnbam8tLzetcdTAwMTSc3lx1MDAxNsu3/U7455FKXHUwMDE2XHUwMDAzsNo5cEjOs9ZcYlQlXHUwMDEyc1x1MDAxZDOZIdTKREzwNyC11yk2u+1ih4UqidBMoKV1oCU5/6BcdTAwMDRcdTAwMTeNPUjURvx00YZj/YVevoIkM0Dvb6S1k8227i5cdTAwMTd2O+L6/uxot/d0u7Qsc0lrMt30Y9+FjFTKTY2X5NvOPa+xXHUwMDA3xjZcdTAwMWVcdTAwMGKZ0ESjYCFcdTAwMWSQluw3WSHeXHUwMDBillRa0zJQ7KNcdTAwMTmGpVKko8bda06aVE4zsdHfiNhOulHuypTYJuj6OLGNjmT+xEb4io9GrDmdmoHYepv793dnhVOSK9pcdTAwMTb11X1xWdTyXHJUNuJcdTAwMDKQ7Fx1MDAxZFx1MDAxOcmKSUWm+GewkVx0j7HK6OGvV+xPxllZvlxyqNJHXHUwMDE0tfQhRVx1MDAwMEJ4U9DRI4Y9tVxioFx1MDAxN4HRdttrhZ3G1fZB5Vxm3IFdxc5BweWR0Vgjplx1MDAwM8VJa6XR01x1MDAwMyX5tnNcclx1MDAxNCNkYJBlTDknXHUwMDE1uFFPzYdcdTAwMWQtX/LMXHUwMDBlc1x1MDAwM8rboo7SXHUwMDEwXHQhPqKOmVx1MDAxMNpcdTAwMDRcdTAwMWT/x6OOmFx1MDAxZVGRhqWG3Ybpg45cdTAwMGbFjZ3b0l2jcnF5fP10V7s7abTa+capZsuTbUpCUqCdXHUwMDE4XG7d95ijXHUwMDBmdUi2P5FIXHUwMDExRC6/xfIsqopN3jxTXHUwMDAxXHUwMDFhRipZdsM0JuDU6GA82Mj8ZkgzVlx1MDAxN4rDjlx1MDAxYnedyn1bdS/XLndP7vTJ8s3RToTD/pH8tj9idndcdTAwMGbbJ8c7u0/rz4W9rytcdTAwMWLP+5WHs9FP+fn5xU6n9ZhHblx1MDAxNDaVXHUwMDFiXHJaQ+ysT1x1MDAwZrnk2cw35Fx1MDAxNLEmM8CeXHUwMDFlXG4tXCJRyp9BTGBezFxic5lcdTAwMDYxXHUwMDFkOXZD3d+JXHUwMDFi51x1MDAxOMScwFx1MDAxNn88iGl1/OmfMLXOx7LVXGbbcYgnXHUwMDE0XrVLXHUwMDA3y7R7b+5cdTAwMGaWz/C8mG+YWkeB88FcdTAwMTih2Hi0MLpcdTAwMWTHfFx1MDAxOZDWftNcdTAwMDUmbJxfgy1cdTAwMDO8Na9EXHUwMDA2YJVD8z14mVx1MDAxOL2c5OsxyJ1hXl0omjy9e+o5fXhTuO/unj5cdTAwMWScPlZKzcs80lx1MDAxOURcdTAwMWO5XHUwMDE4TtjzUNZE9eUknCTfda5xwlx1MDAxOFx02KHVXGIsgSgxln/FRINcdTAwMWFcdTAwMDVMzr96XHUwMDBmTt7k6jl2Q4Vx9o+HLs1cZkKaX1dvgo7/466eTLc7UTo2uKKpRlx1MDAxMzPBVKu2Xl7fXFxWx7fN6spRL6ztVvNcclQtZUDWOf/H25/xXHUwMDFkOVxmpGBcdTAwMWOTZK5BgTY2sGzszky244TP9rRCwWJR2rf9sHJy/LBz2jhdUeHeXHUwMDFk9l5e9mentFm0xdsoTVB69JKVPFx1MDAxYlx1MDAxYmpcdTAwMDZOS77tfENFqVx1MDAwMKQlxVx1MDAwZan3kUajl5JtP4VGStQsw3ODyts25NgsJzDa/HFcdTAwMWbt95HaXHUwMDFj9+MmqPk/vVx1MDAxZodcInU/XHUwMDBlWWDYXHUwMDE4ijj2k3Cqv8GualY2Lm63ji56rdrFc6ma8+il8b6PXHUwMDE1gm045jRnYjhl58mCNWBB5p3SpN/MU1wiXHUwMDBiW/Q3MtrOSq1wXa/0X1x1MDAxZc6ezpw41Fx1MDAwZjvqNJeMlp5cXCx9uu1MiZPJd51voDhcdTAwMTEge8NaWVBKUsz2Y0LjXHRcdTAwMDCf9Zs7QpOgeIW0c1x1MDAxOYT5P1x1MDAxOG2Slv/TjCZdajTFkdJupMBrXHUwMDEyUOsvvX5ono626aZ2+SS3Tlx1MDAwYpVWzp00NjdcdTAwMDNccopcdTAwMDSjJInRKFx1MDAwMIdcdTAwMGLhpKFcdTAwMDS+XWlcdTAwMTerXHUwMDE0YGv1aWl/7dtcdTAwMDNumcJ563Cti4fiKpeMplRcdTAwMWFQlFBcdTAwMGUs4vTh+eS7zjVQXHUwMDFj+mpcdTAwMTm07KSpJEbTXHUwMDAxc3o+XTRSjGwls1xiyH9cdTAwMTDaJCU/f0L7vtGeiNFI2lM8jlwiXHUwMDE4okpHU58mgfTs7uJaq6W9q4JYbX89K5ysVSjvxackXHUwMDA2kVx1MDAxMnQkhVx1MDAwNFx1MDAxN8trtn6rm8lMK+mrepBiXHUwMDAzy6b41GtcdTAwMDIj+Y+14IvaXHUwMDEy9lx1MDAwNjRcdTAwMDbGoWFtYnztW2TL/We2JFx1MDAwMmvULDa+x7lMTuayuVx1MDAwMLnbK3Z6y9Vmpdq8if9K2KykXFypXHUwMDE3u71Cq9Go9nhcdTAwMThcdTAwMDetarNcdTAwMTd/xeB9lzwobsPi2Fxc8DtHr8XR0/bvOMrfw/99XHUwMDFlitfgh1////c/XHUwMDEyX+3zLNDvXGJcdTAwMTFcdTAwMDKxLWOjv40qXHUwMDE4XHUwMDE0n6BUrL/JKT3p7VJcdTAwMDVpcDEmQsM3+1x1MDAxNP3+XHUwMDA2JaLTXVfh038lqulN4sd253y3v9PYW281L+6X1rfra6eYbyXCk1x1MDAxObBcdTAwMGaIxmtcYmbOmFx1MDAwZaHAXG6wvLzKZ4+pV9Jl3pNzXHUwMDFkaGO1RVwiXHUwMDEwJFEm6Vx1MDAxMFx1MDAxYlxmTFx1MDAxMdZmjq14NVx1MDAxZeJcdTAwMDFUbNkrMZdcdTAwMTjPh1x1MDAxNpmXXHUwMDE2SV17/1x1MDAxOFv1rHBvIdW+XHUwMDE3isgnpMzQ16XRqtk6vcgte/fyVD5+OO8/7dzlXHUwMDFi9laKXHUwMDAwtWDVTEL7XGJuXGb3MiBCZ5Enwkas/yxRXHUwMDBmwSBcdTAwMDdYXHSByoDBiFx1MDAxNzHMTIWA9Y5jXHUwMDBix/fQkGrMdnBiYFx1MDAwMc2nJP5cdTAwMDP180J9+tr7x9iqZ1x1MDAwNfvoxupcdTAwMTjbM9lcdTAwMWIjZihcdTAwMWN56G+tt16ueo0t0Fx1MDAwNWdPr85L7d18w55ccquAQDskJUG60Vx1MDAwMitpdeBcZs+671xm40w6178nl1xi2NpwXHUwMDE22NpwXqs7MFx0sGe974tSmVx1MDAxMIyQPlE3XHUwMDBle6+2nNZmLsnpXHUwMDFmsJ9cdTAwMWbsU1x1MDAxN98/xpY9M7pcdTAwMTep2bZcbqQvSpqhXHUwMDBlpbN8uW/2e7Xr8lx1MDAwMW223UV9+3ytm2/YK2NcdTAwMDIljXIk5CCiXHUwMDE2w71cboQwSrOhj9qQSI9cdTAwMTS8h++1XHR4zY0yliz55U+ke/T7hchOh3Q2kjH2KzdJO2FtdD/xXHUwMDAz+Fx1MDAwYlx1MDAwMPzUtfePsVXPXGb2Mj1CqJ0ky1x1MDAwMj893W9udeDrVVx1MDAwYvb1s7zdR+y6Z7uab9wzVtjK14ZVKoGLhGiG3r1lT1x1MDAwMDRcdTAwMDFb0faV1lx1MDAwN+/AvcJAWUHGR1wiQWmbZOaLQFvhQPB1LfU43zPgtTNcdTAwMTbEXFwqqj9wPy/cf01dfP9cdTAwMThb9qyAL0xkXHUwMDFme6zniZTs4msz9Gon9m39tlx0l/XLdkcvN3dezo7C0no95ykpimRgjN9mtk77VrUjyEenXHUwMDAzMsKhNKDQmXTgT7M1kFp5XHUwMDFhsFx1MDAxOce2niFyZEDCXHUwMDEwJFFL3yjWQH4nUehcdTAwMDRcdTAwMDefXHJcdTAwMTY2XHUwMDA1UdpcdTAwMGZTf7GQn776/jG27plB375cdTAwMDJ9so6HY2aI6Fx1MDAxZm3WXHUwMDBi2/tbstHqX2zfXaxvPi+FrXxDX1vWucTkrn1cdTAwMTWsjFXAomMnXGadNd5cdTAwMTCTyuL7SD9cdTAwMWT7zNXOXHSJIFx1MDAwNIFNqjqnQFx0XHUwMDAx1lx1MDAxN1xugc+3XHUwMDE5gz7fhSZrP6C/cNBPWXz/XHUwMDE4W/ZcdTAwMTmRPyHDzaSa/Fx1MDAxNr2Lodz0vH/drlR2e83G/dP+Sa+hbl521rdz3lx1MDAxOYb5PrA+jY9tLjaCaDTC51tOMOZ9oNNJq9+dt1x1MDAxM5adS8A+ylx1MDAwMFP6tb/S3VaCRamyydz5jTlt5rlycnnWuzl8+Vq/fGlcdTAwMWZ2a8tcdTAwMGVymdOG6TltTFx1MDAxNCpaXFw6XHRcdTAwMTnJN51rZDhcdTAwMWMkrYE/ssFZq2LNWIxcdEhcdTAwMDKbK8LvR84po+1NfSFcdTAwMThcdTAwMTTAPrz6XHUwMDFiZWjPsS/EXHUwMDA0nT7/vlx1MDAxMKmWq3ap+HTGILtBYvpg1XLYMLR62S923cbqTlx06b66eptzgGpcdTAwMTO4QVx1MDAxMTt/MXvESt2dXHUwMDBi2KRl835QySPSXHUwMDAxOt3uVDJ1QcDIXHUwMDA0yVrCXHRcdTAwMTBOyoRcdTAwMDbtWlx1MDAwNbxU1oFhx5qt00jN/Y89aa1cZrj5NE76sFrnZrWmr71/jK16pmYrvlJcdTAwMTNslTFcZn2a3m69LPXvb9bE8WZRmdPGkipcdTAwMTZ3njbzXHJ+o/0mgC++YLpDgaNOq7dbvTVrPW9af6TKO+m57MqJhitcdTAwMGbCeb51wjn2nlx1MDAxM+zXxFZp4DtdsFZarH6f8LTbPul8vd+W+mSvt+Mu+zxcdTAwMDF5tFxcZXq2XHUwMDE2SadcdTAwMDDZoptcdTAwMWFcdTAwMWPJd51vcDhcdTAwMWSwyVxufrKtiEHD2EBcdTAwMTmdXHUwMDFkNDK0XFyZyoFcdN1mcfjWh+k6Sa3/4ZZmPp8oXHKlhlx1MDAwMFx1MDAxMVx1MDAwMWawX1vmeHetUmtcdTAwMWaftjvll+fj5tdmId8oJUuB0EKA8YHTaJeP51x1MDAwMYBcdTAwMDb58o5JgrR7Leo6XHUwMDA1SkNcdTAwMTeWw1x1MDAwNJRatqA1+MbVWlx1MDAxM7NYgvWqhoP+6V/y2lx1MDAxOGVhsUpcdJew2K61XHUwMDBmto528WFvufi0WVjZXpq20edz4+CaXHUwMDBlsX9XO1uqXHUwMDE07dpV92DzdPRT3tnoc/6kyEZqXHUwMDFh3JDFzJGS05Ni8mzmXHUwMDFjbo5ccnZUaEE7ZkerY3hz2eEttTGaXHRAKbZajVx1MDAwNKa6pOInXHUwMDFhw5vwiVZcdTAwMDKiu65cdTAwMGLKipGsukmd0eqtUrH++f+aXHUwMDA3YadR7Vx1MDAwZWR4PiQ5gTjG+qR9XHUwMDFmWNKwpmJMfFx1MDAwZmOqSNp9fLOC0Fx1MDAxODNcdTAwMGJjXHUwMDE2+kb3XHUwMDFmzi9cdTAwMGWXtixsmFBcXJ01XHUwMDBl8lx1MDAwZWFcZlx1MDAxNEqBmj1cXClxtMpYk1xmhG88I40jXHUwMDEw0c6bb6HMYqVSMYmUaaVmZWr8mVx1MDAwZiSmokzpXHUwMDAwhJXzST+e34lFtceL3eVVKvdcdTAwMGLy5Fx1MDAwNFe7fbl1nUNqk2hST5U1lt097WagtuS7zjkubOCEP4XEXHUwMDE45Vx1MDAxOS5cdTAwMGVcZpMhMDLmNuNHZFx1MDAxN79B2vTctl+qsZb/XHUwMDFk3DZBxce57cfA5sVtr2TfRWLiY2U2goyYIVx1MDAwMef1nvy5hK+TOrCMYc02KFxiXHUwMDE3q7JBR1x1MDAwMVx0x85cdTAwMTk5n6GTXHUwMDFlr5lmIyMt/8bHKVx1MDAxOVx1MDAwNVJp6/tcdTAwMDOrpINmReBI+t5cdTAwMDFcdTAwMGW0XHUwMDE52fL8mWxPVls2rz9Kalx1MDAxN2snI3Xx/WNs2Ydv9yn6/W0mLVJq/o3xXHUwMDE1fVx1MDAxON3Xm0jdl25VPOpObemob3lldPN8v5lv7CunfXqTkb6MxsSxr3hlyNcxXHUwMDFiXHUwMDBmfmteKbT53VEgYclcdTAwMTCPXHUwMDFlXHUwMDE2y6bdWC+sNK0obe9XrjbgunxcdTAwMTVcdTAwMWXsrU1cdTAwMWJcdTAwMDZqPq+HN4VDXXz8erBTg7a054ePo5+S9zBcdTAwMTB/SmrUVfsoJMyQ1ZM8mbnGXHUwMDFiy2vg3TZlpTXa4GhcdTAwMWS7slwiQ7xlaymjctafXHT1N7KUf19cdTAwMTRoXHUwMDAyc+QqXG5cdTAwMTTNb47v/TOFXHUwMDAzOTlD4s9NSLXy+lV3zZXvnzdLV7ftrduc91x1MDAxOVa+KyNcdTAwMDGRXHUwMDAz61x1MDAxNMVcdTAwMTJ/NJE/XHUwMDBixkqhhSaboyhcdTAwMTA76NqfizGXvlVzY8xcdTAwMWIoXHUwMDBielRcdTAwMTeXpW5v+2HTLG0rzGNcdTAwMGZGadJcdTAwMGZcdTAwMGa0bKn4XGLI9CkxyXeda1ho8KfhMq+wXHTHlrrUMVi47GCRKbNZJLY81OJcdTAwMWb8ksdcdTAwMTDQXHUwMDA09f6bQ0BcdTAwMTN8QZ2e1CZ8XHUwMDE2tpylyZI4LZzaneuT6jb1N/v93dX+TaWcb1x1MDAwNFx1MDAxYnCBdb6NXHUwMDFjSYtKjmbuqEHKuWRcdTAwMDfdsTeOkX5cYjlwXHUwMDA2NVx1MDAxYX9cXM1iJbSVqmcnq6trW7tcdTAwMWR47jds7666XHUwMDA1+38rZ1Clm5K+3or4a/pGJ8nTmW/EXHRcdTAwMTUgoW93XG5CSlx1MDAxZMtcdPBtzTJDXFy27iCxLWktZVx1MDAxMX5ZXHUwMDE01vx97uBcdTAwMDTuyJU76GQ6hlx0lSUzQ7Lr4fmLva71NlxuxXO9ffZAYVs+5Lx82YBccixcYs1cdTAwMDBms1e7uDcoXHUwMDAyxfYuOnbXpIX4uP6gO+hYvYCJNi1aXGLObJftavdy+/T+8Vt346xfflkp1fLIbcak41x1MDAwMlx1MDAxNGglXHUwMDExpvdcdTAwMDeTbzvfwFx1MDAxMFx1MDAxNFxia1x1MDAwNDt8Tlx1MDAxOOfi/qDOXHUwMDEwXHUwMDE4mXKblMYhW1x1MDAxZlx1MDAxZlx1MDAwZeHwRZlR21x1MDAwNFxynyuH0KbvVVxi0EhWO5rePIXVdvH56aDSPLkvbsHafe2o9i3niT1WqkD6RkfaN9eFWD+uXHUwMDAxt0lf/aitMyDfV8mRKbdcdFx1MDAxZoNCq+bTd29+tfk3d7dbJy9bXHUwMDA3p7JcdTAwMTL2i3T50rt+wzHX8ye36ImSY1x1MDAxOTNsXHUwMDEyXHUwMDAyzEJuybedb2SgL7+UPOG+U1x1MDAxZKp4xpvOXHUwMDEwXHUwMDE52UY7XHUwMDE5QiPFmVx1MDAxZtyWXHUwMDE5t03Q8LniNpfeYlaD71+G0+O3XtlTXHUwMDA1acVBdenuyZZ3Ko/9m2LO8cvMxr6PMsI5cFxuh5/8PdRpXHUwMDAzf/wmWq19osn7umtkm8ptfX6t04tFbL3wXHUwMDE5cNuuXHUwMDFhvD3cXX/Y29ktXHUwMDFmtqaNdHbgvFRcdTAwMTNl/dS9OG2e6Zu1jfO95Vx1MDAwNYt0XHUwMDFhnVxuN2vYsDJGTVx1MDAxZiVJns1841xyRcC0XHUwMDAzilx1MDAxZEHfOCm2teBcdTAwMGIrMsNbtoFO4ds88bj/RtVPv48xJzBHrlx1MDAxOJO9iVRvUJNTLNoz5IperH/bck+l65XL1ZV27+z02Vx1MDAxZZRzfvRcdTAwMTIjJ3BorFx1MDAwNvBcdTAwMDXDXHUwMDEw50xcdTAwMTM4IXgqJKFcdTAwMTHwzorhTDlcdTAwMTNcdTAwMWOxUtFysTq1VW42S6V6vdBqnDz1WzvXnbWVZnta0nxovtTCzsFd52Gf7o5s9faxtFtYNNKEdMR5L1x1MDAwNlxiZuiHnjyd+UZcdTAwMWNRgFx1MDAxMpE0OCtcdOKsKTNEXFy2IVRcdTAwMDDt/3zUVUVelFx1MDAxOWtO4I58saZIra1cIkFucI7a1Fx1MDAxMP623928Pnva2ulsnZTXOqd6K1Q5P8qEXHQz8OeWXHSjWFx1MDAxY0GOkqZPXHUwMDE2XHUwMDA1oVx1MDAxMKU/YMZSjrZcdTAwMDeFs+xcdTAwMTmjm89RZXMjzbPTx3phqb3jjm5cdTAwMWHuUmx/7ZVvW3kkN63TXHUwMDBm+7BaOe1TPqZGRvJt51x1MDAxYlx1MDAxOcT0xbdcdKgkk1xcLNvM54tmh4xMyc2QL1x1MDAxYnNcdTAwMTng4oPa4tQ2QcPnidpcXHpIx7co0k7NkPhSutwqnO1ff3sxR5Xnb9vqsVx1MDAwMptf841fMjpcdTAwMTBcdTAwMGVYIPm1XHUwMDAwXCJWyqSFzyX1wVx1MDAxM2f4b/rpfFNcdTAwMWPZ0etcdTAwMTSb3Xaxw8I1jmFcdTAwMDFcdTAwMTToQVx1MDAwMp3wp4qDTWiDKFjXaKmtNNpcdTAwMTjnRlwiTD+yYay0TNLzOY53XG62+22E9HpjT5Hej15qQUxKdnqPq9jf31rphFt7V63Tr+tY0Dudk5yfUe+kXHIkWlx1MDAxND5cdTAwMWGJhFwiJtRcdTAwMTigYmOOpZu9L0pPyJyiXHUwMDE2/jdcYrXSXHUwMDA0vqr6T9XI5kSoUaRcbrXmgYCJztDEw5NXXHUwMDBm1dnKQ2nv+GDDiO21ZbEsc57Xr6xccjT4lq/Gx7JNLMuYNbWTRqHTXHUwMDEyIXr00lx1MDAxYlx1MDAwZVh5VailoEAhOc2eiJL+tJckoVx1MDAxNlx1MDAwMaIxvv6VRZfEeJtcdTAwMDcv1E6bP9a9NidSTS49O0kpQvSdS6ZcdTAwMTbrl+Xb1eeL3UKtdLrXKnYqXHUwMDE3oFd6+Vx1MDAxNmsrMDAsIWhcdTAwMDFcdTAwMWN7TLHeXHUwMDA1mlx1MDAwMuNcdTAwMWLRayn8WXzpOVx1MDAxOCFcdTAwMWL+KN6jq9m/RiPZkzFa+L7KSWJccoEvjTPCgj9YMlx1MDAxMlx1MDAwN1x1MDAxOIo1M4rMpM1evsU6tVx1MDAxMVx1MDAwZqbnJKBcdTAwMDJ2u+xcZoXFr/fMzKVAK2I9Tdqy0KK3nGNcdTAwMTa1MGxcdTAwMTKwq8py5Fu2pnvE77WoXHUwMDE5VtLn7Voh+dOETLA9XHUwMDE0XHUwMDA1Qvqj0CV76MogjVx1MDAxZoEpfOt5XrO55Jfnvlx1MDAxYk9kyOH3QU3T79FPR6vc9+OHwE8sSVYsrOHYXCKNnEH75abY9pNcdTAwMTD4xExcdTAwMWO0i7BAZO3YrCxUXHUwMDFmoFS5849xiVx1MDAxYr7fp+j32Vx1MDAwZjJRqWqHTW/pWFpmiMS9XpeXS72jXHUwMDA3XHUwMDAxYjRg2Vx0XHUwMDA2/jaqdySw3lHExlx1MDAxZlvLJrLHnbV9aFnkpW/pw3zONntk0ofROFx1MDAxN1x1MDAxMPpcdTAwMTNRgVWhZCNwXFzvOMk8zCz8oXeieuf1krKI3vGn4Vnyp5ZcdTAwMWK2qdgvXHUwMDE4nmH8S/F4w4vIq3fn2J5nK2uhXHUwMDE1T6rc+ce4xGWld2R6XHUwMDEytZDSV2bNklDyepZcXC71jvXBXHUwMDE28DlfXHUwMDA2feenmN5he4cnXltcdTAwMDUgeK7St7ffXHUwMDFibOFcdTAwMTU2XG6QNOtccm+1JJz7q2SglD8kUFx1MDAxOM3eZ/Sgwp96x9DAr/17XHUwMDFlo5Smdl5P8IqqXHUwMDFk9o9Q8fSjc8RcXFx1MDAwM2pcXO2wday9PUSWfKd0UFx1MDAwYq11UqXOP8blLTOto1KDYb5cdTAwMDOjL9WbXum8nmWUS6XjNHpjx5/wwapHxVx1MDAwMrxcdTAwMTJcdTAwMDPWQ75cdTAwMTWibz/xSsX9u1x1MDAwM7yBYVnWTChcdTAwMDBWICTtPKpAkW80rkBJIWTEO1x1MDAxZVx1MDAxZVgjeLBzSmxbWKXzen7MqNJBpXw5riFcdTAwMDFCa1x1MDAxM2k9+kPpOO+HeZfbSGBnS4/PylwiKZ1UsfOPcYFLUzqffnzAl2K7fdTjhf+1XHUwMDFhLIDVyo9ccubhXX55qIaPy+lcdTAwMWLzn37Mp9dcdTAwMTjhQFx1MDAxYf/69Nf/XHUwMDAzVVjGXHUwMDE3In0= + + + + + GroupRoleUserGroupRoleGroupUserUserUserRoleRoleGlobal PermissionObject PermissionGlobal PermissionObject PermissionGlobal PermissionObject PermissionObject PermissionObject PermissionObject PermissionObject Permission \ No newline at end of file diff --git a/docs/docs/media/tutorial_1_branch_creation.png b/docs/docs/media/tutorial_1_branch_creation.png index 5759f3d2b1..52674d101e 100644 Binary files a/docs/docs/media/tutorial_1_branch_creation.png and b/docs/docs/media/tutorial_1_branch_creation.png differ diff --git a/docs/docs/media/tutorial_1_branch_details.png b/docs/docs/media/tutorial_1_branch_details.png index ae9bb9e417..a50ddb5052 100644 Binary files a/docs/docs/media/tutorial_1_branch_details.png and b/docs/docs/media/tutorial_1_branch_details.png differ diff --git a/docs/docs/media/tutorial_1_branch_diff.png b/docs/docs/media/tutorial_1_branch_diff.png index 3c77b29435..9ccd2702e6 100644 Binary files a/docs/docs/media/tutorial_1_branch_diff.png and b/docs/docs/media/tutorial_1_branch_diff.png differ diff --git a/docs/docs/media/tutorial_1_branch_list.png b/docs/docs/media/tutorial_1_branch_list.png index 04cb5d89cb..3e4759b90b 100644 Binary files a/docs/docs/media/tutorial_1_branch_list.png and b/docs/docs/media/tutorial_1_branch_list.png differ diff --git a/docs/docs/media/tutorial_1_organization_create.png b/docs/docs/media/tutorial_1_organization_create.png index 3eee13bf55..5c9575787e 100644 Binary files a/docs/docs/media/tutorial_1_organization_create.png and b/docs/docs/media/tutorial_1_organization_create.png differ diff --git a/docs/docs/media/tutorial_1_organization_details.png b/docs/docs/media/tutorial_1_organization_details.png index 43dcdd4542..c6a4af445b 100644 Binary files a/docs/docs/media/tutorial_1_organization_details.png and b/docs/docs/media/tutorial_1_organization_details.png differ diff --git a/docs/docs/media/tutorial_1_organization_edit.png b/docs/docs/media/tutorial_1_organization_edit.png index e1d8434c19..fc877d4651 100644 Binary files a/docs/docs/media/tutorial_1_organization_edit.png and b/docs/docs/media/tutorial_1_organization_edit.png differ diff --git a/docs/docs/media/tutorial_1_organizations.png b/docs/docs/media/tutorial_1_organizations.png index 7eab989d58..624d36e70d 100644 Binary files a/docs/docs/media/tutorial_1_organizations.png and b/docs/docs/media/tutorial_1_organizations.png differ diff --git a/docs/docs/media/tutorial_2_historical.png b/docs/docs/media/tutorial_2_historical.png index cd6cfd6e0d..e3d97e9830 100644 Binary files a/docs/docs/media/tutorial_2_historical.png and b/docs/docs/media/tutorial_2_historical.png differ diff --git a/docs/docs/media/tutorial_3_schema.png b/docs/docs/media/tutorial_3_schema.png index 2a808c338b..817874df49 100644 Binary files a/docs/docs/media/tutorial_3_schema.png and b/docs/docs/media/tutorial_3_schema.png differ diff --git a/docs/docs/media/tutorial_4_metadata.png b/docs/docs/media/tutorial_4_metadata.png index 637a5a9198..60a5aa9381 100644 Binary files a/docs/docs/media/tutorial_4_metadata.png and b/docs/docs/media/tutorial_4_metadata.png differ diff --git a/docs/docs/media/tutorial_4_metadata_edit.png b/docs/docs/media/tutorial_4_metadata_edit.png index f08829abf2..1ef2cf2b8d 100644 Binary files a/docs/docs/media/tutorial_4_metadata_edit.png and b/docs/docs/media/tutorial_4_metadata_edit.png differ diff --git a/docs/docs/media/tutorial_6_branch_creation.png b/docs/docs/media/tutorial_6_branch_creation.png index bd43dac53f..87203d518c 100644 Binary files a/docs/docs/media/tutorial_6_branch_creation.png and b/docs/docs/media/tutorial_6_branch_creation.png differ diff --git a/docs/docs/overview/interfaces.mdx b/docs/docs/overview/interfaces.mdx index 7061433214..9d975cde32 100644 --- a/docs/docs/overview/interfaces.mdx +++ b/docs/docs/overview/interfaces.mdx @@ -54,6 +54,6 @@ More information can be found in the [Python SDK](../python-sdk/) documentation One of the three pillars Infrahub is built on is the idea of having unified storage for data and files. The data is stored in the graph database and the files are stored in Git. -When integrating a Git repository with Infrahub, the Git agent will ensure that both systems stay in sync at any time. Changes to branches or files in a Git repository will be synced to Infrahub automatically. +When integrating a Git repository with Infrahub, the Task worker will ensure that both systems stay in sync at any time. Changes to branches or files in a Git repository will be synced to Infrahub automatically. More information can be found in the [external repositories guide](../guides/repository) diff --git a/docs/docs/overview/readme.mdx b/docs/docs/overview/readme.mdx index 25dae372cb..aa88ef332e 100644 --- a/docs/docs/overview/readme.mdx +++ b/docs/docs/overview/readme.mdx @@ -5,7 +5,7 @@ import ReferenceLink from "../../src/components/Card"; # Infrahub overview -![Infrahub architecture](../media/overview.excalidraw.svg) +![Infrahub architecture](./../media/overview.excalidraw.svg) Infrahub acts as a central hub to manage all of the information and code that powers your infrastructure. At its heart, Infrahub is built on 3 fundamental pillars: diff --git a/docs/docs/python-sdk/guides/client.mdx b/docs/docs/python-sdk/guides/client.mdx index 090c70d1e4..9830e71bf9 100644 --- a/docs/docs/python-sdk/guides/client.mdx +++ b/docs/docs/python-sdk/guides/client.mdx @@ -30,7 +30,11 @@ client = InfrahubClientSync(address="http://localhost:8000") ## Authentication -The SDK is using a token-based authentication method to authenticate with the API and GraphQL. The token can be provided using a `Config` object or you can define it as the `INFRAHUB_API_TOKEN` environment variable. +The SDK can use API Tokens or JWT Tokens to authenticate with the REST API and GraphQL. + +### API tokens + +The API token can be provided using a `Config` object or you can define it as the `INFRAHUB_API_TOKEN` environment variable. @@ -53,6 +57,31 @@ The SDK is using a token-based authentication method to authenticate with the AP +### JWT tokens + +The username and password of the user can be provided using a `Config` object or you can define them using the `INFRAHUB_USERNAME` and `INFRAHUB_PASSWORD` environment variables. The usage of JWT Tokens is completely transparent to the user, including the process of refreshing the JWT token. + + + + + ```python + from infrahub_sdk import Config, InfrahubClient + client = await InfrahubClient(config=Config(username="admin", password="infrahub")) + client = await InfrahubClient() # token is read from the INFRAHUB_USERNAME and INFRAHUB_PASSWORD environment variable + ``` + + + + + ```python + from infrahub_sdk import Config, InfrahubClientSync + client = InfrahubClientSync(config=Config(username="admin", password="infrahub")) + client = InfrahubClientSync() # token is read from the INFRAHUB_USERNAME and INFRAHUB_PASSWORD environment variable + ``` + + + + ## Configuring the client object The client object can be configured by providing a `Config` object. Here we will show you how to enable the client to print out all of the GraphQL queries it will send to Infrahub. diff --git a/docs/docs/python-sdk/reference/config.mdx b/docs/docs/python-sdk/reference/config.mdx index 78f0fdea82..66c7d0d24c 100644 --- a/docs/docs/python-sdk/reference/config.mdx +++ b/docs/docs/python-sdk/reference/config.mdx @@ -158,7 +158,7 @@ The following settings can be defined in the `Config` class **Description**: Default connection timeout in seconds
**Type**: `integer`
-**Default value**: 10
+**Default value**: 60
**Environment variable**: `INFRAHUB_TIMEOUT`
## transport diff --git a/docs/docs/reference/configuration.mdx b/docs/docs/reference/configuration.mdx index d5b5e660e3..c1ee35c0c5 100644 --- a/docs/docs/reference/configuration.mdx +++ b/docs/docs/reference/configuration.mdx @@ -30,8 +30,9 @@ 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 Task worker for internal communication" | http://server:8000 | | | | INFRAHUB_ALLOW_ANONYMOUS_ACCESS | Indicates if the system allows anonymous read access | TRUE | | | +| INFRAHUB_ANONYMOUS_ACCESS_ROLE | Indicates if the system allows anonymous read access | Anonymous User | | | | INFRAHUB_ANALYTICS_ADDRESS | | | | | | INFRAHUB_ANALYTICS_API_KEY | | | | | | INFRAHUB_ANALYTICS_ENABLE | | | | | diff --git a/docs/docs/reference/git-agent.mdx b/docs/docs/reference/git-agent.mdx index 5906143845..57cd4041d1 100644 --- a/docs/docs/reference/git-agent.mdx +++ b/docs/docs/reference/git-agent.mdx @@ -1,12 +1,12 @@ --- -title: Git Agent +title: Task worker --- -# Git agent +# Task worker :::warning Under Construction This page is still under construction and is not available yet.
-Please reach out in Slack if you have some questions about the **Git agent** +Please reach out in Slack if you have some questions about the **Task worker** ::: diff --git a/docs/docs/reference/menu.mdx b/docs/docs/reference/menu.mdx new file mode 100644 index 0000000000..c3523e6efa --- /dev/null +++ b/docs/docs/reference/menu.mdx @@ -0,0 +1,80 @@ +--- +title: Menu definition file +--- + +# Menu definition file + +A menu definition file allows you to control the layout and structure of the menu on the left side of the Infrahub web interface. More information can be found in the [Controlling the menu guide](/guides/menu). + +The menu definition file is a YAML file that follows a particular structure or schema. + +At the top of the file we define a bit of boilerplate statements, to define the version of the schema of the file and the kind of content that it contains. We also define a spec mapping with a data key. + +```yaml +--- +apiversion: infrahub.app/v1 +kind: Menu +spec: + data: +``` + +The value of the data key in the spec mapping, is a sequence (or list) of menu items. + +A menu item is a mapping in which you can define the following key/value pairs: + +| Key | Type | Description | Mandatory | +|--------------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------| +| name | string | the name of the menu item | :white_check_mark: | +| namespace | string | the namespace of the menu item | :white_check_mark: | +| label | string | the name of the menu item in UI | :x: | +| kind | string | selecting this menu item will take you to the list view of this schema node (this automatically sets the value for the path key to the correct value) | :x: | +| path | string | selecting this menu will take you to this path (URL) | :x: | +| icon | string | the icon of the menu item | :x: | +| order_weight | integer | controls the ordering of menu items (lower values are ordered first) | :x: | +| parent | string | a parent menu item (concatenated namespace + name) | :x: | +| children | sequence | list of nested menu items | :x: | + +## Example + +```yaml +--- +apiversion: infrahub.app/v1 +kind: Menu +spec: + data: + - namespace: Location + name: Mainmenu + label: Location + icon: "mingcute:location-line" + children: + data: + - namespace: Location + name: Country + label: Countries + kind: LocationCountry + icon: "gis:search-country" + + - namespace: Location + name: Site + label: Sites + kind: LocationSite + icon: "ri:building-line" + + - namespace: Infrastructure + name: Mainmenu + label: Infrastructure + icon: "mdi:domain" + children: + data: + - namespace: Network + name: Device + label: Devices + kind: NetworkDevice + icon: "mdi:router" + + - namespace: Network + name: Interface + label: Interface + kind: NetworkInterface + icon: "mdi:ethernet" +``` diff --git a/docs/docs/reference/message-bus-events.mdx b/docs/docs/reference/message-bus-events.mdx index ad605f82b8..41cf8bd9ca 100644 --- a/docs/docs/reference/message-bus-events.mdx +++ b/docs/docs/reference/message-bus-events.mdx @@ -205,7 +205,6 @@ For more detailed explanations on how to use these events within Infrahub, see t | **meta** | Meta properties for the message | N/A | None | | **source_branch** | The source branch | string | None | | **target_branch** | The target branch | string | None | -| **ipam_node_details** | Details for changed IP nodes | array | None | #### Event event.branch.rebased @@ -220,7 +219,6 @@ For more detailed explanations on how to use these events within Infrahub, see t |-----|-------------|------|---------------| | **meta** | Meta properties for the message | N/A | None | | **branch** | The branch that was rebased | string | None | -| **ipam_node_details** | Details for changed IP nodes | array | None | @@ -308,28 +306,6 @@ For more detailed explanations on how to use these events within Infrahub, see t - -### Git Branch - - - -#### Event git.branch.create - - -**Description**: Create a branch in a Git repository. - -**Priority**: 3 - - -| Key | Description | Type | Default Value | -|-----|-------------|------|---------------| -| **meta** | Meta properties for the message | N/A | None | -| **branch** | Name of the branch to create | string | None | -| **branch_id** | The unique ID of the branch | string | None | -| **repository_id** | The unique ID of the Repository | string | None | -| **repository_name** | The name of the Repository | string | None | - - ### Git Diff @@ -586,40 +562,6 @@ For more detailed explanations on how to use these events within Infrahub, see t - -### Request Artifact - - - -#### Event request.artifact.generate - - -**Description**: Runs to generate an artifact - -**Priority**: 2 - - -| Key | Description | Type | Default Value | -|-----|-------------|------|---------------| -| **meta** | Meta properties for the message | N/A | None | -| **artifact_name** | Name of the artifact | string | None | -| **artifact_definition** | The the ID of the artifact definition | string | None | -| **commit** | The commit to target | string | None | -| **content_type** | Content type of the artifact | string | None | -| **transform_type** | The type of transform associated with this artifact | string | None | -| **transform_location** | The transforms location within the repository | string | None | -| **repository_id** | The unique ID of the Repository | string | None | -| **repository_name** | The name of the Repository | string | None | -| **repository_kind** | The kind of the Repository | string | None | -| **branch_name** | The branch where the check is run | string | None | -| **target_id** | The ID of the target object for this artifact | string | None | -| **target_name** | Name of the artifact target | string | None | -| **artifact_id** | The id of the artifact if it previously existed | N/A | None | -| **query** | The name of the query to use when collecting data | string | None | -| **timeout** | Timeout for requests used to generate this artifact | integer | None | -| **variables** | Input variables when generating the artifact | object | None | - - ### Request Artifact Definition @@ -643,91 +585,6 @@ For more detailed explanations on how to use these events within Infrahub, see t | **source_branch_sync_with_git** | Indicates if the source branch should sync with git | boolean | None | | **destination_branch** | The target branch | string | None | - -#### Event request.artifact_definition.generate - - -**Description**: Sent to trigger the generation of artifacts for a given branch. - -**Priority**: 3 - - -| Key | Description | Type | Default Value | -|-----|-------------|------|---------------| -| **meta** | Meta properties for the message | N/A | None | -| **artifact_definition** | The unique ID of the Artifact Definition | string | None | -| **branch** | The branch to target | string | None | -| **limit** | List of targets to limit the scope of the generation, if populated only the included artifacts will be regenerated | array | None | - - - -### Request Diff - - - -#### Event request.diff.update - - -**Description**: Request diff to be updated. - - If the message only include a branch_name, it is assumed to be for updating the diff that tracks - the lifetime changes of a branch - -**Priority**: 3 - - -| Key | Description | Type | Default Value | -|-----|-------------|------|---------------| -| **meta** | Meta properties for the message | N/A | None | -| **branch_name** | The branch associated with the diff | string | None | -| **name** | N/A | N/A | None | -| **from_time** | N/A | N/A | None | -| **to_time** | N/A | N/A | None | - - -#### Event request.diff.refresh - - -**Description**: Request diff be recalculated from scratch. - -**Priority**: 3 - - -| Key | Description | Type | Default Value | -|-----|-------------|------|---------------| -| **meta** | Meta properties for the message | N/A | None | -| **branch_name** | The branch associated with the diff | string | None | -| **diff_id** | The id for this diff | string | None | - - - -### Request Generator - - - -#### Event request.generator.run - - -**Description**: Runs a generator. - -**Priority**: 3 - - -| Key | Description | Type | Default Value | -|-----|-------------|------|---------------| -| **meta** | Meta properties for the message | N/A | None | -| **generator_definition** | The Generator definition | N/A | None | -| **generator_instance** | The id of the generator instance if it previously existed | N/A | None | -| **commit** | The commit to target | string | None | -| **repository_id** | The unique ID of the Repository | string | None | -| **repository_name** | The name of the Repository | string | None | -| **repository_kind** | The kind of the Repository | string | None | -| **branch_name** | The branch where the check is run | string | None | -| **target_id** | The ID of the target object for this generator | string | None | -| **target_name** | Name of the generator target | string | None | -| **query** | The name of the query to use when collecting data | string | None | -| **variables** | Input variables when running the generator | object | None | - ### Request Generator Definition @@ -768,39 +625,6 @@ For more detailed explanations on how to use these events within Infrahub, see t | **branch** | The branch to target | string | None | - -### Request Git - - - -#### Event request.git.create_branch - - -**Description**: Sent to trigger the creation of a branch in git repositories. - -**Priority**: 3 - - -| Key | Description | Type | Default Value | -|-----|-------------|------|---------------| -| **meta** | Meta properties for the message | N/A | None | -| **branch** | The branch to target | string | None | -| **branch_id** | The unique ID of the branch | string | None | - - -#### Event request.git.sync - - -**Description**: Request remote repositories to be synced. - -**Priority**: 4 - - -| Key | Description | Type | Default Value | -|-----|-------------|------|---------------| -| **meta** | Meta properties for the message | N/A | None | - - ### Request Graphql Query Group @@ -1033,115 +857,6 @@ For more detailed explanations on how to use these events within Infrahub, see t | **message** | The message to send | string | None | - -### Send Webhook - - - -#### Event send.webhook.event - - -**Description**: Sent a webhook to an external source. - -**Priority**: 3 - - -| Key | Description | Type | Default Value | -|-----|-------------|------|---------------| -| **meta** | Meta properties for the message | N/A | None | -| **webhook_id** | The unique ID of the webhook | string | None | -| **event_type** | The event type | string | None | -| **event_data** | The data tied to the event | object | None | - - - -### Send Telemetry - - - -#### Event send.telemetry.push - - -**Description**: Push usage telemetry. - -**Priority**: 3 - - -| Key | Description | Type | Default Value | -|-----|-------------|------|---------------| -| **meta** | Meta properties for the message | N/A | None | - - - - -### Transform Jinja - - - -#### Event transform.jinja.template - - -**Description**: Sent to trigger the checks for a repository to be executed. - -**Priority**: 4 - - -| Key | Description | Type | Default Value | -|-----|-------------|------|---------------| -| **meta** | Meta properties for the message | N/A | None | -| **repository_id** | The unique ID of the Repository | string | None | -| **repository_name** | The name of the repository | string | None | -| **repository_kind** | The kind of the repository | string | None | -| **data** | Input data for the template | object | None | -| **branch** | The branch to target | string | None | -| **template_location** | Location of the template within the repository | string | None | -| **commit** | The commit id to use when rendering the template | string | None | - - - -### Transform Python - - - -#### Event transform.python.data - - -**Description**: Sent to run a Python transform. - -**Priority**: 4 - - -| Key | Description | Type | Default Value | -|-----|-------------|------|---------------| -| **meta** | Meta properties for the message | N/A | None | -| **repository_id** | The unique ID of the Repository | string | None | -| **repository_name** | The name of the repository | string | None | -| **repository_kind** | The kind of the repository | string | None | -| **data** | Input data for the template | object | None | -| **branch** | The branch to target | string | None | -| **transform_location** | Location of the transform within the repository | string | None | -| **commit** | The commit id to use when rendering the template | string | None | - - - - -### Trigger Artifact Definition - - - -#### Event trigger.artifact_definition.generate - - -**Description**: Sent after a branch has been merged to start the regeneration of artifacts - -**Priority**: 3 - - -| Key | Description | Type | Default Value | -|-----|-------------|------|---------------| -| **meta** | Meta properties for the message | N/A | None | -| **branch** | The impacted branch | string | None | - ### Trigger Generator Definition @@ -1162,26 +877,6 @@ For more detailed explanations on how to use these events within Infrahub, see t | **branch** | The branch to run the Generators in | string | None | - -### Trigger Ipam - - - -#### Event trigger.ipam.reconciliation - - -**Description**: Sent after a branch has been merged/rebased to reconcile changed IP Prefix and Address nodes - -**Priority**: 3 - - -| Key | Description | Type | Default Value | -|-----|-------------|------|---------------| -| **meta** | Meta properties for the message | N/A | None | -| **branch** | The updated branch | string | None | -| **ipam_node_details** | Details for changed IP nodes | array | None | - - ### Trigger Proposed Change @@ -1425,7 +1120,6 @@ For more detailed explanations on how to use these events within Infrahub, see t | **meta** | Meta properties for the message | N/A | None | | **source_branch** | The source branch | string | None | | **target_branch** | The target branch | string | None | -| **ipam_node_details** | Details for changed IP nodes | array | None | #### Event event.branch.rebased @@ -1441,7 +1135,6 @@ For more detailed explanations on how to use these events within Infrahub, see t |-----|-------------|------|---------------| | **meta** | Meta properties for the message | N/A | None | | **branch** | The branch that was rebased | string | None | -| **ipam_node_details** | Details for changed IP nodes | array | None | @@ -1533,29 +1226,6 @@ For more detailed explanations on how to use these events within Infrahub, see t - -### Git Branch - - - -#### Event git.branch.create - - -**Description**: Create a branch in a Git repository. - -**Priority**: 3 - - - -| Key | Description | Type | Default Value | -|-----|-------------|------|---------------| -| **meta** | Meta properties for the message | N/A | None | -| **branch** | Name of the branch to create | string | None | -| **branch_id** | The unique ID of the branch | string | None | -| **repository_id** | The unique ID of the Repository | string | None | -| **repository_name** | The name of the Repository | string | None | - - ### Git Diff @@ -1825,41 +1495,6 @@ For more detailed explanations on how to use these events within Infrahub, see t - -### Request Artifact - - - -#### Event request.artifact.generate - - -**Description**: Runs to generate an artifact - -**Priority**: 2 - - - -| Key | Description | Type | Default Value | -|-----|-------------|------|---------------| -| **meta** | Meta properties for the message | N/A | None | -| **artifact_name** | Name of the artifact | string | None | -| **artifact_definition** | The the ID of the artifact definition | string | None | -| **commit** | The commit to target | string | None | -| **content_type** | Content type of the artifact | string | None | -| **transform_type** | The type of transform associated with this artifact | string | None | -| **transform_location** | The transforms location within the repository | string | None | -| **repository_id** | The unique ID of the Repository | string | None | -| **repository_name** | The name of the Repository | string | None | -| **repository_kind** | The kind of the Repository | string | None | -| **branch_name** | The branch where the check is run | string | None | -| **target_id** | The ID of the target object for this artifact | string | None | -| **target_name** | Name of the artifact target | string | None | -| **artifact_id** | The id of the artifact if it previously existed | N/A | None | -| **query** | The name of the query to use when collecting data | string | None | -| **timeout** | Timeout for requests used to generate this artifact | integer | None | -| **variables** | Input variables when generating the artifact | object | None | - - ### Request Artifact Definition @@ -1884,95 +1519,6 @@ For more detailed explanations on how to use these events within Infrahub, see t | **source_branch_sync_with_git** | Indicates if the source branch should sync with git | boolean | None | | **destination_branch** | The target branch | string | None | - -#### Event request.artifact_definition.generate - - -**Description**: Sent to trigger the generation of artifacts for a given branch. - -**Priority**: 3 - - - -| Key | Description | Type | Default Value | -|-----|-------------|------|---------------| -| **meta** | Meta properties for the message | N/A | None | -| **artifact_definition** | The unique ID of the Artifact Definition | string | None | -| **branch** | The branch to target | string | None | -| **limit** | List of targets to limit the scope of the generation, if populated only the included artifacts will be regenerated | array | None | - - - -### Request Diff - - - -#### Event request.diff.update - - -**Description**: Request diff to be updated. - - If the message only include a branch_name, it is assumed to be for updating the diff that tracks - the lifetime changes of a branch - -**Priority**: 3 - - - -| Key | Description | Type | Default Value | -|-----|-------------|------|---------------| -| **meta** | Meta properties for the message | N/A | None | -| **branch_name** | The branch associated with the diff | string | None | -| **name** | N/A | N/A | None | -| **from_time** | N/A | N/A | None | -| **to_time** | N/A | N/A | None | - - -#### Event request.diff.refresh - - -**Description**: Request diff be recalculated from scratch. - -**Priority**: 3 - - - -| Key | Description | Type | Default Value | -|-----|-------------|------|---------------| -| **meta** | Meta properties for the message | N/A | None | -| **branch_name** | The branch associated with the diff | string | None | -| **diff_id** | The id for this diff | string | None | - - - -### Request Generator - - - -#### Event request.generator.run - - -**Description**: Runs a generator. - -**Priority**: 3 - - - -| Key | Description | Type | Default Value | -|-----|-------------|------|---------------| -| **meta** | Meta properties for the message | N/A | None | -| **generator_definition** | The Generator definition | N/A | None | -| **generator_instance** | The id of the generator instance if it previously existed | N/A | None | -| **commit** | The commit to target | string | None | -| **repository_id** | The unique ID of the Repository | string | None | -| **repository_name** | The name of the Repository | string | None | -| **repository_kind** | The kind of the Repository | string | None | -| **branch_name** | The branch where the check is run | string | None | -| **target_id** | The ID of the target object for this generator | string | None | -| **target_name** | Name of the generator target | string | None | -| **query** | The name of the query to use when collecting data | string | None | -| **variables** | Input variables when running the generator | object | None | - ### Request Generator Definition @@ -2015,41 +1561,6 @@ For more detailed explanations on how to use these events within Infrahub, see t | **branch** | The branch to target | string | None | - -### Request Git - - - -#### Event request.git.create_branch - - -**Description**: Sent to trigger the creation of a branch in git repositories. - -**Priority**: 3 - - - -| Key | Description | Type | Default Value | -|-----|-------------|------|---------------| -| **meta** | Meta properties for the message | N/A | None | -| **branch** | The branch to target | string | None | -| **branch_id** | The unique ID of the branch | string | None | - - -#### Event request.git.sync - - -**Description**: Request remote repositories to be synced. - -**Priority**: 4 - - - -| Key | Description | Type | Default Value | -|-----|-------------|------|---------------| -| **meta** | Meta properties for the message | N/A | None | - - ### Request Graphql Query Group @@ -2294,120 +1805,6 @@ For more detailed explanations on how to use these events within Infrahub, see t | **message** | The message to send | string | None | - -### Send Webhook - - - -#### Event send.webhook.event - - -**Description**: Sent a webhook to an external source. - -**Priority**: 3 - - - -| Key | Description | Type | Default Value | -|-----|-------------|------|---------------| -| **meta** | Meta properties for the message | N/A | None | -| **webhook_id** | The unique ID of the webhook | string | None | -| **event_type** | The event type | string | None | -| **event_data** | The data tied to the event | object | None | - - - -### Send Telemetry - - - -#### Event send.telemetry.push - - -**Description**: Push usage telemetry. - -**Priority**: 3 - - - -| Key | Description | Type | Default Value | -|-----|-------------|------|---------------| -| **meta** | Meta properties for the message | N/A | None | - - - - -### Transform Jinja - - - -#### Event transform.jinja.template - - -**Description**: Sent to trigger the checks for a repository to be executed. - -**Priority**: 4 - - - -| Key | Description | Type | Default Value | -|-----|-------------|------|---------------| -| **meta** | Meta properties for the message | N/A | None | -| **repository_id** | The unique ID of the Repository | string | None | -| **repository_name** | The name of the repository | string | None | -| **repository_kind** | The kind of the repository | string | None | -| **data** | Input data for the template | object | None | -| **branch** | The branch to target | string | None | -| **template_location** | Location of the template within the repository | string | None | -| **commit** | The commit id to use when rendering the template | string | None | - - - -### Transform Python - - - -#### Event transform.python.data - - -**Description**: Sent to run a Python transform. - -**Priority**: 4 - - - -| Key | Description | Type | Default Value | -|-----|-------------|------|---------------| -| **meta** | Meta properties for the message | N/A | None | -| **repository_id** | The unique ID of the Repository | string | None | -| **repository_name** | The name of the repository | string | None | -| **repository_kind** | The kind of the repository | string | None | -| **data** | Input data for the template | object | None | -| **branch** | The branch to target | string | None | -| **transform_location** | Location of the transform within the repository | string | None | -| **commit** | The commit id to use when rendering the template | string | None | - - - - -### Trigger Artifact Definition - - - -#### Event trigger.artifact_definition.generate - - -**Description**: Sent after a branch has been merged to start the regeneration of artifacts - -**Priority**: 3 - - - -| Key | Description | Type | Default Value | -|-----|-------------|------|---------------| -| **meta** | Meta properties for the message | N/A | None | -| **branch** | The impacted branch | string | None | - ### Trigger Generator Definition @@ -2429,27 +1826,6 @@ For more detailed explanations on how to use these events within Infrahub, see t | **branch** | The branch to run the Generators in | string | None | - -### Trigger Ipam - - - -#### Event trigger.ipam.reconciliation - - -**Description**: Sent after a branch has been merged/rebased to reconcile changed IP Prefix and Address nodes - -**Priority**: 3 - - - -| Key | Description | Type | Default Value | -|-----|-------------|------|---------------| -| **meta** | Meta properties for the message | N/A | None | -| **branch** | The updated branch | string | None | -| **ipam_node_details** | Details for changed IP nodes | array | None | - - ### Trigger Proposed Change diff --git a/docs/docs/reference/permissions.mdx b/docs/docs/reference/permissions.mdx new file mode 100644 index 0000000000..ccae2fe5a2 --- /dev/null +++ b/docs/docs/reference/permissions.mdx @@ -0,0 +1,62 @@ +--- +title: Permissions +--- + +# Permissions + +This page provides detailed documentation for all available global and object permissions within Infrahub. + +:::info + +For more detailed explanations on how to use these permissions within Infrahub, see the [roles and permissions](/topics/permissions-roles) topic. + +::: + +## Global permissions + +Below are the eight global permissions possible in Infrahub: + +| Identifier | Action | Decision | +|-------------------------------------------|-------------------------|----------| +| `global:edit_default_branch:allow_all` | `edit_default_branch` | `Allow` | +| `global:manage_accounts:allow_all` | `manage_accounts` | `Allow` | +| `global:manage_permissions:allow_all` | `manage_permissions` | `Allow` | +| `global:manage_repositories:allow_all` | `manage_repositories` | `Allow` | +| `global:merge_branch:allow_all` | `merge_branch` | `Allow` | +| `global:merge_proposed_change:allow_all` | `merge_proposed_change` | `Allow` | +| `global:manage_schema:allow_all` | `manage_schema` | `Allow` | +| `global:super_admin:allow_all` | `super_admin` | `Allow` | + +### Attributes + +- **Identifier**: A unique string that identifies the permission, computed by the backend based on the **Action** and **Decision**. +- **Action**: The action that the permission permits, such as `edit_default_branch` or `manage_accounts`. +- **Decision**: Indicates if the action is permitted or prohibited: + - **Allow**: Grants permission for the action. + - **Deny**: Denies permission for the action. +- **Roles**: These are the roles that make use of this permission. + +## Object permissions + +Object permissions can be applied to different types of objects and across different branches. + +| Identifier | Object Type | Action | Decision | Description | +|-----------------------------------------------|---------------|---------|-------------|---------------------------------------------------------------------------------------------| +| `object:*:*:create:allow_other` | `*` (all types)| `any` | `allow_other`| Allows creating any object, but only on non-default branches. | +| `object:*:*:view:allow_all` | `*` (all types)| `view` | `allow_all` | Allows viewing any object, anywhere, across both default and non-default branches. | +| `object:Builtin:Tag:update:deny` | `BuiltinTag` | `update`| `deny` | Denies the ability to update any object of type BuiltinTag, across all branches. | +| `object:*:Generic:view:allow_all` | `*Generic` | `view` | `allow_all` | Allows viewing all objects that contain 'Generic' in their type (example: LocationGeneric, DeviceGeneric) in all namespaces, across all branches. | + +### Attributes + + +- **Identifier**: A unique string that identifies the permission, computed by the backend based on the **Action**, **Object Type**, **Branch Type**, and **Decision**. +- **Object Type**: The type of object the permission applies to (such as, `tag`, `device`). Wildcards (`*`) can be used to apply the permission to all object types. +- **Action**: The specific action allowed on the object, such as `create`, `update`, `delete`, or `view`. +- **Decision**: Controls whether the action is allowed or denied, and under which branch type it applies: + - **allow_default**: Allows the action on the default branch. + - **allow_other**: Allows the action on branches other than the default one. + - **allow_all**: Allows the action on both the default and non-default branches. + - **deny**: Denies the action regardless of branch. +- **Roles**: The roles that use this permission. + \ No newline at end of file diff --git a/docs/docs/release-notes/infrahub/release-1_0.mdx b/docs/docs/release-notes/infrahub/release-1_0.mdx new file mode 100644 index 0000000000..897d81017f --- /dev/null +++ b/docs/docs/release-notes/infrahub/release-1_0.mdx @@ -0,0 +1,252 @@ +--- +title: Release 1.0.0 +--- + + + + + + + + + + + + + + + + + + + +
Release Number1.0.0
Release DateOctober 30th, 2024
Release CodenameStockholm
Tag[infrahub-v1.0.0](https://github.com/opsmill/infrahub/releases/tag/infrahub-v1.0.0)
+ +# Release 1.0 + +We are thrilled to announce the general availability release of Infrahub, version *1.0*! + +We greatly appreciate all the time and efforts of our dedicated community of developers and beta testers! + +## Main changes + +Infrahub 1.0 is focused on bringing Infrahub to even more organizations and equipping them with a +production-ready solution to their infrastructure automation challenges. + +There are four key features and changes in Infrahub version 1.0 as outlined below: + +- SSO login capabilities (OIDC/OUATH2) +- A new permission framework +- Performance enhancements to version control actions (diff/merge/rebase) +- An exciting UI and navigation redesign + +In addition to these, there have been many bug-fixes and quality-of-life enhancements shown in the detailed changelog. + +### Single sign-on and user permissions + +Early in the development of Infrahub, we consciously decided to focus on the groundbreaking and critical features that make +Infrahub a uniquely world-class Source of Truth. +As a result, we set aside some standard enterprise features, such as Single Sign-On, until later, +knowing that they would be straightforward to implement in the system. + +As we enter a new era with Infrahub 1.0, we have focused on rounding out the enterprise-grade features +that our customers require. +For example, we added Single Sign-On (SSO) integrations for OIDC/Oauth 2.0 and implemented a robust and granular permissions system. +These two features combine to give organizations a level of control that allows them to trust their mission-critical +data in Infrahub and bring even closer integration with existing enterprise systems and workflows. + +#### SSO + +The new OIDC/OAuth2 capabilities, tested and functioning in the field with many Identity Providers +such as Keycloak, Authentik, and Google Auth, allow organizations to manage their users and groups centrally +instead of in the Infrahub UI. +Subsequent releases of Infrahub will soon include validated support for additional Identity Providers +and other authentication methods (such as LDAP). + +This functionality goes hand in hand with the feature we will discuss next: our new User Permissions structure. + +##### Documentation + +- https://docs.infrahub.app/guides/sso + +#### User permissions + +As users store more data in Infrahub and more teams interact with that data, it becomes crucial to protect it +from accidental changes. +By implementing a granular role-based permission system, Infrahub allows organizations to prevent unauthorized changes +to the data behind critical infrastructure automation efforts. + +In the permission structure introduced in Infrahub 1.0, Users are added to Groups, Groups are given Roles, +and Permissions are finally allocated to those Roles. +Permissions come in two fundamental varieties: Global Permissions and Object Permissions. + +The below diagram lays out the relationship between each of these entities. +A User can belong to one or more Groups, a Group can have multiple Roles assigned to it, and each Role can be granted one +or more Global or Object Permissions. + +!["Example relationship of User to Group to Role to Permission in Infrahub 1.0."](../../media/release_notes/infrahub_1_0/permissions_1_0.excalidraw.svg) + +##### Global vs. object permissions + +It is also worth mentioning the difference between Global Permissions and Object Permissions. + +- Global Permissions are specific permission sets that can give users system-wide rights to perform particular actions, for example: + - Editing the default Branch + - Editing Permissions + - Allowing the merging of proposed changes + - Account management +- Object Permissions are tied to individual objects within Infrahub and control what actions users can take on those objects; examples could include: + - Allow read-only access to all objects + - Deny the ability to update Tags + - Allow editing on any object type that starts with `DataCenter` + +For either style, Permissions are structured to be robust and granular by allowing complete control over +the Action, Decision, and Role of a given Permission set (plus the Object Type for Object Permissions). + +##### Documentation + +This is a significant topic on a new feature that enables complex workflows to meet organizational needs. Because of this, we strongly recommend diving into the documentation links below. + +- https://docs.infrahub.app/guides/accounts-permissions +- https://docs.infrahub.app/topics/permissions-roles +- https://docs.infrahub.app/reference/permissions + +### Performance improvements + +Our design philosophy at OpsMill has always been driven by long experience as network practitioners or admins +in Unix-like systems: “Make it work, make it right, make it fast.” + +In Infrahub 1.0, we focused on the last part, “Make it fast.” + +As our early beta testers began to utilize Infrahub in increasingly large infrastructures (greater than 50,000 nodes), +we expected (and found) opportunities for performance improvements. +This was especially true when performing Version Control actions on large data sets. +As a result of the intensive testing, we made dramatic improvements to branch change management. +Infrahub 1.0 has improved how Infrahub computes a difference between two branches, re-bases a branch, +and handles the merge. +For example, in the “diff” generation for a proposed change, we have seen a 30% increase in performance over earlier versions. +As a result, the proposed change functionality can now reliably handle much larger data sets for comparison. + +These performance improvements are only the beginning of our optimization efforts, but they have already improved +the experience of Infrahub users of all infrastructure sizes. + +### UI redesign + +While the UI in Infrahub before 1.0 served its purpose well, there was room for improvement. +A corollary fourth phrase to our design philosophy might be, “Now, make it pretty!” + +We didn’t just change around some colors or styles; we worked closely with our beta testers and a dedicated +User Experience professional to ensure that using Infrahub 1.0 would be an experience that our users enjoyed. + +!["Example screenshot of new UI in Infrahub 1.0."](../../media/release_notes/infrahub_1_0/1_0_ui.png) + +We also provided capabilities for complete customization of the navigation menu. +We continue to emphasize that Infrahub is a powerful, fully customizable system that meets your +organization where it needs to be. + +#### Documentation + +- https://docs.infrahub.app/guides/menu +- https://docs.infrahub.app/reference/menu + +## Other + +### Removed + +- Remove previously deprecated GET API endpoint "/api/schema/" ([#3884](https://github.com/opsmill/infrahub/issues/3884)) + +### Deprecated + +- Marked CoreAccount.role as deprecated + Due to the new permissions framework the account roles "admin" / "read-only" / "read-write" are deprecated and will be removed in Infrahub 1.1 + +### Added + +- Reworked branch selector: + - Redesigned the UI + - Added filter for branch + - Improved accessibility & keyboard navigation + - Improved UX on new branch form + - Added quick link to view all branches +- Add support to sign in with OAuth2 and Open ID Connect (OIDC) ([#1568](https://github.com/opsmill/infrahub/issues/1568)) +- Add internal HTTP adapter to allow for generic access from Infrahub ([#3302](https://github.com/opsmill/infrahub/issues/3302)) +- Add support to search a node by human friendly ID within a GraphQL query ([#3908](https://github.com/opsmill/infrahub/issues/3908)) +- Added link to our Discord server in the account menu +- Added permissions framework for global and object kind level permissions + + In this first iteration the object permissions are applied to nodes as a whole, in upcoming versions it will be possible to define attribute level permissions as well. +- New permissions system in UI: + - Implemented CRUD views for managing accounts, groups, roles, and permissions + - Updated all components to support new permission system + - Added dynamic message display according to user access levels + +### Fixed + +- 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 ([#1075](https://github.com/opsmill/infrahub/issues/1075)) +- Add ability to import repositories with default branch other than 'main' ([#3435](https://github.com/opsmill/infrahub/issues/3435)) +- Disable approve/merge/close buttons for merged Proposed Changes ([#3495](https://github.com/opsmill/infrahub/issues/3495)) +- Fixed regex validation for List type attributes ([#3929](https://github.com/opsmill/infrahub/issues/3929)) +- Allow users to run artifacts and generators on nodes without name attribute ([#4062](https://github.com/opsmill/infrahub/issues/4062)) +- In the schema, properly delete inherited attribute and relationship on Node when the original attribute or relationship are being deleted on the Generic ([#4301](https://github.com/opsmill/infrahub/issues/4301)) +- "Retry All" button for checks is bigger ([#4315](https://github.com/opsmill/infrahub/issues/4315)) +- Add a size restriction on common attribute kinds. Only TextArea and JSON support large values ([#4432](https://github.com/opsmill/infrahub/issues/4432)) +- The HFID of a related node is properly returned via GraphQL in all scenarios ([#4482](https://github.com/opsmill/infrahub/issues/4482)) +- Add full validation to BranchMerge and BranchRebase mutations ([#4595](https://github.com/opsmill/infrahub/issues/4595)) +- Report user-friendly error for invalid uniqueness_constraints when loading schemas ([#4677](https://github.com/opsmill/infrahub/issues/4677)) +- Fixed pagination query for nodes with order_by clause using non unique attributes ([#4700](https://github.com/opsmill/infrahub/issues/4700)) +- Fixed schema migration when an attribute previously present on a node is added back ([#4727](https://github.com/opsmill/infrahub/issues/4727)) +- Add order_weight property to multiple attributes and relationships in the demo schema to improve how some models are displayed in the list views +- Changed the Python SDK connection timeout to 60s +- Fix metric missing the query name in Prometheus data +- Fixes an issue where docker compose would output ANSI control characters that don't support it +- Prevent temporary directories generated by Docusaurus to be imported by Docker + +## Migration guide + +The process to migrate your instance of Infrahub to the latest version may vary depending on your deployment of Infrahub. +However, at a high-level, it will involve getting the latest version of the Infrahub code, and then performing any needed Database Migrations and Schema updates. + +Please ensure you have a **backup of your Infrahub environment** prior to attempting any migration or upgrade activities. + +### Migration of an Infrahub instance + +**First**, update the Infrahub version running in your environment. + +Below are some example ways to get the latest version of Infrahub in your environment. + +- For deployments via Docker Compose, update your container version by updating the `IMAGE_VER` environment variable and relaunch: + - `export IMAGE_VER="1.0.0"; docker compose pull && docker compose up -d` +- For deployments via Kubernetes, utilize the latest version of the Helm chart supplied with this release + +**Second**, once you have gotten the desired version of Infrahub in your environment, please run the following commands. + +> Note: If you are running Infrahub in Docker/K8s, these commands need to run from a container where Infrahub is installed. + +```shell +infrahub db migrate +infrahub db update-core-schema +``` + +**Finally**, restart all instances of Infrahub. + +### Migration of a dev or demo instance + +If you are using the `dev` or `demo` environments, we have provided `invoke` commands to aid in the migration to the latest version. +The below examples provide the `demo` version of the commands, however similar commands can be used for `dev` as well. + +```shell +invoke demo.stop +invoke demo.build +invoke demo.migrate +invoke demo.start +``` + +If you don't want to keep your data, you can start a clean instance with the following command. + +> **Warning: All data will be lost, please make sure to backup everything you need before running this command.** + +```shell +invoke demo.destroy demo.build demo.start demo.load-infra-schema demo.load-infra-data +``` + +The repository https://github.com/opsmill/infrahub-demo-edge has also been updated, it's recommended to pull the latest changes into your fork. \ No newline at end of file diff --git a/docs/docs/release-notes/infrahub/release-1_0-DRAFT.mdx b/docs/docs/release-notes/infrahub/release-1_0_1-DRAFT.mdx similarity index 96% rename from docs/docs/release-notes/infrahub/release-1_0-DRAFT.mdx rename to docs/docs/release-notes/infrahub/release-1_0_1-DRAFT.mdx index 185e7db431..28c209c497 100644 --- a/docs/docs/release-notes/infrahub/release-1_0-DRAFT.mdx +++ b/docs/docs/release-notes/infrahub/release-1_0_1-DRAFT.mdx @@ -1,5 +1,5 @@ --- -title: Release 0.16 - DEVELOPMENT +title: Release 1.0.1 - DEVELOPMENT --- @@ -22,7 +22,7 @@ title: Release 0.16 - DEVELOPMENT
-# Release 1.0 +# Release 1.0.1 ## Main changes diff --git a/docs/docs/topics/architecture.mdx b/docs/docs/topics/architecture.mdx index 44ed346db0..8bcd36668c 100644 --- a/docs/docs/topics/architecture.mdx +++ b/docs/docs/topics/architecture.mdx @@ -14,7 +14,8 @@ The main components are: - A **Frontend** written in React and rendered in the user's browser. - An **API server** written in Python with FastAPI. -- A **Git agent** written in Python to manage the interaction with external Git repositories. +- A **Task manager** based on `Prefect` to orchestrate workflow tasks. +- A **Task worker** written in Python to execute specific tasks such as managing the interaction with external Git repositories. - A **Graph database** based on `neo4j`. - A **Message bus** based on `RabbitMQ`. - A **Cache** based on `redis`. @@ -33,13 +34,23 @@ Multiple instance of the API Server can run at the same time to process more req ::: -### Git agent +### Task manager + +The Task manager is based on Prefect, and is responsible for orchestration of tasks to be delegated to one or more Task workers. + +### Task worker Language: Python -The Git agent is responsible for managing all the content related to the Git repositories. It organizes the file systems in order to quickly access any relevant commit. The Git Agent periodically pulls the Git server for updates and listens to the RPC channel on the event bus for tasks to execute. +The Task worker is responsible for managing all the content related to the Git repositories. It organizes the file systems in order to quickly access any relevant commit. The Task worker periodically pulls the Git server for updates and listens to the RPC channel on the event bus for tasks to execute. + +Currently there are three types of tasks: + +- *Internal* tasks +- *User* tasks +- *Git* tasks -Some of the tasks that can be executed on the Git agent includes: +Some of the tasks that can be executed on the Task worker include: - Rendering a Jinja template. - Rendering a transform function. @@ -48,7 +59,7 @@ Some of the tasks that can be executed on the Git agent includes: :::note -Multiple instance of the Git agent can run at the same time to process more requests. +Multiple instance of the Task worker can run at the same time to process more requests. ::: diff --git a/docs/docs/topics/artifact.mdx b/docs/docs/topics/artifact.mdx index 08afd41a3e..88b1982b8f 100644 --- a/docs/docs/topics/artifact.mdx +++ b/docs/docs/topics/artifact.mdx @@ -34,7 +34,7 @@ An **artifact definition** centralizes all the information required to generate - Format of the output - Information to extract from each target that must be passed to the transformation. -From an **artifact definition** artifact nodes are created, for each target which is part of the group. The result of the transformation is stored in the [object storage](./object-storage.mdx). The generation of the artifacts is performed by the Git agent(s). +From an **artifact definition** artifact nodes are created, for each target which is part of the group. The result of the transformation is stored in the [object storage](./object-storage.mdx). The generation of the artifacts is performed by the Task worker(s). ![](../media/topics/artifact/architecture.excalidraw.svg) diff --git a/docs/docs/topics/auth.mdx b/docs/docs/topics/auth.mdx index 3793295007..63cbda2a98 100644 --- a/docs/docs/topics/auth.mdx +++ b/docs/docs/topics/auth.mdx @@ -4,39 +4,110 @@ title: User management and authentication # User management and authentication -Infrahub now supports standard user management and authentication systems. - -A user account can have 3 levels of permissions - -- `admin` -- `read-write` -- `read-only` - -By default, Infrahub will allow anonymous access in read-only. It's possible to disable this via the configuration `main.allow_anonymous_access` or via the environment variable `INFRAHUB_ALLOW_ANONYMOUS_ACCESS`. +By default, Infrahub will allow anonymous access in read-only mode. It's possible to disable this via the configuration `main.allow_anonymous_access` or via the environment variable `INFRAHUB_ALLOW_ANONYMOUS_ACCESS`. ## Authentication mechanisms -Infrahub supports two authentication methods +Infrahub supports two authentication methods: -- JWT token: Short life tokens generated on demand from the API. -- API Token: Long life tokens generated ahead of time. +- **JWT Token**: Short-lived tokens generated on demand from the API. +- **API Token**: Long-lived tokens generated ahead of time. | | JWT | TOKEN | | ------------------ | ---- | ----- | | API / GraphQL | Yes | Yes | | Frontend | Yes | No | -| Python SDK | Soon | Yes | -| infrahubctl | Soon | Yes | +| Python SDK | Yes | Yes | +| infrahubctl | Yes | Yes | | GraphQL Playground | No | Yes | -More information on managing API token can be found in the [managing API tokens guide](/guides/managing-api-tokens). +More information on managing API tokens can be found in the [Managing API Tokens Guide](/guides/managing-api-tokens). :::info While using the API, the authentication token must be provided in the header: ```yaml -X-INFRAHUB-KEY: 06438eb2-8019-4776-878c-0941b1f1d1ec -``` +X-INFRAHUB-KEY: 06438eb2-8019-4776-878c-0941b1f1d1ec``` ::: + +## Users permissions management + +Users are allocated permissions through groups and roles. + +- **Users** are members of **Groups**. +- **Groups** are related with **Roles**. +- **Roles** are allocated **Permissions** (global or object-specific). + +Using roles and groups to manage permissions, **Infrahub offers a scalable way** to control access for numerous users simultaneously. For more detailed information, visit the [role and permissions page](/topics/permissions-roles). + +## Default setup + +Infrahub comes with a default configuration that contains pre-configured users, groups, and roles to simplify access management from the start. These **default settings** guarantee that key access and admin capabilities are ready to use out of the box. + +### Default account + +| Accounts | Description | +|-----------|------------------------------------------------------------------------------------------------------------------------------------------------------| +| **Admin** | The default administrative user in Infrahub. This user is part of the **Super Administrators** group, which gives them full system-wide permissions. | + +### Default groups + +| Group Name | Description | Assigned Role | +|------------------------|------------------------------------------------------------------------------------------------------------|-------------------------------| +| **Infrahub Users** | Standard users who have general access to Infrahub, with permission to view and interact with resources. | **General Access** | +| **Super Administrators**| Administrators with full control over the system. Users in this group have unrestricted access to all features. | **Super Administrator** | + +### Default roles + +#### General access + +This role gives standard users general permissions to view and interact with resources across the platform while restricting administrative actions. + +| Permission | Description | +|-------------------------------------------|-------------------------------------------------------------------------------------------------| +| `global:manage_repositories:allow_all` | Enables repository management for all branches. | +| `global:manage_schema:allow_all` | Permits global schema management. | +| `global:merge_proposed_change:allow_all` | Allows merging proposed changes across all branches. | +| `object:*:*:view:allow_all` | Allows seeing all objects, across all branches and namespaces. | +| `object:*:*:any:allow_other` | Permits executing any action on non-default branches for all object types. | + +#### Super administrator + +This role provides full administrative control over Infrahub. Users with this role can manage everything within the system. + +| Permission | Description | +|-------------------------------------|--------------------------------------------------------------------------------------------------------------| +| `global:super_admin:allow_all` | Permits complete administrative control, including schema, permissions, users, and repositories management. | + +#### Anonymous user + +If Infrahub is setup to allow anonymous access when it is first initialized, an additional account role called **Anonymous User** will be created. This role defines all the permissions that a user will inherit when not logged in. The default configuration for this role ships with two permissions: + +| Permission | Description | +|-------------------------------------|--------------------------------------------------------------------------------------------------------------| +| `object:*:*:any:deny` | Denies anything on all objects, across all branches (this one is not required, but it is more explicit) | +| `object:*:*:view:allow_all` | llows seeing all objects, across all branches and namespaces. | + +The role is defined by its name in Infrahub's configuration and can be adjusted by changing the setting `main.anonymous_access_role` or the environment variable `INFRAHUB_ANONYMOUS_ACCESS_ROLE`. + +Note that an anonymous user will never be able to make changes to data inside Infrahub whether the role's permisssions allow it or not. + +## Authentication backends + +Infrahub supports authenticating users in a local user store or by using single sign-on through an external identity provider. + +### Local user store + +Users can be created in the local user store. Local users can be added to groups, which can have roles assigned to them. + +### Single sign-on + +Infrahub supports identity providers that support either OAuth2 or OpenID Connect (OIDC). + +Multiple identity providers can be enabled simultaneously, for example to support organizations that use different providers for different security domains. + +A user that was authenticated using SSO will be created in the local user store of Infrahub and optionally automatically added to groups, which can have roles assigned to them. + +For more information on setting up single sign-on can be found in the [configuring single sign-on guide](/guides/sso). diff --git a/docs/docs/topics/graphql.mdx b/docs/docs/topics/graphql.mdx index 2a132920a5..7ee0f06853 100644 --- a/docs/docs/topics/graphql.mdx +++ b/docs/docs/topics/graphql.mdx @@ -8,17 +8,22 @@ The GraphQL interface is the main interface to interact with Infrahub. The Graph The endpoint to interact with the main branch is accessible at `https:///graphql`. To interact with a branch the URL must include the name of the branch, such as `https:///graphql/`. +If you need to extract the current GraphQL schema in your environment you can issue an HTTP get request to: + +- `https:////schema.graphql` +- `https:////schema.graphql?branch=some-other-branch` ## Query & mutations -For each model in the schema, a GraphQL query and 3 mutations will be generated based on the namespace and the name of the model. +In GraphQL, a query is used to fetch data and mutations are use to create/update or delete data. In Infrahub, a GraphQL query and 4 mutations will be generated for each model you define in the schema. The name of the query or mutation is based on the namespace and name of the model. For example, for the model `CoreRepository` the following query and mutations have been generated: -- `Query` : **CoreRepository** -- `Mutation` : **CoreRepositoryCreate** -- `Mutation` : **CoreRepositoryUpdate** -- `Mutation` : **CoreRepositoryDelete** +- `Query` : **CoreRepository** to fetch `CoreRepository` nodes from Infrahub +- `Mutation` : **CoreRepositoryCreate** to create a `CoreRepository` node +- `Mutation` : **CoreRepositoryUpdate** to update an existing `CoreRepository` node +- `Mutation` : **CoreRepositoryUpsert** to create or update a `CoreRepository` node +- `Mutation` : **CoreRepositoryDelete** to delete a `CoreRepository` node ### Query format @@ -26,16 +31,17 @@ The top level query for each model will always return a list of objects and the ```graphql query { - CoreRepository { # PaginatedCoreRepository object - count - edges { # EdgedCoreRepository object - node { # CoreRepository object - id - display_label - __typename - } - } + CoreRepository { # PaginatedCoreRepository object + count + edges { # EdgedCoreRepository object + node { # CoreRepository object + id + hfid + display_label + __typename + } } + } } ``` @@ -45,9 +51,13 @@ All list of objects will be nested under `edges` & `node` to make it possible to ::: -#### `ID` and `display_label` +#### `ID`, `hfid` and `display_label` + +For all nodes, the attribute `id`, `hfid` and `display_label` are automatically available. -For all nodes, the attribute `id` and `display_label` are automatically available. The value used to generate the `display_label` can be defined for each model in the schema. If no value has been provided a generic display label with the kind and the ID of the Node will be generated. +The value used to generate the `display_label` can be defined for each model in the schema. If no value has been provided a generic display label with the kind and the ID of the Node will be generated. + +The value used to generate the `hfid` can be defined for each model in the schema. If no value has been provided and the `model` has a single uniqueness constraint defined, then the `hfid` will be automatically generated from the uniqueness constraint. At the object level, there are mainly 3 types of resources that can be accessed, each with a different format: @@ -64,22 +74,22 @@ At the same level all the metadata of the attribute are also available example: ```graphql {6-14} title="Example query to access the value and the properties of the attribute 'name'" query { - CoreRepository { - count - edges { - node { - name { # TextAttribute object - value - is_protected - is_visible - source { - id - display_label - } - } - } + CoreRepository { + count + edges { + node { + name { # TextAttribute object + value + is_protected + is_visible + source { + id + display_label + } } + } } + } } ``` @@ -89,27 +99,28 @@ A relationship to another model with a cardinality of `One` will be represented ```graphql {6-19} title="Example query to access the peer and the properties of the relationship 'account', with a cardinality of one." query { - CoreRepository { - count - edges { - node { - account { - properties { - is_visible - is_propected - source { - id - display_label - } - } - node { - display_label - id - } - } + CoreRepository { + count + edges { + node { + account { + properties { + is_visible + is_propected + source { + id + display_label } + } + node { + display_label + hfid + id + } } + } } + } } ``` @@ -119,46 +130,48 @@ A relationship with a cardinality of `Many` will be represented with a `NestedPa ```graphql {6-20} title="Example query to access the relationship 'tags', with a cardinality of Many." query { - CoreRepository { - count - edges { + CoreRepository { + count + edges { + node { + tags { # NestedPaginatedBuiltinTag object + count + edges { # NestedEdgedBuiltinTag object + properties { + is_protected + source { + id + } + } node { - tags { # NestedPaginatedBuiltinTag object - count - edges { # NestedEdgedBuiltinTag object - properties { - is_protected - source { - id - } - } - node { - display_label - id - } - } - } + display_label + hfid + id } + } } + } } + } } ``` ### Mutations format -The format of the mutation to `Create` and `Update` an object has some similarities with the query format. The format will be slightly different for: +The format of the mutation to `Create`, `Update` and `Upsert` an object has some similarities with the query format. The format will be slightly different for: - An `Attribute` - A relationship of `Cardinality One` - A relationship of `Cardinality Many` -#### Create and update +#### Create, update and upsert -To `Create` or `Update` an object, the mutations will have the following properties. +To `Create`, `Update` or `Upsert` an object, the mutations will have the following properties. - The input for the mutation must be provided inside `data`. - All mutations will return `ok` and `object` to access some information after the mutation has been executed. -- For `Update`, it is mandatory to provide an `id`. +- `Update` mutations require you to provide an `id` or `hfid` to identify the object you want to update. +- `Upsert` mutations do not require you to provide the `id` or the `hfid`, but enough information needs to be provided for the back-end to uniquely identify the node. Typically this means that all the attribute or relationship values need to be provided that make up the `hfid` or `uniqueness_constraints` of the node. ```graphql mutation { @@ -166,17 +179,30 @@ mutation { data: { name: { value: "myrepop" }, # Attribute location: { value: "myrepop" }, # Attribute - account: { id: "myaccount" }, # Relationship One - tags: [ { id: "my_id" } ]} # Relationship Many + account: { hfid: ["my_account"] }, # Relationship One + tags: [ { hfid: ["my_tag"] } ]} # Relationship Many ) { ok object { id + hfid } } } ``` +#### Delete + +For a `Delete` mutation, we have to provide the `id` or the `hfid` of the node as part of the `data` argument. + +```graphql +mutation { + CoreRepositoryDelete(data: {hfid: ["myrepo"]}) { + ok + } +} +``` + ## Branch management In addition to the queries and the mutations automatically generated based on the schema, there are some queries and mutations to interact with the branches. diff --git a/docs/docs/topics/local-demo-environment.mdx b/docs/docs/topics/local-demo-environment.mdx index d96c7c2840..98dca32f9c 100644 --- a/docs/docs/topics/local-demo-environment.mdx +++ b/docs/docs/topics/local-demo-environment.mdx @@ -28,7 +28,7 @@ It's designed to be controlled by `invoke` using a list of predefined commands. | **message-queue** | rabbitmq:3.12-management | Message bus based on RabbitMQ | | **cache** | redis:7.2 | Cache based on Redis, mainly used for distributed lock | | **infrahub-server** | Dockerfile | Instance of the API server, running GraphQL | -| **infrahub-git** | Dockerfile | Instance of the Git agent, managing the Git Repository | +| **infrahub-git** | Dockerfile | Instance of the Task worker, managing the Git Repository | diff --git a/docs/docs/topics/object-storage.mdx b/docs/docs/topics/object-storage.mdx index 0676f0ab43..b39a322a01 100644 --- a/docs/docs/topics/object-storage.mdx +++ b/docs/docs/topics/object-storage.mdx @@ -15,7 +15,7 @@ At this moment Infrahub supports using local storage, or AWS S3 storage backends ### Local storage -Infrahub can use local storage as a storage backend. It can be any directory on a filesystem that is attached to the system on which Infrahub runs. The only requirement is that all the Infrahub API servers and Git Agents need access to the filesystem. +Infrahub can use local storage as a storage backend. It can be any directory on a filesystem that is attached to the system on which Infrahub runs. The only requirement is that all the Infrahub API servers and Task workers need access to the filesystem. To setup Infrahub to use local storage backend you can use the following configuration: diff --git a/docs/docs/topics/permissions-roles.mdx b/docs/docs/topics/permissions-roles.mdx new file mode 100644 index 0000000000..9ef256bccb --- /dev/null +++ b/docs/docs/topics/permissions-roles.mdx @@ -0,0 +1,74 @@ +--- +title: Permissions and Roles +--- + +# Roles and permissions + +Roles and permissions are essential for controlling user access and behavior in **Infrahub**. Within the platform, they **offer exact control** over what users can see, modify, or control. + +The permissions system is split into two main types: **Global** and **Object-specific**. These permissions aid in defining what users are permitted to do on particular system objects or throughout the system. + +More information on users authentication, can be found in the [User management and authentication Topic](/topics/auth). + +## Overview + +Permissions fall into two categories: **Global** and **Object-specific**, while roles act as **convenient bundles** of permissions. To simplify things further, **Account Groups** let you manage permissions for multiple users at once. + +- **GlobalPermissions** gives users system-wide rights to perform specific actions. **[See full list of available global permissions](/reference/permissions.mdx#global-permissions).** +- **ObjectPermissions** are tied to individual objects within Infrahub and control what actions users can take on those objects. **[See full list of available object permissions](/reference/permissions.mdx#object-permissions).** +- **AccountRoles** are groups of permissions you can assign to accounts. +- **AccountGroups** allow you to manage permissions for multiple users all at once. + +Permissions are allocated to users through groups and roles. +For more detailed information on this allocation, you can check [Users permissions management Section](/topics/auth.mdx#users-permissions-management). + +## Types of permissions + +### Global permissions + +With a **GlobalPermission**, a user may act on the entire system, not just on particular objects. **A person with the authority to handle accounts, for instance, can do so globally.** +**The action is blocked** if the required permission is not granted. + +:::info Example: + +Take the `global:manage_accounts:allow_all` permission: + +- **Action**: `manage_accounts` +- **Decision**: `Allow` + +This gives the user the ability to manage all user accounts. + +::: + +### Object permissions + +**ObjectPermission** specifies actions, that apply to a certain kind of object. Actions like create, update, remove, and view are supported. Depending on the kind of object or branch, object permissions may be granted or refused. + +**Key features**: + +- **Supports wildcards (`*`)** to apply permissions across multiple object types. +- **Can define different permissions per branch types (default or non-default branches).** +- **Grants or denies actions based on the assigned permission.** + +:::info Example: + +Here are some examples of object permissions and their descriptions: + +| Identifier | Object Type | Action | Decision | Description | +|-----------------------------------------------|---------------|---------|-------------|---------------------------------------------------------------------------------------------| +| `object:*:*:create:allow_other` | `*` (all types)| `any` | `allow_other`| Allows creating any object, but only on non-default branches. | +| `object:*:*:view:allow_all` | `*` (all types)| `view` | `allow_all` | Allows viewing any object, anywhere, across both default and non-default branches. | +| `object:Builtin:Tag:update:deny` | `BuiltinTag` | `update`| `deny` | Denies the ability to update any object of type BuiltinTag, across all branches. | +| `object:*:Generic:view:allow_all` | `*Generic` | `view` | `allow_all` | Allows viewing all objects that contain 'Generic' in their type (example: LocationGeneric, DeviceGeneric) in all namespaces, across all branches. | + +::: + +## Future developments + +The authorization structure for Infrahub is constantly changing. Here are some exciting upcoming features: + +- **Attribute-based permissions**: Grant permissions at the attribute level within objects. +- **Metadata-based permissions**: Use metadata to specify access controls. +- **Group-based permissions**: Deepen the integration of group memberships for permission assignments. + +These new features will make Infrahub's permission system even more powerful and flexible in the future. diff --git a/docs/docs/topics/repository.mdx b/docs/docs/topics/repository.mdx index 58a2dbae2d..c9f367a809 100644 --- a/docs/docs/topics/repository.mdx +++ b/docs/docs/topics/repository.mdx @@ -23,13 +23,13 @@ See [this topic](/topics/infrahub-yml) for a full explanation of everything that ## Architecture {#architecture} -The [Infrahub web server](/reference/api-server) will never connect directly with external Git repositories. All interactions between Infrahub and remote Git repositories are handled by the [Git agent](/reference/git-agent). The Git agent(s) can work with any remote Git server that using either `git` or `http` protocols. The Infrahub web server can send commands to the Git agent via our message broker and the Git agent can send data back to the Infrahub web server via GraphQL mutations. +The [Infrahub web server](/reference/api-server) will never connect directly with external Git repositories. All interactions between Infrahub and remote Git repositories are handled by the [Task worker](/reference/git-agent). The Task worker(s) can work with any remote Git server that using either `git` or `http` protocols. The Infrahub web server can send commands to the Task worker via our message broker and the Task worker can send data back to the Infrahub web server via GraphQL mutations. ![](../media/repository_architecture.excalidraw.svg) -Infrahub stores all of the data that it needs for every remote repository in a directory defined by the `git.repositories_directory` setting in `infrahub.toml`. When the Git agent receives an instruction to update a remote repository, it pulls data from the remote repositories and saves it to the filesystem in the `git.repositories_directory` directory. The Git agent then parses the new data and sends the necessary GraphQL mutations to the Infrahub web server. Infrahub attempts to update `Repository` with any changes in the remote repository several times per minute. Read-only repositories are only updated when specifically requested. +Infrahub stores all of the data that it needs for every remote repository in a directory defined by the `git.repositories_directory` setting in `infrahub.toml`. When the Task worker receives an instruction to update a remote repository, it pulls data from the remote repositories and saves it to the filesystem in the `git.repositories_directory` directory. The Task worker then parses the new data and sends the necessary GraphQL mutations to the Infrahub web server. Infrahub attempts to update `Repository` with any changes in the remote repository several times per minute. Read-only repositories are only updated when specifically requested. -Please note that each Git agent must have access to the same directory on the file system so that they can share work among each other. +Please note that each Task worker must have access to the same directory on the file system so that they can share work among each other. ## Read-only Repository vs. Repository {#read-only-vs-core} @@ -45,13 +45,13 @@ Updates **to** remote | When merging Proposed Change | No ### Read-only Repository {#read-only-repository} -Read-only Repositories will only pull data from an external repository into Infrahub and will never push any data to the external repository. A Read-only Repository will pull changes from a single `ref` (branch, tag, or commit) into the Infrahub branch(es) on which it exists. Read-only repositories are not automatically updated. To update a Read-only Repository, you must manually update the `ref` property to a new value, then the Git agent will pull the appropriate commit and create the appropriate objects in Infrahub. +Read-only Repositories will only pull data from an external repository into Infrahub and will never push any data to the external repository. A Read-only Repository will pull changes from a single `ref` (branch, tag, or commit) into the Infrahub branch(es) on which it exists. Read-only repositories are not automatically updated. To update a Read-only Repository, you must manually update the `ref` property to a new value, then the Task worker will pull the appropriate commit and create the appropriate objects in Infrahub. See the [guide](/guides/repository) for instructions on pulling changes from read-only repositories in Infrahub. ### Repository {#repository} -When you create a `Repository`, Infrahub will try to pull every branch defined in the external repository and create an associated Infrahub branch with the same name and matching data according to what is defined in the `.infrahub.yml` configuration file on the particular remote branch. Infrahub will attempt to sync updates from the external repository several times per minute in a background task that runs on the Git agent(s). +When you create a `Repository`, Infrahub will try to pull every branch defined in the external repository and create an associated Infrahub branch with the same name and matching data according to what is defined in the `.infrahub.yml` configuration file on the particular remote branch. Infrahub will attempt to sync updates from the external repository several times per minute in a background task that runs on the Task worker(s). Editing a given GraphQL Query, Transform, Artifact Definition, or Schema within Infrahub **will not** result in those changes being pushed to the external repository and **could potentially be overwritten** when Infrahub pulls new commits from the external repository. Infrahub will only push changes to an external repository when a [Proposed Change](/topics/proposed-change) is merged for which the source and destination branch are both linked to branches on the same external repository. In this case, Infrahub will attempt to create a merge commit and push that commit to the destination branch on the external repository. @@ -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/docs/topics/resources-testing-framework.mdx b/docs/docs/topics/resources-testing-framework.mdx index 3d9fc9295c..d6c6279705 100644 --- a/docs/docs/topics/resources-testing-framework.mdx +++ b/docs/docs/topics/resources-testing-framework.mdx @@ -138,7 +138,7 @@ In this output we can see `infrahub-sdk-0.8.1` (0.8.1 being the Infrahub SDK ver Tests can also run as part of the Infrahub CI pipeline. This is a feature which allows to validate the proper behaviour of a proposed change. -This means that the Infrahub Git agent will take care of running the `pytest` process on behalf of users after creating a proposed change or updating it with new changes. User defined tests, if found, will be run as part of the CI pipeline and be logged in checks. One check per test is created which allows to see the outcome of it and an optional message that gives more details in case of failure. +This means that the Infrahub Task worker will take care of running the `pytest` process on behalf of users after creating a proposed change or updating it with new changes. User defined tests, if found, will be run as part of the CI pipeline and be logged in checks. One check per test is created which allows to see the outcome of it and an optional message that gives more details in case of failure. ## How testing work diff --git a/docs/docs/topics/schema.mdx b/docs/docs/topics/schema.mdx index 5530d15819..b09c0728ab 100644 --- a/docs/docs/topics/schema.mdx +++ b/docs/docs/topics/schema.mdx @@ -91,11 +91,107 @@ The `kind` of a model is generated by concatenating the `namespace` and the `nam #### Relationship kinds -- `Generic`: Default relationship without specific significance -- `Attribute`: Relationship of type Attribute are represented in the detailed view and the list view -- `Component`: Indicate a relationship with another node that is a component of the current node. Example: Interface is a component to a Device -- `Parent`: Indicate a relationship with another node that is a parent to the current node. Example: Device is a parent to an Interface -- `Group`: Indicate a relationship to a member or a subscriber of a group +- `Generic`: A flexible relationship with no specific functional significance. It is commonly used when an entity doesn't fit into specialized categories like `Component` or `Parent`. +- `Attribute`: A relationship where related entities' attributes appear directly in the detailed view and list views. It's used for linking key information, like statuses or roles. +- `Component`: This relationship indicates that one entity is part of another and appears in a separate tab in the detailed view of a node in the UI. It represents a composition-like relationship where one node is a component of the current node. +- `Parent`: This relationship defines a hierarchical link, with the parent entity often serving as a container or owner of another node. **Parent relationships are mandatory** and allow filtering in the UI, such as showing all components for a given parent. +- `Group`: Defines a relationship where a node (inheriting from `CoreNode`) is a member or subscriber to a group (inheriting from `CoreGroup`). These relationships appear in the "Manage Groups" form. +- `Profile`: A special relationship where a node is assigned a profile (inheriting from `CoreProfile`), visible during creation or updates through a "select profile" dropdown. + +:::info Complementary relationship + +Component and Parent typically belong together: +The `Component` relationship is typically paired with the `Parent` relationship. +This ensures a strong relationship in both directions, where the parent node can manage its components, and the component refers back to its parent. + +::: + +:::warning Cascade deletion +Relationships of kind `Component` include an implicit `on_delete: cascade`. This means that if you delete a node with a `Component` relationship, the related nodes connected by this relationship will also be deleted. +::: + +:::info Internal Usage + +Group and Profile are **internal** relationship: +The `Group` and `Profile` relationship kinds are internal types and should not be directly used by the user in their schema. +These are automatically handled by the system for managing memberships and configurations. + +::: + +To help you understand the relationship types better, here’s an example schema using a real-world model of **Car**, **Person**, **Wheel**, and **Car Group**. + +```yaml +version: "1.0" + +nodes: + - name: Car + namespace: Auto + description: "A vehicle used for transportation." + attributes: + - name: model + kind: Text + description: "The model of the car." + - name: year + kind: Number + description: "The manufacturing year of the car." + - name: license_plate + kind: Text + unique: true + description: "License plate number." + relationships: + - name: owner + peer: AutoPerson + kind: Attribute + cardinality: one + optional: false + - name: wheels + peer: AutoWheel + kind: Component + cardinality: many + + - name: Wheel + namespace: Auto + description: "A wheel of the car, a critical component for movement." + attributes: + - name: wheel_size + kind: Number + description: "Size of the wheel in inches." + - name: type + kind: Text + description: "Type of the wheel (e.g., alloy, steel)." + relationships: + # A wheel must belong to a car, hence the Parent relationship is mandatory + - name: car + peer: AutoCar + kind: Parent + cardinality: one + optional: false + + - name: Person + namespace: Auto + description: "A person who may own a car." + attributes: + - name: first_name + kind: Text + description: "First name of the person." + - name: last_name + kind: Text + description: "Last name of the person." + - name: driver_license_number + kind: Text + unique: true + description: "Driver's license number." + relationships: + - name: cars + peer: AutoCar + kind: Component + cardinality: many + optional: true +``` + +- A `Car` node might have an `owner` attribute linking it to a `Person`. This relationship will be visible in the car's detailed view. +- A `Wheel` is a component of a `Car`, meaning wheels are an essential part of the car. The wheels will be displayed in a separate "Components" tab. +- A `Car` can have a `ProfileCar` selected during its creation or update. The insurance details appear in the form where you can pick or assign a profile for the car.
Attribute kinds behavior in the UI @@ -143,18 +239,24 @@ The `kind` of a model is generated by concatenating the `namespace` and the `nam
+::: warning + +When you create a relationship of kind Component, automatically the `on_delete` property of the relationship will get set to the value `cascade`. This means that when you delete a node, all the related nodes of that relationship will also be deleted. + +::: + ### Uniqueness Constraints More complex uniqueness constraints, composed of multiple attributes and/or relationships can be defined at the Node or Generic level with the property `uniqueness_constraints`. It's possible to define multiple uniqueness constraints and each of them will be evaluated individually. -In the example below, the node schema `ExampleCar`, `["owner", "model__value"]` guarantee that a car will unique based on the `owner` and the `model` of the car. +In the example below, the node schema `ExampleCar`, `["owner", "model__value"]` guarantees that a car will be unique based on the `owner` and the `model` of the car. -`uniqueness_constraints` can be composed of a list of N number of attribute or relationship of cardinality one: +`uniqueness_constraints` can be composed of a list of N number of attributes or relationships of cardinality one: -- For an attribute, the valid format is `__value`. *Currently only value is supported but in the future the plan is to allow additional metadata to be used as well.* -- For a relationship, only the name of the relationship should be provided ``. *Only relationship of cardinality one are supported and the relationship must be mandatory.* +- For an attribute, the valid format is `__value`. *Currently only value is supported but in the future, the plan is to allow additional metadata to be used as well.* +- For a relationship, only the name of the relationship should be provided ``. *Only relationships of cardinality `one` are supported and the relationship must be mandatory.* ```yaml {10} showLineNumbers nodes: @@ -196,10 +298,10 @@ In the network industry: In the example below, each `ExamplePerson`, will have a `hfid` composed of his/her `lastname` and `firstname`. -`human_friendly_id` can be composed of N number of attribute or relationship of cardinality one: +`human_friendly_id` can be composed of N number of attributes or relationships of cardinality one: -- For an attribute, the valid format is `__value`. *Currently only value is supported but in the future the plan is to allow additional metadata to be used as well.* -- For a relationship, the name of the relationship and the name of a unique attribute must be provided `____value`. *Only relationship of cardinality one are supported and the relationship must be mandatory.* +- For an attribute, the valid format is `__value`. *Currently only value is supported but in the future, the plan is to allow additional metadata to be used as well.* +- For a relationship, the name of the relationship and the name of a unique attribute must be provided `____value`. *Only relationships of cardinality `one` are supported and the relationship must be mandatory.* ```yaml {4} showLineNumbers nodes: diff --git a/docs/docs/tutorials/getting-started/git-integration.mdx b/docs/docs/tutorials/getting-started/git-integration.mdx index 7098239cd3..b4c1355133 100644 --- a/docs/docs/tutorials/getting-started/git-integration.mdx +++ b/docs/docs/tutorials/getting-started/git-integration.mdx @@ -6,7 +6,7 @@ title: Integration with Git One of the three pillars Infrahub is built on is the idea of having unified storage for data and files. The data is stored in the graph database and the files are stored in Git. -When integrating a Git repository with Infrahub, the Git agent will ensure that both systems stay in sync at any time. Changes to branches or files in a Git repository will be synced to Infrahub automatically. +When integrating a Git repository with Infrahub, the Task worker will ensure that both systems stay in sync at any time. Changes to branches or files in a Git repository will be synced to Infrahub automatically. Please refer to [Repository](/topics/repository) to learn more about it. @@ -58,7 +58,7 @@ After adding the `infrahub-demo-edge` repository you will be able to see several :::note Troubleshooting -If you don't see additional objects under the transformations or `GraphQL Queries`, it's possible that the `Git agent` might not be running anymore. +If you don't see additional objects under the transformations or `GraphQL Queries`, it's possible that the `Task worker` might not be running anymore. In this case, you should run `invoke demo.start` first to ensure that everything is working. diff --git a/docs/docs/tutorials/getting-started/introduction-to-infrahub.mdx b/docs/docs/tutorials/getting-started/introduction-to-infrahub.mdx index 86e59711f3..d367972c57 100644 --- a/docs/docs/tutorials/getting-started/introduction-to-infrahub.mdx +++ b/docs/docs/tutorials/getting-started/introduction-to-infrahub.mdx @@ -17,7 +17,8 @@ During this tutorial we'll mainly use the Frontend, the `infrahubctl` CLI and th | **infrahubctl** | Command line utility to interact with Infrahub and manage some core objects like the branches or the schema. | `invoke demo.cli-git` | | **Frontend** | Main User interface | [http://localhost:8000](http://localhost:8000) | | **API server** | GraphQL and REST API server, primary component to interact with the data. | [http://localhost:8000/graphql](http://localhost:8000/graphql) | -| **Git agent** | Infrahub agent that manages all content hosted in Git. | --- | +| **Task manager** | Orchestrator of workflow tasks | --- | +| **Task worker** | Infrahub agent that manages all content hosted in Git. | --- | | **Git server** | External Git server like GitHub or GitLab that can host some Git repositories. | --- | | **GraphDB** | Main database based on neo4j where all information in the graph are stored. | --- | | **Cache** | Cache based on Redis. Mainly used to support the reservation of shared resources across all components. | --- | diff --git a/docs/docs/tutorials/getting-started/readme.mdx b/docs/docs/tutorials/getting-started/readme.mdx index 93438bbee5..15b5e61924 100644 --- a/docs/docs/tutorials/getting-started/readme.mdx +++ b/docs/docs/tutorials/getting-started/readme.mdx @@ -79,12 +79,18 @@ Refer to [User management](/topics/auth/) page for more information regarding th To follow the tutorial you should use the `admin` account but you can try the other accounts too to see how the interface behaves with different permission levels. -| name | username | password | role | -| ------------- | --------------- | ------------- | ---------- | -| Administrator | `admin` | `infrahub` | admin | -| Chloe O'Brian | `Chloe O'Brian` | `Password123` | read-write | -| David Palmer | `David Palmer` | `Password123` | read-write | -| Jack Bauer | `Jack Bauer` | `Password123` | read-only | +| name | username | password | group | +| --------------- | --------------- | ------------- | -------------------- | +| Administrator | `admin` | `infrahub` | Super Administrators | +| Sue Dough | `sudo` | `Password123` | Administrators | +| Chloe O'Brian | `cobrian` | `Password123` | Engineering Team | +| Sofia Hernandez | `shernandez` | `Password123` | Engineering Team | +| Ryan Patel | `rpatel` | `Password123` | Engineering Team | +| Jack Bauer | `jbauer` | `Password123` | Operations Team | +| Emily Lawson | `elawson` | `Password123` | Operations Team | +| Jacob Thompson | `jthompson` | `Password123` | Operations Team | +| David Palmer | `dpalmer` | `Password123` | Architecture Team | +| Olivia Carter | `ocarter` | `Password123` | Architecture Team | ## Access the Infrahub interfaces diff --git a/docs/package-lock.json b/docs/package-lock.json index d7f990e744..00f962aa43 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" } @@ -5735,9 +5993,9 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" }, "node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", "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,36 @@ } }, "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.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", + "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", "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": "0.7.1", "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 +7549,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 +7755,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 +7776,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 +7784,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 +8024,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 +8810,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 +8898,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 +8993,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 +9334,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 +9351,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 +9366,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 +9544,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 +9608,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 +9620,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 +10084,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 +10110,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 +11811,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 +11826,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 +12027,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 +12041,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 +12064,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 +12109,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 +12364,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 +12428,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 +12455,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 +12553,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 +12570,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 +12714,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 +12884,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 +13092,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 +13143,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 +13181,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 +13197,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 +13283,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 +13355,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 +13370,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 +13462,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 +13592,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 +13636,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 +13649,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 +14204,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 +14265,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 +14274,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 +14301,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 +14381,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 +14408,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 +14416,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 +14542,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 +14575,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 +14641,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 +14679,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 +14700,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 +14722,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 +14743,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 +14760,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 +14838,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 +14974,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 +15014,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 +15229,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 +15325,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 +15338,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 +15347,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 +15364,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 +15376,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 +15566,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 +15864,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 +15893,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 +15918,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 +16221,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 8f1aa4fc80..22ff56b098 100644 --- a/docs/sidebars.ts +++ b/docs/sidebars.ts @@ -67,6 +67,8 @@ const sidebars: SidebarsConfig = { 'guides/installation', 'guides/create-schema', 'guides/import-schema', + 'guides/menu', + 'guides/accounts-permissions', 'guides/groups', 'guides/generator', 'guides/repository', @@ -76,6 +78,7 @@ const sidebars: SidebarsConfig = { 'guides/database-backup', 'guides/profiles', 'guides/object-storage', + 'guides/sso', 'guides/check', 'guides/resource-manager', 'guides/managing-api-tokens', @@ -101,10 +104,10 @@ const sidebars: SidebarsConfig = { 'topics/groups', 'topics/metadata', 'topics/object-storage', + 'topics/permissions-roles', 'topics/version-control', 'topics/proposed-change', 'topics/repository', - 'topics/schema', 'topics/transformation', 'topics/auth', 'topics/database-backup', @@ -138,6 +141,7 @@ const sidebars: SidebarsConfig = { 'reference/schema/validator-migration', ], }, + 'reference/menu', { type: 'category', label: 'infrahub cli', @@ -157,6 +161,7 @@ const sidebars: SidebarsConfig = { 'reference/api-server', 'reference/dotinfrahub', 'reference/infrahub-tests', + 'reference/permissions', 'reference/schema-validation' ], }, @@ -307,7 +312,8 @@ const sidebars: SidebarsConfig = { slug: 'release-notes/infrahub', }, items: [ - // 'release-notes/infrahub/release-1_0-DRAFT', + // 'release-notes/infrahub/release-1_0_1-DRAFT', + 'release-notes/infrahub/release-1_0', 'release-notes/infrahub/release-0_16_4', 'release-notes/infrahub/release-0_16_3', 'release-notes/infrahub/release-0_16_2', 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..bf337a6dc0 --- /dev/null +++ b/frontend/app/biome.json @@ -0,0 +1,132 @@ +{ + "$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": "error", + "noUndeclaredVariables": "off", + "noUnusedFunctionParameters": "off", + "noUnusedImports": "error", + "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": { + "level": "error", + "options": { "allow": ["assert", "error", "info", "warn"] } + }, + "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 + } + }, + "graphql": { + "formatter": { + "enabled": true + }, + "linter": { + "enabled": 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/cypress/support/e2e.ts b/frontend/app/cypress/support/e2e.ts index 29de13d834..416fbbdd21 100644 --- a/frontend/app/cypress/support/e2e.ts +++ b/frontend/app/cypress/support/e2e.ts @@ -25,8 +25,8 @@ Cypress.Commands.add("login", (username: string, password: string) => { cy.session( [username, password], () => { - cy.visit("/signin"); - cy.contains("Sign in to your account").should("be.visible"); + cy.visit("/login"); + cy.contains("Log in to your account").should("be.visible"); cy.get(":nth-child(1) > .relative > .block").type(username); 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..57fb9fb7da 100644 --- a/frontend/app/package-lock.json +++ b/frontend/app/package-lock.json @@ -1,7 +1,7 @@ { "name": "frontend", "version": "0.1.0", - "lockfileVersion": 2, + "lockfileVersion": 3, "requires": true, "packages": { "": { @@ -9,22 +9,26 @@ "version": "0.1.0", "dependencies": { "@apollo/client": "^3.9.10", - "@codemirror/commands": "^6.3.3", - "@codemirror/lang-markdown": "^6.2.4", + "@codemirror/commands": "^6.7.0", + "@codemirror/lang-markdown": "^6.3.0", + "@codemirror/language": "^6.10.3", "@codemirror/state": "^6.4.1", "@codemirror/theme-one-dark": "^6.1.2", - "@codemirror/view": "^6.26.1", - "@graphiql/plugin-explorer": "^1.0.4", + "@codemirror/view": "^6.34.1", + "@graphiql/plugin-explorer": "^3.2.2", + "@graphiql/toolkit": "^0.11.0", "@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-accordion": "^1.2.1", "@radix-ui/react-dropdown-menu": "^2.0.6", "@radix-ui/react-label": "^2.0.2", "@radix-ui/react-popover": "^1.0.7", "@radix-ui/react-progress": "^1.0.3", + "@radix-ui/react-scroll-area": "^1.2.0", "@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-tooltip": "^1.0.7", "@svgr/rollup": "^8.1.0", @@ -36,12 +40,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", + "graphiql": "^3.7.1", "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", @@ -57,6 +62,7 @@ "react-markdown": "^9.0.1", "react-paginate": "^8.2.0", "react-popper": "^2.3.0", + "react-resizable-panels": "^2.1.5", "react-router-dom": "^6.22.3", "react-simple-code-editor": "^0.13.1", "react-toastify": "^9.1.3", @@ -66,16 +72,17 @@ "sha1": "^1.1.1", "subscriptions-transport-ws": "^0.11.0", "tailwind-merge": "^2.2.2", + "tailwindcss-animate": "^1.0.7", "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,46 +93,23 @@ "@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", - "husky": "^8.0.3", + "cypress": "^13.15.0", "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", "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "license": "MIT", "engines": { "node": ">=10" }, @@ -134,21 +118,23 @@ } }, "node_modules/@ampproject/remapping": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", - "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "license": "Apache-2.0", "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" } }, "node_modules/@apollo/client": { - "version": "3.9.10", - "resolved": "https://registry.npmjs.org/@apollo/client/-/client-3.9.10.tgz", - "integrity": "sha512-w8i/Lk1P0vvWZF0Xb00XPonn79/0rgRJ1vopBlVudVuy9QP29/NZXK0rI2xJIN6VrKuEqJZaVGJC+7k23I2sfA==", + "version": "3.11.8", + "resolved": "https://registry.npmjs.org/@apollo/client/-/client-3.11.8.tgz", + "integrity": "sha512-CgG1wbtMjsV2pRGe/eYITmV5B8lXUCYljB2gB/6jWTFQcrvirUVvKg7qtFdjYkQSFbIffU1IDyxgeaN81eTjbA==", + "license": "MIT", "dependencies": { "@graphql-typed-document-node/core": "^3.1.1", "@wry/caches": "^1.0.0", @@ -158,7 +144,7 @@ "hoist-non-react-statics": "^3.3.2", "optimism": "^0.18.0", "prop-types": "^15.7.2", - "rehackt": "0.0.6", + "rehackt": "^0.1.0", "response-iterator": "^0.2.6", "symbol-observable": "^4.0.0", "ts-invariant": "^0.10.3", @@ -168,8 +154,8 @@ "peerDependencies": { "graphql": "^15.0.0 || ^16.0.0", "graphql-ws": "^5.5.5", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0", "subscriptions-transport-ws": "^0.9.0 || ^0.11.0" }, "peerDependenciesMeta": { @@ -192,6 +178,7 @@ "resolved": "https://registry.npmjs.org/@ardatan/relay-compiler/-/relay-compiler-12.0.0.tgz", "integrity": "sha512-9anThAaj1dQr6IGmzBMcfzOQKTa5artjuPmw8NYK/fiGEMjADbSguBY2FMDykt+QhilR3wc9VA/3yVju7JHg7Q==", "dev": true, + "license": "MIT", "dependencies": { "@babel/core": "^7.14.0", "@babel/generator": "^7.14.0", @@ -223,6 +210,7 @@ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -232,75 +220,26 @@ "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", "dev": true, + "license": "ISC", "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^6.2.0" } }, - "node_modules/@ardatan/relay-compiler/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/@ardatan/relay-compiler/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/@ardatan/relay-compiler/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/@ardatan/relay-compiler/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/@ardatan/relay-compiler/node_modules/y18n": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/@ardatan/relay-compiler/node_modules/yargs": { "version": "15.4.1", "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", "dev": true, + "license": "MIT", "dependencies": { "cliui": "^6.0.0", "decamelize": "^1.2.0", @@ -323,6 +262,7 @@ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", "dev": true, + "license": "ISC", "dependencies": { "camelcase": "^5.0.0", "decamelize": "^1.2.0" @@ -336,6 +276,7 @@ "resolved": "https://registry.npmjs.org/@ardatan/sync-fetch/-/sync-fetch-0.0.1.tgz", "integrity": "sha512-xhlTqH0m31mnsG0tIP4ETgfSB6gXDaYYsUWTrlUV93fFQPI9dd8hE0Ot6MHLCtqgB32hwJAC3YZMWlXZw7AleA==", "dev": true, + "license": "MIT", "dependencies": { "node-fetch": "^2.6.1" }, @@ -344,104 +285,43 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", - "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.25.7.tgz", + "integrity": "sha512-0xZJFNE5XMpENsgfHYTw8FbX4kv53mFLn2i3XPoq69LyhYSCBJtitaHx9QnsVTrsogI4Z3+HtEfZ2/GFPOtf5g==", + "license": "MIT", "dependencies": { - "@babel/highlight": "^7.23.4", - "chalk": "^2.4.2" + "@babel/highlight": "^7.25.7", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/code-frame/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/code-frame/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, - "node_modules/@babel/code-frame/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==", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/code-frame/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/compat-data": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.1.tgz", - "integrity": "sha512-Pc65opHDliVpRHuKfzI+gSA4zcgr65O4cl64fFJIWEEh8JoHIHh0Oez1Eo8Arz8zq/JhgKodQaxEwUPRtZylVA==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.8.tgz", + "integrity": "sha512-ZsysZyXY4Tlx+Q53XdnOFmqwfB9QDTHYxaZYajWRoBLuLEAwI2UIbtxOjWh/cFaa9IKUlcB+DDuoskLuKu56JA==", + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.9.tgz", - "integrity": "sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.8.tgz", + "integrity": "sha512-Oixnb+DzmRT30qu9d3tJSQkxuygWm32DFykT4bRoORPa9hZ/L4KhVB/XiRm6KG+roIEM7DBQlmg27kw2HZkdZg==", + "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.6", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helpers": "^7.23.9", - "@babel/parser": "^7.23.9", - "@babel/template": "^7.23.9", - "@babel/traverse": "^7.23.9", - "@babel/types": "^7.23.9", + "@babel/code-frame": "^7.25.7", + "@babel/generator": "^7.25.7", + "@babel/helper-compilation-targets": "^7.25.7", + "@babel/helper-module-transforms": "^7.25.7", + "@babel/helpers": "^7.25.7", + "@babel/parser": "^7.25.8", + "@babel/template": "^7.25.7", + "@babel/traverse": "^7.25.7", + "@babel/types": "^7.25.8", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -457,49 +337,54 @@ } }, "node_modules/@babel/generator": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", - "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.7.tgz", + "integrity": "sha512-5Dqpl5fyV9pIAD62yK9P7fcA768uVPUyrQmqpqstHWgMma4feF1x/oFysBCVZLY5wJ2GkMUCdsNDnGZrPoR6rA==", + "license": "MIT", "dependencies": { - "@babel/types": "^7.23.6", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" + "@babel/types": "^7.25.7", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", - "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.7.tgz", + "integrity": "sha512-4xwU8StnqnlIhhioZf1tqnVWeQ9pvH/ujS8hRfw/WOza+/a+1qv69BWNy+oY231maTCWgKWhfBU7kDpsds6zAA==", + "license": "MIT", "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.25.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz", - "integrity": "sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.25.7.tgz", + "integrity": "sha512-12xfNeKNH7jubQNm7PAkzlLwEmCs1tfuX3UjIw6vP6QXi+leKh6+LyC/+Ed4EIQermwd58wsyh070yjDHFlNGg==", + "license": "MIT", "dependencies": { - "@babel/types": "^7.22.15" + "@babel/traverse": "^7.25.7", + "@babel/types": "^7.25.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", - "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.7.tgz", + "integrity": "sha512-DniTEax0sv6isaw6qSQSfV4gVRNtw2rte8HHM45t9ZR0xILaufBRNkpMifCRiAPyvL4ACD6v0gfCwCmtOQaV4A==", + "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.23.5", - "@babel/helper-validator-option": "^7.23.5", - "browserslist": "^4.22.2", + "@babel/compat-data": "^7.25.7", + "@babel/helper-validator-option": "^7.25.7", + "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, @@ -508,18 +393,17 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.1.tgz", - "integrity": "sha512-1yJa9dX9g//V6fDebXoEfEsxkZHk3Hcbm+zLhyu6qVgYFLvmTALTeV+jNU9e5RnYtioBrGEOdoI2joMSNQ/+aA==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-member-expression-to-functions": "^7.23.0", - "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-replace-supers": "^7.24.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.7.tgz", + "integrity": "sha512-bD4WQhbkx80mAyj/WCm4ZHcF4rDxkoLFO6ph8/5/mQ3z4vAzltQXAmbc7GvVJx5H+lk5Mi5EmbTeox5nMGCsbw==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.7", + "@babel/helper-member-expression-to-functions": "^7.25.7", + "@babel/helper-optimise-call-expression": "^7.25.7", + "@babel/helper-replace-supers": "^7.25.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.7", + "@babel/traverse": "^7.25.7", "semver": "^6.3.1" }, "engines": { @@ -530,12 +414,13 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz", - "integrity": "sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.7.tgz", + "integrity": "sha512-byHhumTj/X47wJ6C6eLpK7wW/WBEcnUeb7D0FNc/jFQnQVw7DOso3Zz5u9x/zLrFVkHa89ZGDbkAa1D54NdrCQ==", + "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "regexpu-core": "^5.3.1", + "@babel/helper-annotate-as-pure": "^7.25.7", + "regexpu-core": "^6.1.1", "semver": "^6.3.1" }, "engines": { @@ -546,9 +431,10 @@ } }, "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.1.tgz", - "integrity": "sha512-o7SDgTJuvx5vLKD6SFvkydkSMBvahDKGiNJzG22IZYXhiqoe9efY7zocICBgzHV4IRg5wdgl2nEL/tulKIEIbA==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.2.tgz", + "integrity": "sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==", + "license": "MIT", "dependencies": { "@babel/helper-compilation-targets": "^7.22.6", "@babel/helper-plugin-utils": "^7.22.5", @@ -560,69 +446,42 @@ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", - "dependencies": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz", - "integrity": "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.7.tgz", + "integrity": "sha512-O31Ssjd5K6lPbTX9AAYpSKrZmLeagt9uwschJd+Ixo6QiRyfpvgtVQp8qrDR9UNFjZ8+DO34ZkdrN+BnPXemeA==", + "license": "MIT", "dependencies": { - "@babel/types": "^7.23.0" + "@babel/traverse": "^7.25.7", + "@babel/types": "^7.25.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.24.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz", - "integrity": "sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.7.tgz", + "integrity": "sha512-o0xCgpNmRohmnoWKQ0Ij8IdddjyBFE4T2kagL/x6M3+4zUgc+4qTOUBoNe4XxDskt1HPKO007ZPiMgLDq2s7Kw==", + "license": "MIT", "dependencies": { - "@babel/types": "^7.24.0" + "@babel/traverse": "^7.25.7", + "@babel/types": "^7.25.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", - "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.7.tgz", + "integrity": "sha512-k/6f8dKG3yDz/qCwSM+RKovjMix563SLxQFo0UhRNo239SP6n9u5/eLtKD6EAjwta2JHJ49CsD8pms2HdNiMMQ==", + "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.20" + "@babel/helper-module-imports": "^7.25.7", + "@babel/helper-simple-access": "^7.25.7", + "@babel/helper-validator-identifier": "^7.25.7", + "@babel/traverse": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -632,32 +491,35 @@ } }, "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", - "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.7.tgz", + "integrity": "sha512-VAwcwuYhv/AT+Vfr28c9y6SHzTan1ryqrydSTFGjU0uDJHw3uZ+PduI8plCLkRsDnqK2DMEDmwrOQRsK/Ykjng==", + "license": "MIT", "dependencies": { - "@babel/types": "^7.22.5" + "@babel/types": "^7.25.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.0.tgz", - "integrity": "sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.7.tgz", + "integrity": "sha512-eaPZai0PiqCi09pPs3pAFfl/zYgGaE6IdXtYvmf0qlcDTd3WCtO7JWCcRd64e0EQrcYgiHibEZnOGsSY4QSgaw==", + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz", - "integrity": "sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.7.tgz", + "integrity": "sha512-kRGE89hLnPfcz6fTrlNU+uhgcwv0mBE4Gv3P9Ke9kLVJYpi4AMVVEElXvB5CabrPZW4nCM8P8UyyjrzCM0O2sw==", + "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-wrap-function": "^7.22.20" + "@babel/helper-annotate-as-pure": "^7.25.7", + "@babel/helper-wrap-function": "^7.25.7", + "@babel/traverse": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -667,13 +529,14 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.24.1.tgz", - "integrity": "sha512-QCR1UqC9BzG5vZl8BMicmZ28RuUBnHhAMddD8yHFHDRH9lLTZ9uUPehX8ctVPT8l0TKblJidqcgUUKGVrePleQ==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.7.tgz", + "integrity": "sha512-iy8JhqlUW9PtZkd4pHM96v6BdJ66Ba9yWSE4z0W4TvSZwLBPkyDsiIU3ENe4SmrzRBs76F7rQXTy1lYC49n6Lw==", + "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-member-expression-to-functions": "^7.23.0", - "@babel/helper-optimise-call-expression": "^7.22.5" + "@babel/helper-member-expression-to-functions": "^7.25.7", + "@babel/helper-optimise-call-expression": "^7.25.7", + "@babel/traverse": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -683,96 +546,95 @@ } }, "node_modules/@babel/helper-simple-access": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", - "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.25.7.tgz", + "integrity": "sha512-FPGAkJmyoChQeM+ruBGIDyrT2tKfZJO8NcxdC+CWNJi7N8/rZpSxK7yvBJ5O/nF1gfu5KzN7VKG3YVSLFfRSxQ==", + "license": "MIT", "dependencies": { - "@babel/types": "^7.22.5" + "@babel/traverse": "^7.25.7", + "@babel/types": "^7.25.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", - "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.7.tgz", + "integrity": "sha512-pPbNbchZBkPMD50K0p3JGcFMNLVUCuU/ABybm/PGNj4JiHrpmNyqqCphBk4i19xXtNV0JhldQJJtbSW5aUvbyA==", + "license": "MIT", "dependencies": { - "@babel/types": "^7.22.5" + "@babel/traverse": "^7.25.7", + "@babel/types": "^7.25.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", - "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.7.tgz", + "integrity": "sha512-CbkjYdsJNHFk8uqpEkpCvRs3YRp9tY6FmFY7wLMSYuGYkrdUi7r2lc4/wqsvlHoMznX3WJ9IP8giGPq68T/Y6g==", + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.7.tgz", + "integrity": "sha512-AM6TzwYqGChO45oiuPqwL2t20/HdMC1rTPAesnBCgPCSF1x3oN9MVUwQV2iyz4xqWrctwK5RNC8LV22kaQCNYg==", + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", - "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.7.tgz", + "integrity": "sha512-ytbPLsm+GjArDYXJ8Ydr1c/KJuutjF2besPNbIZnZ6MKUxi/uTA22t2ymmA4WFjZFpjiAMO0xuuJPqK2nvDVfQ==", + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.20.tgz", - "integrity": "sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.7.tgz", + "integrity": "sha512-MA0roW3JF2bD1ptAaJnvcabsVlNQShUaThyJbCDD4bCp8NEgiFvpoqRI2YS22hHlc2thjO/fTg2ShLMC3jygAg==", + "license": "MIT", "dependencies": { - "@babel/helper-function-name": "^7.22.5", - "@babel/template": "^7.22.15", - "@babel/types": "^7.22.19" + "@babel/template": "^7.25.7", + "@babel/traverse": "^7.25.7", + "@babel/types": "^7.25.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.9.tgz", - "integrity": "sha512-87ICKgU5t5SzOT7sBMfCOZQ2rHjRU+Pcb9BoILMYz600W6DkVRLFBPwQ18gwUVvggqXivaUakpnxWQGbpywbBQ==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.7.tgz", + "integrity": "sha512-Sv6pASx7Esm38KQpF/U/OXLwPPrdGHNKoeblRxgZRLXnAtnkEe4ptJPDtAZM7fBLadbc1Q07kQpSiGQ0Jg6tRA==", + "license": "MIT", "dependencies": { - "@babel/template": "^7.23.9", - "@babel/traverse": "^7.23.9", - "@babel/types": "^7.23.9" + "@babel/template": "^7.25.7", + "@babel/types": "^7.25.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", - "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.25.7.tgz", + "integrity": "sha512-iYyACpW3iW8Fw+ZybQK+drQre+ns/tKpXbNESfrhNnPLIklLbXr7MYJ6gPEd0iETGLOK+SxMjVvKb/ffmk+FEw==", + "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", + "@babel/helper-validator-identifier": "^7.25.7", "chalk": "^2.4.2", - "js-tokens": "^4.0.0" + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" @@ -782,6 +644,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "license": "MIT", "dependencies": { "color-convert": "^1.9.0" }, @@ -793,6 +656,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "license": "MIT", "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -806,6 +670,7 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", "dependencies": { "color-name": "1.1.3" } @@ -813,20 +678,14 @@ "node_modules/@babel/highlight/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, - "node_modules/@babel/highlight/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==", - "engines": { - "node": ">=0.8.0" - } + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "license": "MIT" }, "node_modules/@babel/highlight/node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "license": "MIT", "engines": { "node": ">=4" } @@ -835,6 +694,7 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "license": "MIT", "dependencies": { "has-flag": "^3.0.0" }, @@ -843,9 +703,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.1.tgz", - "integrity": "sha512-Zo9c7N3xdOIQrNip7Lc9wvRPzlRtovHVE4lkz8WEDr7uYh/GMQhSiIgFxGIArRHYdJE5kxtZjAf8rT0xhdLCzg==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.8.tgz", + "integrity": "sha512-HcttkxzdPucv3nNFmfOOMfFf64KgdJVqm1KaCm25dPGMLElo9nsLvXeJECQg8UzPuBGLyTSA0ZzqCtDSzKTEoQ==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.25.8" + }, "bin": { "parser": "bin/babel-parser.js" }, @@ -853,12 +717,44 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.7.tgz", + "integrity": "sha512-UV9Lg53zyebzD1DwQoT9mzkEKa922LNUp5YkTJ6Uta0RbyXaQNUgcvSt7qIu1PpPzVb6rd10OVNTzkyBGeVmxQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/traverse": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.7.tgz", + "integrity": "sha512-GDDWeVLNxRIkQTnJn2pDOM1pkCgYdSqPeT1a9vh9yIqu2uzzgw1zcqEb+IJOhy+dTBMlNdThrDIksr2o09qrrQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.24.1.tgz", - "integrity": "sha512-y4HqEnkelJIOQGd+3g1bTeKsA5c6qM7eOn7VggGVbBc0y8MLSKHacwcIE2PplNlQSj0PqS9rrXL/nkPVK+kUNg==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.7.tgz", + "integrity": "sha512-wxyWg2RYaSUYgmd9MR0FyRGyeOMQE/Uzr1wzd/g5cf5bwi9A4v6HFdDm7y1MgDtod/fLOSTZY6jDgV0xU9d5bA==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -868,13 +764,14 @@ } }, "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.24.1.tgz", - "integrity": "sha512-Hj791Ii4ci8HqnaKHAlLNs+zaLXb0EzSDhiAWp5VNlyvCNymYfacs64pxTxbH1znW/NcArSmwpmG9IKE/TUVVQ==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.7.tgz", + "integrity": "sha512-Xwg6tZpLxc4iQjorYsyGMyfJE7nP5MV8t/Ka58BgiA7Jw0fRqQNcANlLfdJ/yvBt9z9LD2We+BEkT7vLqZRWng==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/plugin-transform-optional-chaining": "^7.24.1" + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.7", + "@babel/plugin-transform-optional-chaining": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -884,12 +781,13 @@ } }, "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.24.1.tgz", - "integrity": "sha512-m9m/fXsXLiHfwdgydIFnpk+7jlVbnvlK5B2EKiPdLUb6WX654ZaaEWJUjk8TftRbZpK0XibovlLWX4KIZhV6jw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.7.tgz", + "integrity": "sha512-UVATLMidXrnH+GMUIuxq55nejlj02HP7F5ETyBONzP6G87fPBogG4CH6kxrSrdIuAjdwNO9VzyaYsrZPscWUrw==", + "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/traverse": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -904,6 +802,7 @@ "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead.", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-create-class-features-plugin": "^7.18.6", "@babel/helper-plugin-utils": "^7.18.6" @@ -921,6 +820,7 @@ "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==", "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-object-rest-spread instead.", "dev": true, + "license": "MIT", "dependencies": { "@babel/compat-data": "^7.20.5", "@babel/helper-compilation-targets": "^7.20.7", @@ -939,6 +839,7 @@ "version": "7.21.0-placeholder-for-preset-env.2", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "license": "MIT", "engines": { "node": ">=6.9.0" }, @@ -946,21 +847,12 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-syntax-class-properties": { "version": "7.12.13", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.12.13" }, @@ -968,12 +860,14 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "node_modules/@babel/plugin-syntax-flow": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.25.7.tgz", + "integrity": "sha512-fyoj6/YdVtlv2ROig/J0fP7hh/wNO1MJGm1NR70Pg7jbkF+jOUL9joorqaCOQh06Y+LfgTagHzC8KqZ3MF782w==", + "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -982,35 +876,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-flow": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.24.1.tgz", - "integrity": "sha512-sxi2kLTI5DeW5vDtMUsk4mTPwvlUDbjOnoWayhynCwrw4QXRld4QEYwqzY8JmQXaJUtgUuCIurtSRH5sn4c7mA==", - "dev": true, + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.25.7.tgz", + "integrity": "sha512-ZvZQRmME0zfJnDQnVBKYzHxXT7lYBB3Revz1GuS7oLXWMgqUPX4G+DDbT30ICClht9WKV34QVrZhSw6WdklwZQ==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1019,12 +891,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.24.1.tgz", - "integrity": "sha512-IuwnI5XnuF189t91XbxmXeCDz3qs6iDRO7GJ++wcfgeXNs/8FmIlKcpDSXNVyuLQxlwvskmI3Ct73wUODkJBlQ==", + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.25.7.tgz", + "integrity": "sha512-AqVo+dguCgmpi/3mYBdu9lkngOBlQ2w2vnNpa6gfiCxQZLzV4ZbhsXitJ2Yblkoe1VQwtHSaNmIaGll/26YWRw==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1033,12 +906,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.24.1.tgz", - "integrity": "sha512-zhQTMH0X2nVLnb04tz+s7AMuasX8U0FnpE+nHTOhSOINjWMnopoZTxtIKsd45n4GQ/HIZLyfIpoul8e2m0DnRA==", + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.7.tgz", + "integrity": "sha512-ruZOnKO+ajVL/MVx+PwNBPOkrnXTXoWMtte1MBpegfCArhqOe3Bj52avVj1huLLxNKYKXYaSxZ2F+woK1ekXfw==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1047,79 +921,12 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.1.tgz", - "integrity": "sha512-2eCtxZXf+kbkMIsXS4poTvT4Yu5rXiRa+9xGVT56raghjmBTKMpFNc9R4IDiB4emao9eO22Ox7CxuJG7BgExqA==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-syntax-object-rest-spread": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -1127,62 +934,13 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.1.tgz", - "integrity": "sha512-Yhnmvy5HZEnHUty6i++gcfH1/l68AHnItFHnaCv6hn9dNh0hQvvQJsxpi4BMBFN5DLeHBuucT/0DgzXif/OyRw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.7.tgz", + "integrity": "sha512-rR+5FDjpCHqqZN2bzZm18bVYGaejGq5ZkpVCJLXor/+zlSrSoc4KWcHI0URVWjl/68Dyr1uwZUz/1njycEAv9g==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1195,6 +953,7 @@ "version": "7.18.6", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "license": "MIT", "dependencies": { "@babel/helper-create-regexp-features-plugin": "^7.18.6", "@babel/helper-plugin-utils": "^7.18.6" @@ -1207,11 +966,12 @@ } }, "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.24.1.tgz", - "integrity": "sha512-ngT/3NkRhsaep9ck9uj2Xhv9+xB1zShY3tM3g6om4xxCELwCDN4g4Aq5dRn48+0hasAql7s2hdBOysCfNpr4fw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.7.tgz", + "integrity": "sha512-EJN2mKxDwfOUCPxMO6MUI58RN3ganiRAG/MS/S3HfB6QFNjroAMelQo/gybyYq97WerCBAZoyrAoW8Tzdq2jWg==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1221,14 +981,14 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.24.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.24.3.tgz", - "integrity": "sha512-Qe26CMYVjpQxJ8zxM1340JFNjZaF+ISWpr1Kt/jGo+ZTUzKkfw/pphEWbRCb+lmSM6k/TOgfYLvmbHkUQ0asIg==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.8.tgz", + "integrity": "sha512-9ypqkozyzpG+HxlH4o4gdctalFGIjjdufzo7I2XPda0iBnZ6a+FO0rIEQcdSPXp02CkvGsII1exJhmROPQd5oA==", + "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-remap-async-to-generator": "^7.22.20", - "@babel/plugin-syntax-async-generators": "^7.8.4" + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-remap-async-to-generator": "^7.25.7", + "@babel/traverse": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1238,13 +998,14 @@ } }, "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.24.1.tgz", - "integrity": "sha512-AawPptitRXp1y0n4ilKcGbRYWfbbzFWz2NqNu7dacYDtFtz0CMjG64b3LQsb3KIgnf4/obcUL78hfaOS7iCUfw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.7.tgz", + "integrity": "sha512-ZUCjAavsh5CESCmi/xCpX1qcCaAglzs/7tmuvoFnJgA1dM7gQplsguljoTg+Ru8WENpX89cQyAtWoaE0I3X3Pg==", + "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.24.1", - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-remap-async-to-generator": "^7.22.20" + "@babel/helper-module-imports": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-remap-async-to-generator": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1254,11 +1015,12 @@ } }, "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.24.1.tgz", - "integrity": "sha512-TWWC18OShZutrv9C6mye1xwtam+uNi2bnTOCBUd5sZxyHOiWbU6ztSROofIMrK84uweEZC219POICK/sTYwfgg==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.25.7.tgz", + "integrity": "sha512-xHttvIM9fvqW+0a3tZlYcZYSBpSWzGBFIt/sYG3tcdSzBB8ZeVgz2gBP7Df+sM0N1850jrviYSSeUuc+135dmQ==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1268,11 +1030,12 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.1.tgz", - "integrity": "sha512-h71T2QQvDgM2SmT29UYU6ozjMlAt7s7CSs5Hvy8f8cf/GM/Z4a2zMfN+fjVGaieeCrXR3EdQl6C4gQG+OgmbKw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.7.tgz", + "integrity": "sha512-ZEPJSkVZaeTFG/m2PARwLZQ+OG0vFIhPlKHK/JdIMy8DbRJ/htz6LRrTFtdzxi9EHmcwbNPAKDnadpNSIW+Aow==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1282,12 +1045,13 @@ } }, "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.24.1.tgz", - "integrity": "sha512-OMLCXi0NqvJfORTaPQBwqLXHhb93wkBKZ4aNwMl6WtehO7ar+cmp+89iPEQPqxAnxsOKTaMcs3POz3rKayJ72g==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.7.tgz", + "integrity": "sha512-mhyfEW4gufjIqYFo9krXHJ3ElbFLIze5IDp+wQTxoPd+mwFb1NxatNAwmv8Q8Iuxv7Zc+q8EkiMQwc9IhyGf4g==", + "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.24.1", - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-create-class-features-plugin": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1297,13 +1061,13 @@ } }, "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.24.1.tgz", - "integrity": "sha512-FUHlKCn6J3ERiu8Dv+4eoz7w8+kFLSyeVG4vDAikwADGjUCoHw/JHokyGtr8OR4UjpwPVivyF+h8Q5iv/JmrtA==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.25.8.tgz", + "integrity": "sha512-e82gl3TCorath6YLf9xUwFehVvjvfqFhdOo4+0iVIVju+6XOi5XHkqB3P2AXnSwoeTX0HBoXq5gJFtvotJzFnQ==", + "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.24.1", - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/plugin-syntax-class-static-block": "^7.14.5" + "@babel/helper-create-class-features-plugin": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1313,17 +1077,16 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.24.1.tgz", - "integrity": "sha512-ZTIe3W7UejJd3/3R4p7ScyyOoafetUShSf4kCqV0O7F/RiHxVj/wRaRnQlrGwflvcehNA8M42HkAiEDYZu2F1Q==", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-replace-supers": "^7.24.1", - "@babel/helper-split-export-declaration": "^7.22.6", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.7.tgz", + "integrity": "sha512-9j9rnl+YCQY0IGoeipXvnk3niWicIB6kCsWRGLwX241qSXpbA4MKxtp/EdvFxsc4zI5vqfLxzOd0twIJ7I99zg==", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.25.7", + "@babel/helper-compilation-targets": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-replace-supers": "^7.25.7", + "@babel/traverse": "^7.25.7", "globals": "^11.1.0" }, "engines": { @@ -1334,12 +1097,13 @@ } }, "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.1.tgz", - "integrity": "sha512-5pJGVIUfJpOS+pAqBQd+QMaTD2vCL/HcePooON6pDpHgRp4gNRmzyHTPIkXntwKsq3ayUFVfJaIKPw2pOkOcTw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.7.tgz", + "integrity": "sha512-QIv+imtM+EtNxg/XBKL3hiWjgdLjMOmZ+XzQwSgmBfKbfxUjBzGgVPklUuE55eq5/uVoh8gg3dqlrwR/jw3ZeA==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/template": "^7.24.0" + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/template": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1349,11 +1113,12 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.1.tgz", - "integrity": "sha512-ow8jciWqNxR3RYbSNVuF4U2Jx130nwnBnhRw6N6h1bOejNkABmcI5X5oz29K4alWX7vf1C+o6gtKXikzRKkVdw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.7.tgz", + "integrity": "sha512-xKcfLTlJYUczdaM1+epcdh1UGewJqr9zATgrNHcLBcV2QmfvPPEixo/sK/syql9cEmbr7ulu5HMFG5vbbt/sEA==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1363,12 +1128,13 @@ } }, "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.24.1.tgz", - "integrity": "sha512-p7uUxgSoZwZ2lPNMzUkqCts3xlp8n+o05ikjy7gbtFJSt9gdU88jAmtfmOxHM14noQXBxfgzf2yRWECiNVhTCw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.7.tgz", + "integrity": "sha512-kXzXMMRzAtJdDEgQBLF4oaiT6ZCU3oWHgpARnTKDAqPkDJ+bs3NrZb310YYevR5QlRo3Kn7dzzIdHbZm1VzJdQ==", + "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-create-regexp-features-plugin": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1378,11 +1144,12 @@ } }, "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.24.1.tgz", - "integrity": "sha512-msyzuUnvsjsaSaocV6L7ErfNsa5nDWL1XKNnDePLgmz+WdU4w/J8+AxBMrWfi9m4IxfL5sZQKUPQKDQeeAT6lA==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.7.tgz", + "integrity": "sha512-by+v2CjoL3aMnWDOyCIg+yxU9KXSRa9tN6MbqggH5xvymmr9p4AMjYkNlQy4brMceBnUyHZ9G8RnpvT8wP7Cfg==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1391,13 +1158,29 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.7.tgz", + "integrity": "sha512-HvS6JF66xSS5rNKXLqkk7L9c/jZ/cdIVIcoPVrnl8IsVpLggTjXs8OWekbLHs/VtYDDh5WXnQyeE3PPUGm22MA==", + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.24.1.tgz", - "integrity": "sha512-av2gdSTyXcJVdI+8aFZsCAtR29xJt0S5tas+Ef8NvBNmD1a+N/3ecMLeMBgfcK+xzsjdLDT6oHt+DFPyeqUbDA==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.8.tgz", + "integrity": "sha512-gznWY+mr4ZQL/EWPcbBQUP3BXS5FwZp8RUOw06BaRn8tQLzN4XLIxXejpHN9Qo8x8jjBmAAKp6FoS51AgkSA/A==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1407,12 +1190,13 @@ } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.24.1.tgz", - "integrity": "sha512-U1yX13dVBSwS23DEAqU+Z/PkwE9/m7QQy8Y9/+Tdb8UWYaGNDYwTLi19wqIAiROr8sXVum9A/rtiH5H0boUcTw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.25.7.tgz", + "integrity": "sha512-yjqtpstPfZ0h/y40fAXRv2snciYr0OAoMXY/0ClC7tm4C/nG5NJKmIItlaYlLbIVAWNfrYuy9dq1bE0SbX0PEg==", + "license": "MIT", "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.15", - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1422,12 +1206,12 @@ } }, "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.24.1.tgz", - "integrity": "sha512-Ft38m/KFOyzKw2UaJFkWG9QnHPG/Q/2SkOrRk4pNBPg5IPZ+dOxcmkK5IyuBcxiNPyyYowPGUReyBvrvZs7IlQ==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.8.tgz", + "integrity": "sha512-sPtYrduWINTQTW7FtOy99VCTWp4H23UX7vYcut7S4CIMEXU+54zKX9uCoGkLsWXteyaMXzVHgzWbLfQ1w4GZgw==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1437,13 +1221,14 @@ } }, "node_modules/@babel/plugin-transform-flow-strip-types": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.24.1.tgz", - "integrity": "sha512-iIYPIWt3dUmUKKE10s3W+jsQ3icFkw0JyRVyY1B7G4yK/nngAOHLVx8xlhA6b/Jzl/Y0nis8gjqhqKtRDQqHWQ==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.25.7.tgz", + "integrity": "sha512-q8Td2PPc6/6I73g96SreSUCKEcwMXCwcXSIAVTyTTN6CpJe0dMj8coxu1fg1T9vfBLi6Rsi6a4ECcFBbKabS5w==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/plugin-syntax-flow": "^7.24.1" + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/plugin-syntax-flow": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1453,12 +1238,13 @@ } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.24.1.tgz", - "integrity": "sha512-OxBdcnF04bpdQdR3i4giHZNZQn7cm8RQKcSwA17wAAqEELo1ZOwp5FFgeptWUQXFyT9kwHo10aqqauYkRZPCAg==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.25.7.tgz", + "integrity": "sha512-n/TaiBGJxYFWvpJDfsxSj9lEEE44BFM1EPGz4KEiTipTgkoFVVcCmzAL3qA7fdQU96dpo4gGf5HBx/KnDvqiHw==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1468,13 +1254,14 @@ } }, "node_modules/@babel/plugin-transform-function-name": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.24.1.tgz", - "integrity": "sha512-BXmDZpPlh7jwicKArQASrj8n22/w6iymRnvHYYd2zO30DbE277JO20/7yXJT3QxDPtiQiOxQBbZH4TpivNXIxA==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.7.tgz", + "integrity": "sha512-5MCTNcjCMxQ63Tdu9rxyN6cAWurqfrDZ76qvVPrGYdBxIj+EawuuxTu/+dgJlhK5eRz3v1gLwp6XwS8XaX2NiQ==", + "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-compilation-targets": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/traverse": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1484,12 +1271,12 @@ } }, "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.24.1.tgz", - "integrity": "sha512-U7RMFmRvoasscrIFy5xA4gIp8iWnWubnKkKuUGJjsuOH7GfbMkB+XZzeslx2kLdEGdOJDamEmCqOks6e8nv8DQ==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.8.tgz", + "integrity": "sha512-4OMNv7eHTmJ2YXs3tvxAfa/I43di+VcF+M4Wt66c88EAED1RoGaf1D64cL5FkRpNL+Vx9Hds84lksWvd/wMIdA==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/plugin-syntax-json-strings": "^7.8.3" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1499,11 +1286,12 @@ } }, "node_modules/@babel/plugin-transform-literals": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.24.1.tgz", - "integrity": "sha512-zn9pwz8U7nCqOYIiBaOxoQOtYmMODXTJnkxG4AtX8fPmnCRYWBOHD0qcpwS9e2VDSp1zNJYpdnFMIKb8jmwu6g==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.7.tgz", + "integrity": "sha512-fwzkLrSu2fESR/cm4t6vqd7ebNIopz2QHGtjoU+dswQo/P6lwAG04Q98lliE3jkz/XqnbGFLnUcE0q0CVUf92w==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1513,12 +1301,12 @@ } }, "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.24.1.tgz", - "integrity": "sha512-OhN6J4Bpz+hIBqItTeWJujDOfNP+unqv/NJgyhlpSqgBTPm37KkMmZV6SYcOj+pnDbdcl1qRGV/ZiIjX9Iy34w==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.8.tgz", + "integrity": "sha512-f5W0AhSbbI+yY6VakT04jmxdxz+WsID0neG7+kQZbCOjuyJNdL5Nn4WIBm4hRpKnUcO9lP0eipUhFN12JpoH8g==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1528,11 +1316,12 @@ } }, "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.24.1.tgz", - "integrity": "sha512-4ojai0KysTWXzHseJKa1XPNXKRbuUrhkOPY4rEGeR+7ChlJVKxFa3H3Bz+7tWaGKgJAXUWKOGmltN+u9B3+CVg==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.7.tgz", + "integrity": "sha512-Std3kXwpXfRV0QtQy5JJcRpkqP8/wG4XL7hSKZmGlxPlDqmpXtEPRmhF7ztnlTCtUN3eXRUJp+sBEZjaIBVYaw==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1542,12 +1331,13 @@ } }, "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.24.1.tgz", - "integrity": "sha512-lAxNHi4HVtjnHd5Rxg3D5t99Xm6H7b04hUS7EHIXcUl2EV4yl1gWdqZrNzXnSrHveL9qMdbODlLF55mvgjAfaQ==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.7.tgz", + "integrity": "sha512-CgselSGCGzjQvKzghCvDTxKHP3iooenLpJDO842ehn5D2G5fJB222ptnDwQho0WjEvg7zyoxb9P+wiYxiJX5yA==", + "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-module-transforms": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1557,13 +1347,14 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.1.tgz", - "integrity": "sha512-szog8fFTUxBfw0b98gEWPaEqF42ZUD/T3bkynW/wtgx2p/XCP55WEsb+VosKceRSd6njipdZvNogqdtI4Q0chw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.25.7.tgz", + "integrity": "sha512-L9Gcahi0kKFYXvweO6n0wc3ZG1ChpSFdgG+eV1WYZ3/dGbJK7vvk91FgGgak8YwRgrCuihF8tE/Xg07EkL5COg==", + "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-simple-access": "^7.22.5" + "@babel/helper-module-transforms": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-simple-access": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1573,14 +1364,15 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.24.1.tgz", - "integrity": "sha512-mqQ3Zh9vFO1Tpmlt8QPnbwGHzNz3lpNEMxQb1kAemn/erstyqw1r9KeOlOfo3y6xAnFEcOv2tSyrXfmMk+/YZA==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.7.tgz", + "integrity": "sha512-t9jZIvBmOXJsiuyOwhrIGs8dVcD6jDyg2icw1VL4A/g+FnWyJKwUfSSU2nwJuMV2Zqui856El9u+ElB+j9fV1g==", + "license": "MIT", "dependencies": { - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-validator-identifier": "^7.22.20" + "@babel/helper-module-transforms": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-validator-identifier": "^7.25.7", + "@babel/traverse": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1590,12 +1382,13 @@ } }, "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.24.1.tgz", - "integrity": "sha512-tuA3lpPj+5ITfcCluy6nWonSL7RvaG0AOTeAuvXqEKS34lnLzXpDb0dcP6K8jD0zWZFNDVly90AGFJPnm4fOYg==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.7.tgz", + "integrity": "sha512-p88Jg6QqsaPh+EB7I9GJrIqi1Zt4ZBHUQtjw3z1bzEXcLh6GfPqzZJ6G+G1HBGKUNukT58MnKG7EN7zXQBCODw==", + "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-module-transforms": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1605,12 +1398,13 @@ } }, "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz", - "integrity": "sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.7.tgz", + "integrity": "sha512-BtAT9LzCISKG3Dsdw5uso4oV1+v2NlVXIIomKJgQybotJY3OwCwJmkongjHgwGKoZXd0qG5UZ12JUlDQ07W6Ow==", + "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-create-regexp-features-plugin": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1620,11 +1414,12 @@ } }, "node_modules/@babel/plugin-transform-new-target": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.24.1.tgz", - "integrity": "sha512-/rurytBM34hYy0HKZQyA0nHbQgQNFm4Q/BOc9Hflxi2X3twRof7NaE5W46j4kQitm7SvACVRXsa6N/tSZxvPug==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.7.tgz", + "integrity": "sha512-CfCS2jDsbcZaVYxRFo2qtavW8SpdzmBXC2LOI4oO0rP+JSRDxxF3inF4GcPsLgfb5FjkhXG5/yR/lxuRs2pySA==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1634,12 +1429,12 @@ } }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.24.1.tgz", - "integrity": "sha512-iQ+caew8wRrhCikO5DrUYx0mrmdhkaELgFa+7baMcVuhxIkN7oxt06CZ51D65ugIb1UWRQ8oQe+HXAVM6qHFjw==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.25.8.tgz", + "integrity": "sha512-Z7WJJWdQc8yCWgAmjI3hyC+5PXIubH9yRKzkl9ZEG647O9szl9zvmKLzpbItlijBnVhTUf1cpyWBsZ3+2wjWPQ==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1649,12 +1444,12 @@ } }, "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.24.1.tgz", - "integrity": "sha512-7GAsGlK4cNL2OExJH1DzmDeKnRv/LXq0eLUSvudrehVA5Rgg4bIrqEUW29FbKMBRT0ztSqisv7kjP+XIC4ZMNw==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.8.tgz", + "integrity": "sha512-rm9a5iEFPS4iMIy+/A/PiS0QN0UyjPIeVvbU5EMZFKJZHt8vQnasbpo3T3EFcxzCeYO0BHfc4RqooCZc51J86Q==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1664,14 +1459,14 @@ } }, "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.24.1.tgz", - "integrity": "sha512-XjD5f0YqOtebto4HGISLNfiNMTTs6tbkFf2TOqJlYKYmbo+mN9Dnpl4SRoofiziuOWMIyq3sZEUqLo3hLITFEA==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.8.tgz", + "integrity": "sha512-LkUu0O2hnUKHKE7/zYOIjByMa4VRaV2CD/cdGz0AxU9we+VA3kDDggKEzI0Oz1IroG+6gUP6UmWEHBMWZU316g==", + "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.24.1" + "@babel/helper-compilation-targets": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/plugin-transform-parameters": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1681,12 +1476,13 @@ } }, "node_modules/@babel/plugin-transform-object-super": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.24.1.tgz", - "integrity": "sha512-oKJqR3TeI5hSLRxudMjFQ9re9fBVUU0GICqM3J1mi8MqlhVr6hC/ZN4ttAyMuQR6EZZIY6h/exe5swqGNNIkWQ==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.7.tgz", + "integrity": "sha512-pWT6UXCEW3u1t2tcAGtE15ornCBvopHj9Bps9D2DsH15APgNVOTwwczGckX+WkAvBmuoYKRCFa4DK+jM8vh5AA==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-replace-supers": "^7.24.1" + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-replace-supers": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1696,12 +1492,12 @@ } }, "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.24.1.tgz", - "integrity": "sha512-oBTH7oURV4Y+3EUrf6cWn1OHio3qG/PVwO5J03iSJmBg6m2EhKjkAu/xuaXaYwWW9miYtvbWv4LNf0AmR43LUA==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.8.tgz", + "integrity": "sha512-EbQYweoMAHOn7iJ9GgZo14ghhb9tTjgOc88xFgYngifx7Z9u580cENCV159M4xDh3q/irbhSjZVpuhpC2gKBbg==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1711,13 +1507,13 @@ } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.1.tgz", - "integrity": "sha512-n03wmDt+987qXwAgcBlnUUivrZBPZ8z1plL0YvgQalLm+ZE5BMhGm94jhxXtA1wzv1Cu2aaOv1BM9vbVttrzSg==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.8.tgz", + "integrity": "sha512-q05Bk7gXOxpTHoQ8RSzGSh/LHVB9JEIkKnk3myAWwZHnYiTGYtbdrYkIsS8Xyh4ltKf7GNUSgzs/6P2bJtBAQg==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1727,11 +1523,12 @@ } }, "node_modules/@babel/plugin-transform-parameters": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.24.1.tgz", - "integrity": "sha512-8Jl6V24g+Uw5OGPeWNKrKqXPDw2YDjLc53ojwfMcKwlEoETKU9rU0mHUtcg9JntWI/QYzGAXNWEcVHZ+fR+XXg==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.7.tgz", + "integrity": "sha512-FYiTvku63me9+1Nz7TOx4YMtW3tWXzfANZtrzHhUZrz4d47EEtMQhzFoZWESfXuAMMT5mwzD4+y1N8ONAX6lMQ==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1741,12 +1538,13 @@ } }, "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.24.1.tgz", - "integrity": "sha512-tGvisebwBO5em4PaYNqt4fkw56K2VALsAbAakY0FjTYqJp7gfdrgr7YX76Or8/cpik0W6+tj3rZ0uHU9Oil4tw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.7.tgz", + "integrity": "sha512-KY0hh2FluNxMLwOCHbxVOKfdB5sjWG4M183885FmaqWWiGMhRZq4DQRKH6mHdEucbJnyDyYiZNwNG424RymJjA==", + "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.24.1", - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-create-class-features-plugin": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1756,14 +1554,14 @@ } }, "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.24.1.tgz", - "integrity": "sha512-pTHxDVa0BpUbvAgX3Gat+7cSciXqUcY9j2VZKTbSB6+VQGpNgNO9ailxTGHSXlqOnX1Hcx1Enme2+yv7VqP9bg==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.8.tgz", + "integrity": "sha512-8Uh966svuB4V8RHHg0QJOB32QK287NBksJOByoKmHMp1TAobNniNalIkI2i5IPj5+S9NYCG4VIjbEuiSN8r+ow==", + "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.24.1", - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + "@babel/helper-annotate-as-pure": "^7.25.7", + "@babel/helper-create-class-features-plugin": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1773,11 +1571,12 @@ } }, "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.24.1.tgz", - "integrity": "sha512-LetvD7CrHmEx0G442gOomRr66d7q8HzzGGr4PMHGr+5YIm6++Yke+jxj246rpvsbyhJwCLxcTn6zW1P1BSenqA==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.7.tgz", + "integrity": "sha512-lQEeetGKfFi0wHbt8ClQrUSUMfEeI3MMm74Z73T9/kuz990yYVtfofjf3NuA42Jy3auFOpbjDyCSiIkTs1VIYw==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1787,11 +1586,12 @@ } }, "node_modules/@babel/plugin-transform-react-constant-elements": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.24.1.tgz", - "integrity": "sha512-QXp1U9x0R7tkiGB0FOk8o74jhnap0FlZ5gNkRIWdG3eP+SvMFg118e1zaWewDzgABb106QSKpVsD3Wgd8t6ifA==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.25.7.tgz", + "integrity": "sha512-/qXt69Em8HgsjCLu7G3zdIQn7A2QwmYND7Wa0LTp09Na+Zn8L5d0A7wSXrKi18TJRc/Q5S1i1De/SU1LzVkSvA==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1801,11 +1601,12 @@ } }, "node_modules/@babel/plugin-transform-react-display-name": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.24.1.tgz", - "integrity": "sha512-mvoQg2f9p2qlpDQRBC7M3c3XTr0k7cp/0+kFKKO/7Gtu0LSw16eKB+Fabe2bDT/UpsyasTBBkAnbdsLrkD5XMw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.25.7.tgz", + "integrity": "sha512-r0QY7NVU8OnrwE+w2IWiRom0wwsTbjx4+xH2RTd7AVdof3uurXOF+/mXHQDRk+2jIvWgSaCHKMgggfvM4dyUGA==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1815,15 +1616,16 @@ } }, "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.23.4.tgz", - "integrity": "sha512-5xOpoPguCZCRbo/JeHlloSkTA8Bld1J/E1/kLfD1nsuiW1m8tduTA1ERCgIZokDflX/IBzKcqR3l7VlRgiIfHA==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.7.tgz", + "integrity": "sha512-vILAg5nwGlR9EXE8JIOX4NHXd49lrYbN8hnjffDtoULwpL9hUx/N55nqh2qd0q6FyNDfjl9V79ecKGvFbcSA0Q==", + "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-jsx": "^7.23.3", - "@babel/types": "^7.23.4" + "@babel/helper-annotate-as-pure": "^7.25.7", + "@babel/helper-module-imports": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/plugin-syntax-jsx": "^7.25.7", + "@babel/types": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1833,11 +1635,12 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-development": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.22.5.tgz", - "integrity": "sha512-bDhuzwWMuInwCYeDeMzyi7TaBgRQei6DqxhbyniL7/VG4RSS7HtSL2QbY4eESy1KJqlWt8g3xeEBGPuo+XqC8A==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.25.7.tgz", + "integrity": "sha512-5yd3lH1PWxzW6IZj+p+Y4OLQzz0/LzlOG8vGqonHfVR3euf1vyzyMUJk9Ac+m97BH46mFc/98t9PmYLyvgL3qg==", + "license": "MIT", "dependencies": { - "@babel/plugin-transform-react-jsx": "^7.22.5" + "@babel/plugin-transform-react-jsx": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1847,11 +1650,12 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.23.3.tgz", - "integrity": "sha512-qXRvbeKDSfwnlJnanVRp0SfuWE5DQhwQr5xtLBzp56Wabyo+4CMosF6Kfp+eOD/4FYpql64XVJ2W0pVLlJZxOQ==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.25.7.tgz", + "integrity": "sha512-JD9MUnLbPL0WdVK8AWC7F7tTG2OS6u/AKKnsK+NdRhUiVdnzyR1S3kKQCaRLOiaULvUiqK6Z4JQE635VgtCFeg==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1861,11 +1665,12 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-source": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.23.3.tgz", - "integrity": "sha512-91RS0MDnAWDNvGC6Wio5XYkyWI39FMFO+JK9+4AlgaTH+yWwVTsw7/sn6LK0lH7c5F+TFkpv/3LfCJ1Ydwof/g==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.25.7.tgz", + "integrity": "sha512-S/JXG/KrbIY06iyJPKfxr0qRxnhNOdkNXYBl/rmwgDd72cQLH9tEGkDm/yJPGvcSIUoikzfjMios9i+xT/uv9w==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1875,12 +1680,13 @@ } }, "node_modules/@babel/plugin-transform-react-pure-annotations": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.24.1.tgz", - "integrity": "sha512-+pWEAaDJvSm9aFvJNpLiM2+ktl2Sn2U5DdyiWdZBxmLc6+xGt88dvFqsHiAiDS+8WqUwbDfkKz9jRxK3M0k+kA==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.25.7.tgz", + "integrity": "sha512-6YTHJ7yjjgYqGc8S+CbEXhLICODk0Tn92j+vNJo07HFk9t3bjFgAKxPLFhHwF2NjmQVSI1zBRfBWUeVBa2osfA==", + "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-annotate-as-pure": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1890,11 +1696,12 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.24.1.tgz", - "integrity": "sha512-sJwZBCzIBE4t+5Q4IGLaaun5ExVMRY0lYwos/jNecjMrVCygCdph3IKv0tkP5Fc87e/1+bebAmEAGBfnRD+cnw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.7.tgz", + "integrity": "sha512-mgDoQCRjrY3XK95UuV60tZlFCQGXEtMg8H+IsW72ldw1ih1jZhzYXbJvghmAEpg5UVhhnCeia1CkGttUvCkiMQ==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-plugin-utils": "^7.25.7", "regenerator-transform": "^0.15.2" }, "engines": { @@ -1905,11 +1712,12 @@ } }, "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.24.1.tgz", - "integrity": "sha512-JAclqStUfIwKN15HrsQADFgeZt+wexNQ0uLhuqvqAUFoqPMjEcFCYZBhq0LUdz6dZK/mD+rErhW71fbx8RYElg==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.7.tgz", + "integrity": "sha512-3OfyfRRqiGeOvIWSagcwUTVk2hXBsr/ww7bLn6TRTuXnexA+Udov2icFOxFX9abaj4l96ooYkcNN1qi2Zvqwng==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1919,11 +1727,12 @@ } }, "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.24.1.tgz", - "integrity": "sha512-LyjVB1nsJ6gTTUKRjRWx9C1s9hE7dLfP/knKdrfeH9UPtAGjYGgxIbFfx7xyLIEWs7Xe1Gnf8EWiUqfjLhInZA==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.7.tgz", + "integrity": "sha512-uBbxNwimHi5Bv3hUccmOFlUy3ATO6WagTApenHz9KzoIdn0XeACdB12ZJ4cjhuB2WSi80Ez2FWzJnarccriJeA==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1933,12 +1742,13 @@ } }, "node_modules/@babel/plugin-transform-spread": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.24.1.tgz", - "integrity": "sha512-KjmcIM+fxgY+KxPVbjelJC6hrH1CgtPmTvdXAfn3/a9CnWGSTY7nH4zm5+cjmWJybdcPSsD0++QssDsjcpe47g==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.7.tgz", + "integrity": "sha512-Mm6aeymI0PBh44xNIv/qvo8nmbkpZze1KvR8MkEqbIREDxoiWTi18Zr2jryfRMwDfVZF9foKh060fWgni44luw==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1948,11 +1758,12 @@ } }, "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.24.1.tgz", - "integrity": "sha512-9v0f1bRXgPVcPrngOQvLXeGNNVLc8UjMVfebo9ka0WF3/7+aVUHmaJVT3sa0XCzEFioPfPHZiOcYG9qOsH63cw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.7.tgz", + "integrity": "sha512-ZFAeNkpGuLnAQ/NCsXJ6xik7Id+tHuS+NT+ue/2+rn/31zcdnupCdmunOizEaP0JsUmTFSTOPoQY7PkK2pttXw==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1962,11 +1773,12 @@ } }, "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.24.1.tgz", - "integrity": "sha512-WRkhROsNzriarqECASCNu/nojeXCDTE/F2HmRgOzi7NGvyfYGq1NEjKBK3ckLfRgGc6/lPAqP0vDOSw3YtG34g==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.25.7.tgz", + "integrity": "sha512-SI274k0nUsFFmyQupiO7+wKATAmMFf8iFgq2O+vVFXZ0SV9lNfT1NGzBEhjquFmD8I9sqHLguH+gZVN3vww2AA==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1976,11 +1788,12 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.1.tgz", - "integrity": "sha512-CBfU4l/A+KruSUoW+vTQthwcAdwuqbpRNB8HQKlZABwHRhsdHZ9fezp4Sn18PeAlYxTNiLMlx4xUBV3AWfg1BA==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.25.7.tgz", + "integrity": "sha512-OmWmQtTHnO8RSUbL0NTdtpbZHeNTnm68Gj5pA4Y2blFNh+V4iZR68V1qL9cI37J21ZN7AaCnkfdHtLExQPf2uA==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -1990,14 +1803,16 @@ } }, "node_modules/@babel/plugin-transform-typescript": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.24.1.tgz", - "integrity": "sha512-liYSESjX2fZ7JyBFkYG78nfvHlMKE6IpNdTVnxmlYUR+j5ZLsitFbaAE+eJSK2zPPkNWNw4mXL51rQ8WrvdK0w==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.25.7.tgz", + "integrity": "sha512-VKlgy2vBzj8AmEzunocMun2fF06bsSWV+FvVXohtL6FGve/+L217qhHxRTVGHEDO/YR8IANcjzgJsd04J8ge5Q==", + "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.24.1", - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/plugin-syntax-typescript": "^7.24.1" + "@babel/helper-annotate-as-pure": "^7.25.7", + "@babel/helper-create-class-features-plugin": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.25.7", + "@babel/plugin-syntax-typescript": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -2007,11 +1822,12 @@ } }, "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.24.1.tgz", - "integrity": "sha512-RlkVIcWT4TLI96zM660S877E7beKlQw7Ig+wqkKBiWfj0zH5Q4h50q6er4wzZKRNSYpfo6ILJ+hrJAGSX2qcNw==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.7.tgz", + "integrity": "sha512-BN87D7KpbdiABA+t3HbVqHzKWUDN3dymLaTnPFAMyc8lV+KN3+YzNhVRNdinaCPA4AUqx7ubXbQ9shRjYBl3SQ==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -2021,12 +1837,13 @@ } }, "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.24.1.tgz", - "integrity": "sha512-Ss4VvlfYV5huWApFsF8/Sq0oXnGO+jB+rijFEFugTd3cwSObUSnUi88djgR5528Csl0uKlrI331kRqe56Ov2Ng==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.7.tgz", + "integrity": "sha512-IWfR89zcEPQGB/iB408uGtSPlQd3Jpq11Im86vUgcmSTcoWAiQMCTOa2K2yNNqFJEBVICKhayctee65Ka8OB0w==", + "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-create-regexp-features-plugin": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -2036,12 +1853,13 @@ } }, "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.24.1.tgz", - "integrity": "sha512-2A/94wgZgxfTsiLaQ2E36XAOdcZmGAaEEgVmxQWwZXWkGhvoHbaqXcKnU8zny4ycpu3vNqg0L/PcCiYtHtA13g==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.7.tgz", + "integrity": "sha512-8JKfg/hiuA3qXnlLx8qtv5HWRbgyFx2hMMtpDDuU2rTckpKkGu4ycK5yYHwuEa16/quXfoxHBIApEsNyMWnt0g==", + "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-create-regexp-features-plugin": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -2051,12 +1869,13 @@ } }, "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.24.1.tgz", - "integrity": "sha512-fqj4WuzzS+ukpgerpAoOnMfQXwUHFxXUZUE84oL2Kao2N8uSlvcpnAidKASgsNgzZHBsHWvcm8s9FPWUhAb8fA==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.7.tgz", + "integrity": "sha512-YRW8o9vzImwmh4Q3Rffd09bH5/hvY0pxg+1H1i0f7APoUeg12G7+HhLj9ZFNIrYkgBXhIijPJ+IXypN0hLTIbw==", + "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.24.0" + "@babel/helper-create-regexp-features-plugin": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -2066,89 +1885,78 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.24.3", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.3.tgz", - "integrity": "sha512-fSk430k5c2ff8536JcPvPWK4tZDwehWLGlBp0wrsBUjZVdeQV6lePbwKWZaZfK2vnh/1kQX1PzAJWsnBmVgGJA==", - "dependencies": { - "@babel/compat-data": "^7.24.1", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-validator-option": "^7.23.5", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.24.1", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.1", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.24.1", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.25.8.tgz", + "integrity": "sha512-58T2yulDHMN8YMUxiLq5YmWUnlDCyY1FsHM+v12VMx+1/FlrUj5tY50iDCpofFQEM8fMYOaY9YRvym2jcjn1Dg==", + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.25.8", + "@babel/helper-compilation-targets": "^7.25.7", + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-validator-option": "^7.25.7", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.7", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.7", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.7", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.25.7", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.7", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.24.1", - "@babel/plugin-syntax-import-attributes": "^7.24.1", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-syntax-import-assertions": "^7.25.7", + "@babel/plugin-syntax-import-attributes": "^7.25.7", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.24.1", - "@babel/plugin-transform-async-generator-functions": "^7.24.3", - "@babel/plugin-transform-async-to-generator": "^7.24.1", - "@babel/plugin-transform-block-scoped-functions": "^7.24.1", - "@babel/plugin-transform-block-scoping": "^7.24.1", - "@babel/plugin-transform-class-properties": "^7.24.1", - "@babel/plugin-transform-class-static-block": "^7.24.1", - "@babel/plugin-transform-classes": "^7.24.1", - "@babel/plugin-transform-computed-properties": "^7.24.1", - "@babel/plugin-transform-destructuring": "^7.24.1", - "@babel/plugin-transform-dotall-regex": "^7.24.1", - "@babel/plugin-transform-duplicate-keys": "^7.24.1", - "@babel/plugin-transform-dynamic-import": "^7.24.1", - "@babel/plugin-transform-exponentiation-operator": "^7.24.1", - "@babel/plugin-transform-export-namespace-from": "^7.24.1", - "@babel/plugin-transform-for-of": "^7.24.1", - "@babel/plugin-transform-function-name": "^7.24.1", - "@babel/plugin-transform-json-strings": "^7.24.1", - "@babel/plugin-transform-literals": "^7.24.1", - "@babel/plugin-transform-logical-assignment-operators": "^7.24.1", - "@babel/plugin-transform-member-expression-literals": "^7.24.1", - "@babel/plugin-transform-modules-amd": "^7.24.1", - "@babel/plugin-transform-modules-commonjs": "^7.24.1", - "@babel/plugin-transform-modules-systemjs": "^7.24.1", - "@babel/plugin-transform-modules-umd": "^7.24.1", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", - "@babel/plugin-transform-new-target": "^7.24.1", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.1", - "@babel/plugin-transform-numeric-separator": "^7.24.1", - "@babel/plugin-transform-object-rest-spread": "^7.24.1", - "@babel/plugin-transform-object-super": "^7.24.1", - "@babel/plugin-transform-optional-catch-binding": "^7.24.1", - "@babel/plugin-transform-optional-chaining": "^7.24.1", - "@babel/plugin-transform-parameters": "^7.24.1", - "@babel/plugin-transform-private-methods": "^7.24.1", - "@babel/plugin-transform-private-property-in-object": "^7.24.1", - "@babel/plugin-transform-property-literals": "^7.24.1", - "@babel/plugin-transform-regenerator": "^7.24.1", - "@babel/plugin-transform-reserved-words": "^7.24.1", - "@babel/plugin-transform-shorthand-properties": "^7.24.1", - "@babel/plugin-transform-spread": "^7.24.1", - "@babel/plugin-transform-sticky-regex": "^7.24.1", - "@babel/plugin-transform-template-literals": "^7.24.1", - "@babel/plugin-transform-typeof-symbol": "^7.24.1", - "@babel/plugin-transform-unicode-escapes": "^7.24.1", - "@babel/plugin-transform-unicode-property-regex": "^7.24.1", - "@babel/plugin-transform-unicode-regex": "^7.24.1", - "@babel/plugin-transform-unicode-sets-regex": "^7.24.1", + "@babel/plugin-transform-arrow-functions": "^7.25.7", + "@babel/plugin-transform-async-generator-functions": "^7.25.8", + "@babel/plugin-transform-async-to-generator": "^7.25.7", + "@babel/plugin-transform-block-scoped-functions": "^7.25.7", + "@babel/plugin-transform-block-scoping": "^7.25.7", + "@babel/plugin-transform-class-properties": "^7.25.7", + "@babel/plugin-transform-class-static-block": "^7.25.8", + "@babel/plugin-transform-classes": "^7.25.7", + "@babel/plugin-transform-computed-properties": "^7.25.7", + "@babel/plugin-transform-destructuring": "^7.25.7", + "@babel/plugin-transform-dotall-regex": "^7.25.7", + "@babel/plugin-transform-duplicate-keys": "^7.25.7", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.7", + "@babel/plugin-transform-dynamic-import": "^7.25.8", + "@babel/plugin-transform-exponentiation-operator": "^7.25.7", + "@babel/plugin-transform-export-namespace-from": "^7.25.8", + "@babel/plugin-transform-for-of": "^7.25.7", + "@babel/plugin-transform-function-name": "^7.25.7", + "@babel/plugin-transform-json-strings": "^7.25.8", + "@babel/plugin-transform-literals": "^7.25.7", + "@babel/plugin-transform-logical-assignment-operators": "^7.25.8", + "@babel/plugin-transform-member-expression-literals": "^7.25.7", + "@babel/plugin-transform-modules-amd": "^7.25.7", + "@babel/plugin-transform-modules-commonjs": "^7.25.7", + "@babel/plugin-transform-modules-systemjs": "^7.25.7", + "@babel/plugin-transform-modules-umd": "^7.25.7", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.7", + "@babel/plugin-transform-new-target": "^7.25.7", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.25.8", + "@babel/plugin-transform-numeric-separator": "^7.25.8", + "@babel/plugin-transform-object-rest-spread": "^7.25.8", + "@babel/plugin-transform-object-super": "^7.25.7", + "@babel/plugin-transform-optional-catch-binding": "^7.25.8", + "@babel/plugin-transform-optional-chaining": "^7.25.8", + "@babel/plugin-transform-parameters": "^7.25.7", + "@babel/plugin-transform-private-methods": "^7.25.7", + "@babel/plugin-transform-private-property-in-object": "^7.25.8", + "@babel/plugin-transform-property-literals": "^7.25.7", + "@babel/plugin-transform-regenerator": "^7.25.7", + "@babel/plugin-transform-reserved-words": "^7.25.7", + "@babel/plugin-transform-shorthand-properties": "^7.25.7", + "@babel/plugin-transform-spread": "^7.25.7", + "@babel/plugin-transform-sticky-regex": "^7.25.7", + "@babel/plugin-transform-template-literals": "^7.25.7", + "@babel/plugin-transform-typeof-symbol": "^7.25.7", + "@babel/plugin-transform-unicode-escapes": "^7.25.7", + "@babel/plugin-transform-unicode-property-regex": "^7.25.7", + "@babel/plugin-transform-unicode-regex": "^7.25.7", + "@babel/plugin-transform-unicode-sets-regex": "^7.25.7", "@babel/preset-modules": "0.1.6-no-external-plugins", "babel-plugin-polyfill-corejs2": "^0.4.10", - "babel-plugin-polyfill-corejs3": "^0.10.4", + "babel-plugin-polyfill-corejs3": "^0.10.6", "babel-plugin-polyfill-regenerator": "^0.6.1", - "core-js-compat": "^3.31.0", + "core-js-compat": "^3.38.1", "semver": "^6.3.1" }, "engines": { @@ -2162,6 +1970,7 @@ "version": "0.1.6-no-external-plugins", "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@babel/types": "^7.4.4", @@ -2172,16 +1981,17 @@ } }, "node_modules/@babel/preset-react": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.24.1.tgz", - "integrity": "sha512-eFa8up2/8cZXLIpkafhaADTXSnl7IsUFCYenRWrARBz0/qZwcT0RBXpys0LJU4+WfPoF2ZG6ew6s2V6izMCwRA==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.25.7.tgz", + "integrity": "sha512-GjV0/mUEEXpi1U5ZgDprMRRgajGMRW3G5FjMr5KLKD8nT2fTG8+h/klV3+6Dm5739QE+K5+2e91qFKAYI3pmRg==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-validator-option": "^7.23.5", - "@babel/plugin-transform-react-display-name": "^7.24.1", - "@babel/plugin-transform-react-jsx": "^7.23.4", - "@babel/plugin-transform-react-jsx-development": "^7.22.5", - "@babel/plugin-transform-react-pure-annotations": "^7.24.1" + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-validator-option": "^7.25.7", + "@babel/plugin-transform-react-display-name": "^7.25.7", + "@babel/plugin-transform-react-jsx": "^7.25.7", + "@babel/plugin-transform-react-jsx-development": "^7.25.7", + "@babel/plugin-transform-react-pure-annotations": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -2191,15 +2001,16 @@ } }, "node_modules/@babel/preset-typescript": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.24.1.tgz", - "integrity": "sha512-1DBaMmRDpuYQBPWD8Pf/WEwCrtgRHxsZnP4mIy9G/X+hFfbI47Q2G4t1Paakld84+qsk2fSsUPMKg71jkoOOaQ==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.25.7.tgz", + "integrity": "sha512-rkkpaXJZOFN45Fb+Gki0c+KMIglk4+zZXOoMJuyEK8y8Kkc8Jd3BDmP7qPsz0zQMJj+UD7EprF+AqAXcILnexw==", + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-validator-option": "^7.23.5", - "@babel/plugin-syntax-jsx": "^7.24.1", - "@babel/plugin-transform-modules-commonjs": "^7.24.1", - "@babel/plugin-transform-typescript": "^7.24.1" + "@babel/helper-plugin-utils": "^7.25.7", + "@babel/helper-validator-option": "^7.25.7", + "@babel/plugin-syntax-jsx": "^7.25.7", + "@babel/plugin-transform-modules-commonjs": "^7.25.7", + "@babel/plugin-transform-typescript": "^7.25.7" }, "engines": { "node": ">=6.9.0" @@ -2208,15 +2019,11 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/regjsgen": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" - }, "node_modules/@babel/runtime": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.1.tgz", - "integrity": "sha512-+BIznRzyqBf+2wCTxcKE3wDjfGeCoVE61KSHGpkzqrLi8qxqFwBeUFyId2cxkTmm55fzDGnm0+yCxaxygrLUnQ==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.7.tgz", + "integrity": "sha512-FjoyLe754PMiYsFaN5C94ttGiOmBNYTf6pLr4xXHAT5uctHb092PBszndLDR5XA/jghQvn4n7JMHl7dmTgbm9w==", + "license": "MIT", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -2225,31 +2032,30 @@ } }, "node_modules/@babel/template": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz", - "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.7.tgz", + "integrity": "sha512-wRwtAgI3bAS+JGU2upWNL9lSlDcRCqD05BZ1n3X2ONLH1WilFP6O1otQjeMK/1g0pvYcXC7b/qVUB1keofjtZA==", + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.23.5", - "@babel/parser": "^7.24.0", - "@babel/types": "^7.24.0" + "@babel/code-frame": "^7.25.7", + "@babel/parser": "^7.25.7", + "@babel/types": "^7.25.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.9.tgz", - "integrity": "sha512-I/4UJ9vs90OkBtY6iiiTORVMyIhJ4kAVmsKo9KFc8UOxMeUfi2hvtIBsET5u9GizXE6/GFSuKCTNfgCswuEjRg==", - "dependencies": { - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.6", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.9", - "@babel/types": "^7.23.9", + "version": "7.25.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.7.tgz", + "integrity": "sha512-jatJPT1Zjqvh/1FyJs6qAHL+Dzb7sTb+xr7Q+gM1b+1oBsMsQQ4FkVKb6dFlJvLlVssqkRzV05Jzervt9yhnzg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.25.7", + "@babel/generator": "^7.25.7", + "@babel/parser": "^7.25.7", + "@babel/template": "^7.25.7", + "@babel/types": "^7.25.7", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -2258,12 +2064,13 @@ } }, "node_modules/@babel/types": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", - "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", + "version": "7.25.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.8.tgz", + "integrity": "sha512-JWtuCu8VQsMladxVz/P4HzHUGCAwpuqacmowgXFs5XjxIgKuNjnLokQzuVjlTvIzODaDmpjT3oxcC48vyk9EWg==", + "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.23.4", - "@babel/helper-validator-identifier": "^7.22.20", + "@babel/helper-string-parser": "^7.25.7", + "@babel/helper-validator-identifier": "^7.25.7", "to-fast-properties": "^2.0.0" }, "engines": { @@ -2274,108 +2081,253 @@ "version": "0.2.3", "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true + "dev": true, + "license": "MIT" }, - "node_modules/@codemirror/autocomplete": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.2.0.tgz", - "integrity": "sha512-yNCm2CEE4kE4L2Sf7WeyCej1Q3951ccaCWfomrlBkoERKCss+TzuEeqGe5VnAJTEybLy1yzf1BdMUY/988bfpg==", - "dependencies": { - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", - "@lezer/common": "^1.0.0" + "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" }, - "peerDependencies": { - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", - "@lezer/common": "^1.0.0" - } - }, - "node_modules/@codemirror/commands": { - "version": "6.3.3", - "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.3.3.tgz", - "integrity": "sha512-dO4hcF0fGT9tu1Pj1D2PvGvxjeGkbC6RGcZw6Qs74TH+Ed1gw98jmUgd2axWvIZEqTeTuFrg1lEB1KV6cK9h1A==", - "dependencies": { - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.4.0", - "@codemirror/view": "^6.0.0", - "@lezer/common": "^1.1.0" + "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/@codemirror/lang-css": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-6.2.1.tgz", - "integrity": "sha512-/UNWDNV5Viwi/1lpr/dIXJNWiwDxpw13I4pTUAsNxZdg6E0mI2kTQb0P2iHczg1Tu+H4EBgJR+hYhKiHKko7qg==", - "dependencies": { - "@codemirror/autocomplete": "^6.0.0", - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@lezer/common": "^1.0.2", - "@lezer/css": "^1.0.0" + "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/@codemirror/lang-html": { - "version": "6.4.8", - "resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.8.tgz", - "integrity": "sha512-tE2YK7wDlb9ZpAH6mpTPiYm6rhfdQKVDa5r9IwIFlwwgvVaKsCfuKKZoJGWsmMZIf3FQAuJ5CHMPLymOtg1hXw==", - "dependencies": { - "@codemirror/autocomplete": "^6.0.0", - "@codemirror/lang-css": "^6.0.0", - "@codemirror/lang-javascript": "^6.0.0", - "@codemirror/language": "^6.4.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.17.0", - "@lezer/common": "^1.0.0", - "@lezer/css": "^1.1.0", - "@lezer/html": "^1.3.0" + "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/@codemirror/lang-html/node_modules/@codemirror/language": { - "version": "6.10.1", - "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.1.tgz", - "integrity": "sha512-5GrXzrhq6k+gL5fjkAwt90nYDmjlzTIJV8THnxNFtNKWotMIlzzN+CpqxqwXOECnUdOndmSeWntVrVcv5axWRQ==", - "dependencies": { - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.23.0", - "@lezer/common": "^1.1.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0", - "style-mod": "^4.0.0" + "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/@codemirror/lang-javascript": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.1.tgz", - "integrity": "sha512-jlFOXTejVyiQCW3EQwvKH0m99bUYIw40oPmFjSX2VS78yzfe0HELZ+NEo9Yfo1MkGRpGlj3Gnu4rdxV1EnAs5A==", - "dependencies": { - "@codemirror/autocomplete": "^6.0.0", - "@codemirror/language": "^6.6.0", - "@codemirror/lint": "^6.0.0", + "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", + "integrity": "sha512-yNCm2CEE4kE4L2Sf7WeyCej1Q3951ccaCWfomrlBkoERKCss+TzuEeqGe5VnAJTEybLy1yzf1BdMUY/988bfpg==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "@lezer/common": "^1.0.0" + }, + "peerDependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@codemirror/view": "^6.0.0", + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@codemirror/commands": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.7.0.tgz", + "integrity": "sha512-+cduIZ2KbesDhbykV02K25A5xIVrquSPz4UxxYBemRlAT2aW8dhwUgLDwej7q/RJUHKk4nALYcR1puecDvbdqw==", + "license": "MIT", + "dependencies": { + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.4.0", + "@codemirror/view": "^6.27.0", + "@lezer/common": "^1.1.0" + } + }, + "node_modules/@codemirror/lang-css": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-6.3.0.tgz", + "integrity": "sha512-CyR4rUNG9OYcXDZwMPvJdtb6PHbBDKUc/6Na2BIwZ6dKab1JQqKa4di+RNRY9Myn7JB81vayKwJeQ7jEdmNVDA==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.0.0", + "@codemirror/state": "^6.0.0", + "@lezer/common": "^1.0.2", + "@lezer/css": "^1.1.7" + } + }, + "node_modules/@codemirror/lang-html": { + "version": "6.4.9", + "resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.9.tgz", + "integrity": "sha512-aQv37pIMSlueybId/2PVSP6NPnmurFDVmZwzc7jszd2KAF8qd4VBbvNYPXWQq90WIARjsdVkPbw29pszmHws3Q==", + "license": "MIT", + "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/lang-css": "^6.0.0", + "@codemirror/lang-javascript": "^6.0.0", + "@codemirror/language": "^6.4.0", "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.17.0", "@lezer/common": "^1.0.0", - "@lezer/javascript": "^1.0.0" + "@lezer/css": "^1.1.0", + "@lezer/html": "^1.3.0" } }, - "node_modules/@codemirror/lang-javascript/node_modules/@codemirror/language": { - "version": "6.10.1", - "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.1.tgz", - "integrity": "sha512-5GrXzrhq6k+gL5fjkAwt90nYDmjlzTIJV8THnxNFtNKWotMIlzzN+CpqxqwXOECnUdOndmSeWntVrVcv5axWRQ==", + "node_modules/@codemirror/lang-javascript": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.2.tgz", + "integrity": "sha512-VGQfY+FCc285AhWuwjYxQyUQcYurWlxdKYT4bqwr3Twnd5wP5WSeu52t4tvvuWmljT4EmgEgZCqSieokhtY8hg==", + "license": "MIT", "dependencies": { + "@codemirror/autocomplete": "^6.0.0", + "@codemirror/language": "^6.6.0", + "@codemirror/lint": "^6.0.0", "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.23.0", - "@lezer/common": "^1.1.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0", - "style-mod": "^4.0.0" + "@codemirror/view": "^6.17.0", + "@lezer/common": "^1.0.0", + "@lezer/javascript": "^1.0.0" } }, "node_modules/@codemirror/lang-markdown": { - "version": "6.2.4", - "resolved": "https://registry.npmjs.org/@codemirror/lang-markdown/-/lang-markdown-6.2.4.tgz", - "integrity": "sha512-UghkA1vSMs8bT7RSZM6vsIocigyah2bV00eRQuZy76401UmFZdsTsbQNBGdyxRQDOLeEvF5iFwap0BM8LKyd+g==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/@codemirror/lang-markdown/-/lang-markdown-6.3.0.tgz", + "integrity": "sha512-lYrI8SdL/vhd0w0aHIEvIRLRecLF7MiiRfzXFZY94dFwHqC9HtgxgagJ8fyYNBldijGatf9wkms60d8SrAj6Nw==", + "license": "MIT", "dependencies": { "@codemirror/autocomplete": "^6.7.1", "@codemirror/lang-html": "^6.0.0", @@ -2387,9 +2339,10 @@ } }, "node_modules/@codemirror/lang-markdown/node_modules/@codemirror/autocomplete": { - "version": "6.16.0", - "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.16.0.tgz", - "integrity": "sha512-P/LeCTtZHRTCU4xQsa89vSKWecYv1ZqwzOd5topheGRf+qtacFgBeIMQi3eL8Kt/BUNvxUWkx+5qP2jlGoARrg==", + "version": "6.18.1", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.18.1.tgz", + "integrity": "sha512-iWHdj/B1ethnHRTwZj+C1obmmuCzquH29EbcKr0qIjA9NfDeBDJ7vs+WOHsFeLeflE4o+dHfYndJloMKHUkWUA==", + "license": "MIT", "dependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.0.0", @@ -2403,10 +2356,11 @@ "@lezer/common": "^1.0.0" } }, - "node_modules/@codemirror/lang-markdown/node_modules/@codemirror/language": { - "version": "6.10.1", - "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.1.tgz", - "integrity": "sha512-5GrXzrhq6k+gL5fjkAwt90nYDmjlzTIJV8THnxNFtNKWotMIlzzN+CpqxqwXOECnUdOndmSeWntVrVcv5axWRQ==", + "node_modules/@codemirror/language": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.3.tgz", + "integrity": "sha512-kDqEU5sCP55Oabl6E7m5N+vZRoc0iWqgDVhEKifcHzPzjqCegcO4amfrYVL9PmPZpl4G0yjkpTpUO/Ui8CzO8A==", + "license": "MIT", "dependencies": { "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.23.0", @@ -2416,23 +2370,11 @@ "style-mod": "^4.0.0" } }, - "node_modules/@codemirror/language": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.2.1.tgz", - "integrity": "sha512-MC3svxuvIj0MRpFlGHxLS6vPyIdbTr2KKPEW46kCoCXw2ktb4NTkpkPBI/lSP/FoNXLCBJ0mrnUi1OoZxtpW1Q==", - "dependencies": { - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", - "@lezer/common": "^1.0.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0", - "style-mod": "^4.0.0" - } - }, "node_modules/@codemirror/lint": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.2.1.tgz", "integrity": "sha512-y1muai5U/uUPAGRyHMx9mHuHLypPcHWxzlZGknp/U5Mdb5Ol8Q5ZLp67UqyTbNFJJ3unVxZ8iX3g1fMN79S1JQ==", + "license": "MIT", "dependencies": { "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.0.0", @@ -2442,12 +2384,14 @@ "node_modules/@codemirror/state": { "version": "6.4.1", "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.4.1.tgz", - "integrity": "sha512-QkEyUiLhsJoZkbumGZlswmAhA7CBU02Wrz7zvH4SrcifbsqwlXShVXg65f3v/ts57W3dqyamEriMhij1Z3Zz4A==" + "integrity": "sha512-QkEyUiLhsJoZkbumGZlswmAhA7CBU02Wrz7zvH4SrcifbsqwlXShVXg65f3v/ts57W3dqyamEriMhij1Z3Zz4A==", + "license": "MIT" }, "node_modules/@codemirror/theme-one-dark": { "version": "6.1.2", "resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.2.tgz", "integrity": "sha512-F+sH0X16j/qFLMAfbciKTxVOwkdAS336b7AXTKOZhy8BR3eH/RelsnLgLFINrpST63mmN2OuwUt0W2ndUgYwUA==", + "license": "MIT", "dependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.0.0", @@ -2456,9 +2400,10 @@ } }, "node_modules/@codemirror/view": { - "version": "6.26.1", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.26.1.tgz", - "integrity": "sha512-wLw0t3R9AwOSQThdZ5Onw8QQtem5asE7+bPlnzc57eubPqiuJKIzwjMZ+C42vQett+iva+J8VgFV4RYWDBh5FA==", + "version": "6.34.1", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.34.1.tgz", + "integrity": "sha512-t1zK/l9UiRqwUNPm+pdIT0qzJlzuVckbTEMVNFhfWkGiBQClstzg+78vedCvLSX0xJEZ6lwZbPpnljL7L6iwMQ==", + "license": "MIT", "dependencies": { "@codemirror/state": "^6.4.0", "style-mod": "^4.1.0", @@ -2470,38 +2415,18 @@ "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", "dev": true, + "license": "MIT", "optional": true, "engines": { "node": ">=0.1.90" } }, - "node_modules/@cspotcode/source-map-support": { - "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, - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "devOptional": 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 +2434,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,25 +2451,12 @@ "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", "integrity": "sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==", "dev": true, + "license": "MIT", "dependencies": { "debug": "^3.1.0", "lodash.once": "^4.1.1" @@ -2555,6 +2467,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, + "license": "MIT", "dependencies": { "ms": "^2.1.1" } @@ -2563,6 +2476,7 @@ "version": "0.8.8", "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz", "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==", + "license": "MIT", "optional": true, "dependencies": { "@emotion/memoize": "0.7.4" @@ -2572,6 +2486,7 @@ "version": "0.7.4", "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==", + "license": "MIT", "optional": true }, "node_modules/@esbuild/aix-ppc64": { @@ -2942,112 +2857,32 @@ "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", - "integrity": "sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==", + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.8.tgz", + "integrity": "sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA==", + "license": "MIT", "dependencies": { - "@floating-ui/utils": "^0.2.1" + "@floating-ui/utils": "^0.2.8" } }, "node_modules/@floating-ui/dom": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.3.tgz", - "integrity": "sha512-RnDthu3mzPlQ31Ss/BTwQ1zjzIhr3lk1gZB1OC56h/1vEtaXkESrOqL5fQVMfXpwGtRwX+YsZBdyHtJMQnkArw==", + "version": "1.6.11", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.11.tgz", + "integrity": "sha512-qkMCxSR24v2vGkhYDo/UzxfJN3D4syqSjyuTFz6C7XcpU1pASPRieNI0Kj5VP3/503mOfYiGY891ugBX1GlABQ==", + "license": "MIT", "dependencies": { - "@floating-ui/core": "^1.0.0", - "@floating-ui/utils": "^0.2.0" + "@floating-ui/core": "^1.6.0", + "@floating-ui/utils": "^0.2.8" } }, "node_modules/@floating-ui/react-dom": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.8.tgz", - "integrity": "sha512-HOdqOt3R3OGeTKidaLvJKcgg75S6tibQ3Tif4eyd91QnIJWr0NLvoXFpJA/j8HqkFSL68GDca9AuyWEHlhyClw==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.2.tgz", + "integrity": "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==", + "license": "MIT", "dependencies": { - "@floating-ui/dom": "^1.6.1" + "@floating-ui/dom": "^1.0.0" }, "peerDependencies": { "react": ">=16.8.0", @@ -3055,40 +2890,33 @@ } }, "node_modules/@floating-ui/utils": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz", - "integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==" + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.8.tgz", + "integrity": "sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==", + "license": "MIT" }, "node_modules/@graphiql/plugin-explorer": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@graphiql/plugin-explorer/-/plugin-explorer-1.0.4.tgz", - "integrity": "sha512-Z0UDhHSX1u4PfiqtlOMrXVrSE11ifC0zycGwhzK+BeglS9z56hknEky7NwJvUb9qC7sTlTmXEgfGLsYb5DjKrg==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@graphiql/plugin-explorer/-/plugin-explorer-3.2.2.tgz", + "integrity": "sha512-zeBZJUAX9h+3nXw3GLHZoxi6wwYqDBU2L/xeSXSTagJhcLNW1Hwb/t/wb296hQ1x/9nyGySsTA0DQiiWV3rCBQ==", + "license": "MIT", "dependencies": { "graphiql-explorer": "^0.9.0" }, "peerDependencies": { - "@graphiql/react": "^0.20.4", - "graphql": "^15.5.0 || ^16.0.0", + "@graphiql/react": "^0.26.0", + "graphql": "^15.5.0 || ^16.0.0 || ^17.0.0-alpha.2", "react": "^16.8.0 || ^17 || ^18", "react-dom": "^16.8.0 || ^17 || ^18" } }, - "node_modules/@graphiql/plugin-explorer/node_modules/graphiql-explorer": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/graphiql-explorer/-/graphiql-explorer-0.9.0.tgz", - "integrity": "sha512-fZC/wsuatqiQDO2otchxriFO0LaWIo/ovF/CQJ1yOudmY0P7pzDiP+l9CEHUiWbizk3e99x6DQG4XG1VxA+d6A==", - "peerDependencies": { - "graphql": "^0.6.0 || ^0.7.0 || ^0.8.0-b || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0", - "react": "^15.6.0 || ^16.0.0", - "react-dom": "^15.6.0 || ^16.0.0" - } - }, "node_modules/@graphiql/react": { - "version": "0.20.4", - "resolved": "https://registry.npmjs.org/@graphiql/react/-/react-0.20.4.tgz", - "integrity": "sha512-LDgIlHa65pSngk8G2O0hvohNz4B41VUa7Yg6iPwifa1XreXxHIXjhV6FC1qi5oSjdCIRp4T8dkZnHA6iI5eElg==", + "version": "0.26.2", + "resolved": "https://registry.npmjs.org/@graphiql/react/-/react-0.26.2.tgz", + "integrity": "sha512-aO4GWf/kJmqrjO+PORT/NPxwGvPGlg+mwye1v8xAlf8Q9j7P0hVtVBawYaSLUCCfJ/QnH7JAP+0VRamyooZZCw==", + "license": "MIT", "dependencies": { - "@graphiql/toolkit": "^0.9.1", + "@graphiql/toolkit": "^0.11.0", "@headlessui/react": "^1.7.15", "@radix-ui/react-dialog": "^1.0.4", "@radix-ui/react-dropdown-menu": "^2.0.5", @@ -3097,15 +2925,16 @@ "@types/codemirror": "^5.60.8", "clsx": "^1.2.1", "codemirror": "^5.65.3", - "codemirror-graphql": "^2.0.11", + "codemirror-graphql": "^2.1.1", "copy-to-clipboard": "^3.2.0", "framer-motion": "^6.5.1", - "graphql-language-service": "^5.2.0", - "markdown-it": "^12.2.0", + "get-value": "^3.0.1", + "graphql-language-service": "^5.3.0", + "markdown-it": "^14.1.0", "set-value": "^4.1.0" }, "peerDependencies": { - "graphql": "^15.5.0 || ^16.0.0", + "graphql": "^15.5.0 || ^16.0.0 || ^17.0.0-alpha.2", "react": "^16.8.0 || ^17 || ^18", "react-dom": "^16.8.0 || ^17 || ^18" } @@ -3114,6 +2943,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.0.0.tgz", "integrity": "sha512-rtjk5ifyMzOna1c7PBu7J1VCt0PvA5wy3o8eMVnxMKb7z8KA7JFecvD04dSn14vj/bBaAbqRsGed5OjtofEnLA==", + "license": "MIT", "peer": true, "dependencies": { "@codemirror/state": "^6.0.0", @@ -3128,42 +2958,46 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/@graphiql/react/node_modules/codemirror-graphql": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/codemirror-graphql/-/codemirror-graphql-2.0.11.tgz", - "integrity": "sha512-j1QDDXKVkpin2VsyS0ke2nAhKal6/N1UJtgnBGrPe3gj9ZSP6/K8Xytft94k0xW6giIU/JhZjvW0GwwERNzbFA==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/codemirror-graphql/-/codemirror-graphql-2.1.1.tgz", + "integrity": "sha512-qVNd+H4OqkeBLDztB5bYllAmToxmZASOoELgbf+csNcoovIHKqSB/eppkzWI5jdQGd8bvLK1lTePfqXsCBFryw==", + "license": "MIT", "dependencies": { "@types/codemirror": "^0.0.90", - "graphql-language-service": "5.2.0" + "graphql-language-service": "5.3.0" }, "peerDependencies": { "@codemirror/language": "6.0.0", "codemirror": "^5.65.3", - "graphql": "^15.5.0 || ^16.0.0" + "graphql": "^15.5.0 || ^16.0.0 || ^17.0.0-alpha.2" } }, "node_modules/@graphiql/react/node_modules/codemirror-graphql/node_modules/@types/codemirror": { "version": "0.0.90", "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-0.0.90.tgz", "integrity": "sha512-8Z9+tSg27NPRGubbUPUCrt5DDG/OWzLph5BvcDykwR5D7RyZh5mhHG0uS1ePKV1YFCA+/cwc4Ey2AJAEFfV3IA==", + "license": "MIT", "dependencies": { "@types/tern": "*" } }, "node_modules/@graphiql/toolkit": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/@graphiql/toolkit/-/toolkit-0.9.1.tgz", - "integrity": "sha512-LVt9pdk0830so50ZnU2Znb2rclcoWznG8r8asqAENzV0U1FM1kuY0sdPpc/rBc9MmmNgnB6A+WZzDhq6dbhTHA==", + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@graphiql/toolkit/-/toolkit-0.11.0.tgz", + "integrity": "sha512-VqqQrvkMwgbGhj7J5907yfuAy5B1OCgOTIPi7gtRneG1jYmnqvSxi8Yrmu0B8G8fZxkxKVsYi8dE8EtsOBrTGQ==", + "license": "MIT", "dependencies": { "@n1ru4l/push-pull-async-iterable-iterator": "^3.1.0", "meros": "^1.1.4" }, "peerDependencies": { - "graphql": "^15.5.0 || ^16.0.0", + "graphql": "^15.5.0 || ^16.0.0 || ^17.0.0-alpha.2", "graphql-ws": ">= 4.5.0" }, "peerDependenciesMeta": { @@ -3173,10 +3007,11 @@ } }, "node_modules/@graphql-codegen/add": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@graphql-codegen/add/-/add-5.0.2.tgz", - "integrity": "sha512-ouBkSvMFUhda5VoKumo/ZvsZM9P5ZTyDsI8LW18VxSNWOjrTeLXBWHG8Gfaai0HwhflPtCYVABbriEcOmrRShQ==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@graphql-codegen/add/-/add-5.0.3.tgz", + "integrity": "sha512-SxXPmramkth8XtBlAHu4H4jYcYXM/o3p01+psU+0NADQowA8jtYkK6MW5rV6T+CxkEaNZItfSmZRPgIuypcqnA==", "dev": true, + "license": "MIT", "dependencies": { "@graphql-codegen/plugin-helpers": "^5.0.3", "tslib": "~2.6.0" @@ -3185,16 +3020,24 @@ "graphql": "^0.8.0 || ^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/@graphql-codegen/add/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "dev": true, + "license": "0BSD" + }, "node_modules/@graphql-codegen/cli": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@graphql-codegen/cli/-/cli-5.0.2.tgz", - "integrity": "sha512-MBIaFqDiLKuO4ojN6xxG9/xL9wmfD3ZjZ7RsPjwQnSHBCUXnEkdKvX+JVpx87Pq29Ycn8wTJUguXnTZ7Di0Mlw==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@graphql-codegen/cli/-/cli-5.0.3.tgz", + "integrity": "sha512-ULpF6Sbu2d7vNEOgBtE9avQp2oMgcPY/QBYcCqk0Xru5fz+ISjcovQX29V7CS7y5wWBRzNLoXwJQGeEyWbl05g==", "dev": true, + "license": "MIT", "dependencies": { "@babel/generator": "^7.18.13", "@babel/template": "^7.18.10", "@babel/types": "^7.18.13", - "@graphql-codegen/client-preset": "^4.2.2", + "@graphql-codegen/client-preset": "^4.4.0", "@graphql-codegen/core": "^4.0.2", "@graphql-codegen/plugin-helpers": "^5.0.3", "@graphql-tools/apollo-engine-loader": "^8.0.0", @@ -3207,12 +3050,12 @@ "@graphql-tools/prisma-loader": "^8.0.0", "@graphql-tools/url-loader": "^8.0.0", "@graphql-tools/utils": "^10.0.0", - "@whatwg-node/fetch": "^0.8.0", + "@whatwg-node/fetch": "^0.9.20", "chalk": "^4.1.0", "cosmiconfig": "^8.1.3", "debounce": "^1.2.0", "detect-indent": "^6.0.0", - "graphql-config": "^5.0.2", + "graphql-config": "^5.1.1", "inquirer": "^8.0.0", "is-glob": "^4.0.1", "jiti": "^1.17.1", @@ -3233,6 +3076,9 @@ "graphql-codegen": "cjs/bin.js", "graphql-codegen-esm": "esm/bin.js" }, + "engines": { + "node": ">=16" + }, "peerDependencies": { "@parcel/watcher": "^2.1.0", "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" @@ -3244,34 +3090,46 @@ } }, "node_modules/@graphql-codegen/client-preset": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/@graphql-codegen/client-preset/-/client-preset-4.2.5.tgz", - "integrity": "sha512-hAdB6HN8EDmkoBtr0bPUN/7NH6svzqbcTDMWBCRXPESXkl7y80po+IXrXUjsSrvhKG8xkNXgJNz/2mjwHzywcA==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@graphql-codegen/client-preset/-/client-preset-4.4.0.tgz", + "integrity": "sha512-Q0NHFK7KXLhEaRC/k82ge0dHDfeHJEvvDeV0vV3+oSurHNa/lpxQtbK2BqknZe+JDfZ1YOOvYT93XsAkYD+SQg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.20.2", "@babel/template": "^7.20.7", - "@graphql-codegen/add": "^5.0.2", - "@graphql-codegen/gql-tag-operations": "4.0.6", - "@graphql-codegen/plugin-helpers": "^5.0.3", - "@graphql-codegen/typed-document-node": "^5.0.6", - "@graphql-codegen/typescript": "^4.0.6", - "@graphql-codegen/typescript-operations": "^4.2.0", - "@graphql-codegen/visitor-plugin-common": "^5.1.0", + "@graphql-codegen/add": "^5.0.3", + "@graphql-codegen/gql-tag-operations": "4.0.10", + "@graphql-codegen/plugin-helpers": "^5.0.4", + "@graphql-codegen/typed-document-node": "^5.0.10", + "@graphql-codegen/typescript": "^4.1.0", + "@graphql-codegen/typescript-operations": "^4.3.0", + "@graphql-codegen/visitor-plugin-common": "^5.4.0", "@graphql-tools/documents": "^1.0.0", "@graphql-tools/utils": "^10.0.0", "@graphql-typed-document-node/core": "3.2.0", "tslib": "~2.6.0" }, + "engines": { + "node": ">=16" + }, "peerDependencies": { "graphql": "^0.8.0 || ^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/@graphql-codegen/client-preset/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "dev": true, + "license": "0BSD" + }, "node_modules/@graphql-codegen/core": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@graphql-codegen/core/-/core-4.0.2.tgz", "integrity": "sha512-IZbpkhwVqgizcjNiaVzNAzm/xbWT6YnGgeOLwVjm4KbJn3V2jchVtuzHH09G5/WkkLSk2wgbXNdwjM41JxO6Eg==", "dev": true, + "license": "MIT", "dependencies": { "@graphql-codegen/plugin-helpers": "^5.0.3", "@graphql-tools/schema": "^10.0.0", @@ -3282,27 +3140,46 @@ "graphql": "^0.8.0 || ^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/@graphql-codegen/core/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "dev": true, + "license": "0BSD" + }, "node_modules/@graphql-codegen/gql-tag-operations": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@graphql-codegen/gql-tag-operations/-/gql-tag-operations-4.0.6.tgz", - "integrity": "sha512-y6iXEDpDNjwNxJw3WZqX1/Znj0QHW7+y8O+t2V8qvbTT+3kb2lr9ntc8By7vCr6ctw9tXI4XKaJgpTstJDOwFA==", + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/@graphql-codegen/gql-tag-operations/-/gql-tag-operations-4.0.10.tgz", + "integrity": "sha512-WsBEVL3XQdBboFJJL5WxrUjkuo3B7Sa51R9NbT7PKBe0HCNstoouGZIvQJRUubttFCqTTyoFtNsoRSKB+rsRug==", "dev": true, + "license": "MIT", "dependencies": { - "@graphql-codegen/plugin-helpers": "^5.0.3", - "@graphql-codegen/visitor-plugin-common": "5.1.0", + "@graphql-codegen/plugin-helpers": "^5.0.4", + "@graphql-codegen/visitor-plugin-common": "5.4.0", "@graphql-tools/utils": "^10.0.0", "auto-bind": "~4.0.0", "tslib": "~2.6.0" }, + "engines": { + "node": ">=16" + }, "peerDependencies": { "graphql": "^0.8.0 || ^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/@graphql-codegen/gql-tag-operations/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "dev": true, + "license": "0BSD" + }, "node_modules/@graphql-codegen/plugin-helpers": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/@graphql-codegen/plugin-helpers/-/plugin-helpers-5.0.4.tgz", "integrity": "sha512-MOIuHFNWUnFnqVmiXtrI+4UziMTYrcquljaI5f/T/Bc7oO7sXcfkAvgkNWEEi9xWreYwvuer3VHCuPI/lAFWbw==", "dev": true, + "license": "MIT", "dependencies": { "@graphql-tools/utils": "^10.0.0", "change-case-all": "1.0.15", @@ -3315,11 +3192,19 @@ "graphql": "^0.8.0 || ^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/@graphql-codegen/plugin-helpers/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "dev": true, + "license": "0BSD" + }, "node_modules/@graphql-codegen/schema-ast": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@graphql-codegen/schema-ast/-/schema-ast-4.0.2.tgz", - "integrity": "sha512-5mVAOQQK3Oz7EtMl/l3vOQdc2aYClUzVDHHkMvZlunc+KlGgl81j8TLa+X7ANIllqU4fUEsQU3lJmk4hXP6K7Q==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@graphql-codegen/schema-ast/-/schema-ast-4.1.0.tgz", + "integrity": "sha512-kZVn0z+th9SvqxfKYgztA6PM7mhnSZaj4fiuBWvMTqA+QqQ9BBed6Pz41KuD/jr0gJtnlr2A4++/0VlpVbCTmQ==", "dev": true, + "license": "MIT", "dependencies": { "@graphql-codegen/plugin-helpers": "^5.0.3", "@graphql-tools/utils": "^10.0.0", @@ -3329,82 +3214,102 @@ "graphql": "^0.8.0 || ^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/@graphql-codegen/schema-ast/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "dev": true, + "license": "0BSD" + }, "node_modules/@graphql-codegen/typed-document-node": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/@graphql-codegen/typed-document-node/-/typed-document-node-5.0.6.tgz", - "integrity": "sha512-US0J95hOE2/W/h42w4oiY+DFKG7IetEN1mQMgXXeat1w6FAR5PlIz4JrRrEkiVfVetZ1g7K78SOwBD8/IJnDiA==", + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/@graphql-codegen/typed-document-node/-/typed-document-node-5.0.10.tgz", + "integrity": "sha512-YPDUNs6x0muoVWlbY2yEs0lGxFHMTszlGDh6klT/5rqiTDTZg3zz8Wd1ZTihkcH8+V6T0AT9qDWwcx9fcS2tvQ==", "dev": true, + "license": "MIT", "dependencies": { - "@graphql-codegen/plugin-helpers": "^5.0.3", - "@graphql-codegen/visitor-plugin-common": "5.1.0", + "@graphql-codegen/plugin-helpers": "^5.0.4", + "@graphql-codegen/visitor-plugin-common": "5.4.0", "auto-bind": "~4.0.0", "change-case-all": "1.0.15", "tslib": "~2.6.0" }, + "engines": { + "node": ">=16" + }, "peerDependencies": { "graphql": "^0.8.0 || ^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/@graphql-codegen/typed-document-node/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "dev": true, + "license": "0BSD" + }, "node_modules/@graphql-codegen/typescript": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript/-/typescript-4.0.9.tgz", - "integrity": "sha512-0O35DMR4d/ctuHL1Zo6mRUUzp0BoszKfeWsa6sCm/g70+S98+hEfTwZNDkQHylLxapiyjssF9uw/F+sXqejqLw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript/-/typescript-4.1.0.tgz", + "integrity": "sha512-/fS53Nh6U6c58GTOxqfyKTLQfQv36P8II/vPw/fg0cdcWbALhRPls69P8vXUWjrElmLKzCrdusBWPp/r+AKUBQ==", "dev": true, + "license": "MIT", "dependencies": { "@graphql-codegen/plugin-helpers": "^5.0.4", "@graphql-codegen/schema-ast": "^4.0.2", - "@graphql-codegen/visitor-plugin-common": "5.3.1", + "@graphql-codegen/visitor-plugin-common": "5.4.0", "auto-bind": "~4.0.0", "tslib": "~2.6.0" }, + "engines": { + "node": ">=16" + }, "peerDependencies": { "graphql": "^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" } }, "node_modules/@graphql-codegen/typescript-operations": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript-operations/-/typescript-operations-4.2.0.tgz", - "integrity": "sha512-lmuwYb03XC7LNRS8oo9M4/vlOrq/wOKmTLBHlltK2YJ1BO/4K/Q9Jdv/jDmJpNydHVR1fmeF4wAfsIp1f9JibA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript-operations/-/typescript-operations-4.3.0.tgz", + "integrity": "sha512-ZORwMy8OgsiYd9EZUhTMd4/g5LvTFpx6Fh6dNN0cxFkqSc6KhjX0vhzWsyK8N9+ILaHSutT8UTrLMdJi35HzDQ==", "dev": true, + "license": "MIT", "dependencies": { - "@graphql-codegen/plugin-helpers": "^5.0.3", - "@graphql-codegen/typescript": "^4.0.6", - "@graphql-codegen/visitor-plugin-common": "5.1.0", + "@graphql-codegen/plugin-helpers": "^5.0.4", + "@graphql-codegen/typescript": "^4.1.0", + "@graphql-codegen/visitor-plugin-common": "5.4.0", "auto-bind": "~4.0.0", "tslib": "~2.6.0" }, + "engines": { + "node": ">=16" + }, "peerDependencies": { "graphql": "^0.8.0 || ^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/@graphql-codegen/typescript/node_modules/@graphql-codegen/visitor-plugin-common": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-5.3.1.tgz", - "integrity": "sha512-MktoBdNZhSmugiDjmFl1z6rEUUaqyxtFJYWnDilE7onkPgyw//O0M+TuPBJPBWdyV6J2ond0Hdqtq+rkghgSIQ==", + "node_modules/@graphql-codegen/typescript-operations/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", "dev": true, - "dependencies": { - "@graphql-codegen/plugin-helpers": "^5.0.4", - "@graphql-tools/optimize": "^2.0.0", - "@graphql-tools/relay-operation-optimizer": "^7.0.0", - "@graphql-tools/utils": "^10.0.0", - "auto-bind": "~4.0.0", - "change-case-all": "1.0.15", - "dependency-graph": "^0.11.0", - "graphql-tag": "^2.11.0", - "parse-filepath": "^1.0.2", - "tslib": "~2.6.0" - }, - "peerDependencies": { - "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" - } + "license": "0BSD" + }, + "node_modules/@graphql-codegen/typescript/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "dev": true, + "license": "0BSD" }, "node_modules/@graphql-codegen/visitor-plugin-common": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-5.1.0.tgz", - "integrity": "sha512-eamQxtA9bjJqI2lU5eYoA1GbdMIRT2X8m8vhWYsVQVWD3qM7sx/IqJU0kx0J3Vd4/CSd36BzL6RKwksibytDIg==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-5.4.0.tgz", + "integrity": "sha512-tL7hOrO+4MiNfDiHewhRQCiH9GTAh0M9Y/BZxYGGEdnrfGgqK5pCxtjq7EY/L19VGIyU7hhzYTQ0r1HzEbB4Jw==", "dev": true, + "license": "MIT", "dependencies": { - "@graphql-codegen/plugin-helpers": "^5.0.3", + "@graphql-codegen/plugin-helpers": "^5.0.4", "@graphql-tools/optimize": "^2.0.0", "@graphql-tools/relay-operation-optimizer": "^7.0.0", "@graphql-tools/utils": "^10.0.0", @@ -3415,18 +3320,29 @@ "parse-filepath": "^1.0.2", "tslib": "~2.6.0" }, + "engines": { + "node": ">=16" + }, "peerDependencies": { "graphql": "^0.8.0 || ^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/@graphql-codegen/visitor-plugin-common/node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "dev": true, + "license": "0BSD" + }, "node_modules/@graphql-tools/apollo-engine-loader": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@graphql-tools/apollo-engine-loader/-/apollo-engine-loader-8.0.1.tgz", - "integrity": "sha512-NaPeVjtrfbPXcl+MLQCJLWtqe2/E4bbAqcauEOQ+3sizw1Fc2CNmhHRF8a6W4D0ekvTRRXAMptXYgA2uConbrA==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@graphql-tools/apollo-engine-loader/-/apollo-engine-loader-8.0.2.tgz", + "integrity": "sha512-HTFoCILMU7u/Y97G5iu2EPSMTW/b/Lx6Ww2emX/WDtubU2A/7RqzBUjrDj/JMPTEblOAPUwJ1XcxtvXgQVaSyQ==", "dev": true, + "license": "MIT", "dependencies": { "@ardatan/sync-fetch": "^0.0.1", - "@graphql-tools/utils": "^10.0.13", + "@graphql-tools/utils": "^10.5.5", "@whatwg-node/fetch": "^0.9.0", "tslib": "^2.4.0" }, @@ -3437,57 +3353,14 @@ "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, - "node_modules/@graphql-tools/apollo-engine-loader/node_modules/@whatwg-node/events": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@whatwg-node/events/-/events-0.1.1.tgz", - "integrity": "sha512-AyQEn5hIPV7Ze+xFoXVU3QTHXVbWPrzaOkxtENMPMuNL6VVHrp4hHfDt9nrQpjO7BgvuM95dMtkycX5M/DZR3w==", - "dev": true, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@graphql-tools/apollo-engine-loader/node_modules/@whatwg-node/fetch": { - "version": "0.9.17", - "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.9.17.tgz", - "integrity": "sha512-TDYP3CpCrxwxpiNY0UMNf096H5Ihf67BK1iKGegQl5u9SlpEDYrvnV71gWBGJm+Xm31qOy8ATgma9rm8Pe7/5Q==", - "dev": true, - "dependencies": { - "@whatwg-node/node-fetch": "^0.5.7", - "urlpattern-polyfill": "^10.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@graphql-tools/apollo-engine-loader/node_modules/@whatwg-node/node-fetch": { - "version": "0.5.10", - "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.5.10.tgz", - "integrity": "sha512-KIAHepie/T1PRkUfze4t+bPlyvpxlWiXTPtcGlbIZ0vWkBJMdRmCg4ZrJ2y4XaO1eTPo1HlWYUuj1WvoIpumqg==", - "dev": true, - "dependencies": { - "@kamilkisiela/fast-url-parser": "^1.1.4", - "@whatwg-node/events": "^0.1.0", - "busboy": "^1.6.0", - "fast-querystring": "^1.1.1", - "tslib": "^2.3.1" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@graphql-tools/apollo-engine-loader/node_modules/urlpattern-polyfill": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz", - "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==", - "dev": true - }, "node_modules/@graphql-tools/batch-execute": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/@graphql-tools/batch-execute/-/batch-execute-9.0.4.tgz", - "integrity": "sha512-kkebDLXgDrep5Y0gK1RN3DMUlLqNhg60OAz0lTCqrYeja6DshxLtLkj+zV4mVbBA4mQOEoBmw6g1LZs3dA84/w==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/@graphql-tools/batch-execute/-/batch-execute-9.0.5.tgz", + "integrity": "sha512-wkHLqBNtprKuNk+6ZoOw/RthsnGDycIjtOo976K8f0IgbE7fRNO9SnyhjSziHaIWVDjOuP3XaJD5v/i3vQsa5Q==", "dev": true, + "license": "MIT", "dependencies": { - "@graphql-tools/utils": "^10.0.13", + "@graphql-tools/utils": "^10.5.5", "dataloader": "^2.2.2", "tslib": "^2.4.0", "value-or-promise": "^1.0.12" @@ -3500,13 +3373,14 @@ } }, "node_modules/@graphql-tools/code-file-loader": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/@graphql-tools/code-file-loader/-/code-file-loader-8.1.1.tgz", - "integrity": "sha512-q4KN25EPSUztc8rA8YUU3ufh721Yk12xXDbtUA+YstczWS7a1RJlghYMFEfR1HsHSYbF7cUqkbnTKSGM3o52bQ==", + "version": "8.1.4", + "resolved": "https://registry.npmjs.org/@graphql-tools/code-file-loader/-/code-file-loader-8.1.4.tgz", + "integrity": "sha512-vwMk+trCGLidWTmwC5CybqN0+W9fG6VMf61HEggUGBcYLzUmTAIn9DXsU1IFeLRtn8rNx8xH4JpDGd6fv0YWUQ==", "dev": true, + "license": "MIT", "dependencies": { - "@graphql-tools/graphql-tag-pluck": "8.3.0", - "@graphql-tools/utils": "^10.0.13", + "@graphql-tools/graphql-tag-pluck": "8.3.3", + "@graphql-tools/utils": "^10.5.5", "globby": "^11.0.3", "tslib": "^2.4.0", "unixify": "^1.0.0" @@ -3519,15 +3393,17 @@ } }, "node_modules/@graphql-tools/delegate": { - "version": "10.0.4", - "resolved": "https://registry.npmjs.org/@graphql-tools/delegate/-/delegate-10.0.4.tgz", - "integrity": "sha512-WswZRbQZMh/ebhc8zSomK9DIh6Pd5KbuiMsyiKkKz37TWTrlCOe+4C/fyrBFez30ksq6oFyCeSKMwfrCbeGo0Q==", + "version": "10.0.26", + "resolved": "https://registry.npmjs.org/@graphql-tools/delegate/-/delegate-10.0.26.tgz", + "integrity": "sha512-8KaphA86onhO8h9WJeu7YpRNwYDkbbD+KctV6LPJ99vK3w+rQuWkZoxrL1H2nN2FwDBP/9OXposeE7z5C6cv8w==", "dev": true, + "license": "MIT", "dependencies": { - "@graphql-tools/batch-execute": "^9.0.4", - "@graphql-tools/executor": "^1.2.1", - "@graphql-tools/schema": "^10.0.3", - "@graphql-tools/utils": "^10.0.13", + "@graphql-tools/batch-execute": "^9.0.5", + "@graphql-tools/executor": "^1.3.2", + "@graphql-tools/schema": "^10.0.7", + "@graphql-tools/utils": "^10.5.5", + "@repeaterjs/repeater": "^3.0.6", "dataloader": "^2.2.2", "tslib": "^2.5.0" }, @@ -3539,10 +3415,11 @@ } }, "node_modules/@graphql-tools/documents": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@graphql-tools/documents/-/documents-1.0.0.tgz", - "integrity": "sha512-rHGjX1vg/nZ2DKqRGfDPNC55CWZBMldEVcH+91BThRa6JeT80NqXknffLLEZLRUxyikCfkwMsk6xR3UNMqG0Rg==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@graphql-tools/documents/-/documents-1.0.1.tgz", + "integrity": "sha512-aweoMH15wNJ8g7b2r4C4WRuJxZ0ca8HtNO54rkye/3duxTkW4fGBEutCx03jCIr5+a1l+4vFJNP859QnAVBVCA==", "dev": true, + "license": "MIT", "dependencies": { "lodash.sortby": "^4.7.0", "tslib": "^2.4.0" @@ -3555,12 +3432,13 @@ } }, "node_modules/@graphql-tools/executor": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/@graphql-tools/executor/-/executor-1.2.5.tgz", - "integrity": "sha512-s7sW4K3BUNsk9sjq+vNicwb9KwcR3G55uS/CI8KZQ4x0ZdeYMIwpeU9MVeORCCpHuQyTaV+/VnO0hFrS/ygzsg==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@graphql-tools/executor/-/executor-1.3.2.tgz", + "integrity": "sha512-U8nAR709IPNjwf0aLG6U9FlX0t7vA4cdWvL4RtMR/L/Ll4OHZ39OqUtq6moy+kLRRwLTqLif6iiUYrxnWpUGXw==", "dev": true, + "license": "MIT", "dependencies": { - "@graphql-tools/utils": "^10.1.1", + "@graphql-tools/utils": "^10.5.5", "@graphql-typed-document-node/core": "3.2.0", "@repeaterjs/repeater": "^3.0.4", "tslib": "^2.4.0", @@ -3574,17 +3452,18 @@ } }, "node_modules/@graphql-tools/executor-graphql-ws": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@graphql-tools/executor-graphql-ws/-/executor-graphql-ws-1.1.2.tgz", - "integrity": "sha512-+9ZK0rychTH1LUv4iZqJ4ESbmULJMTsv3XlFooPUngpxZkk00q6LqHKJRrsLErmQrVaC7cwQCaRBJa0teK17Lg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@graphql-tools/executor-graphql-ws/-/executor-graphql-ws-1.3.1.tgz", + "integrity": "sha512-UAS5aeWLqv89iJ899OK8uwBMVGVH4nhJDIuIT+8z8f5iPiIpfqt2ipZLasdSLpi5WUpYDIolnVUFd2NvzccO7A==", "dev": true, + "license": "MIT", "dependencies": { - "@graphql-tools/utils": "^10.0.13", + "@graphql-tools/utils": "^10.5.5", "@types/ws": "^8.0.0", "graphql-ws": "^5.14.0", "isomorphic-ws": "^5.0.0", "tslib": "^2.4.0", - "ws": "^8.13.0" + "ws": "^8.17.1" }, "engines": { "node": ">=16.0.0" @@ -3594,12 +3473,13 @@ } }, "node_modules/@graphql-tools/executor-http": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@graphql-tools/executor-http/-/executor-http-1.0.9.tgz", - "integrity": "sha512-+NXaZd2MWbbrWHqU4EhXcrDbogeiCDmEbrAN+rMn4Nu2okDjn2MTFDbTIab87oEubQCH4Te1wDkWPKrzXup7+Q==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@graphql-tools/executor-http/-/executor-http-1.1.7.tgz", + "integrity": "sha512-iWTE1MtCW26jxs5DeXsUNPkIFmVWEhioJx0wcDSacJ0onXjyMalfae5SgsuwHMQCVuvvUtQUgb8a9hmPhQ0y+g==", "dev": true, + "license": "MIT", "dependencies": { - "@graphql-tools/utils": "^10.0.13", + "@graphql-tools/utils": "^10.5.5", "@repeaterjs/repeater": "^3.0.4", "@whatwg-node/fetch": "^0.9.0", "extract-files": "^11.0.0", @@ -3614,61 +3494,18 @@ "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, - "node_modules/@graphql-tools/executor-http/node_modules/@whatwg-node/events": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@whatwg-node/events/-/events-0.1.1.tgz", - "integrity": "sha512-AyQEn5hIPV7Ze+xFoXVU3QTHXVbWPrzaOkxtENMPMuNL6VVHrp4hHfDt9nrQpjO7BgvuM95dMtkycX5M/DZR3w==", - "dev": true, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@graphql-tools/executor-http/node_modules/@whatwg-node/fetch": { - "version": "0.9.17", - "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.9.17.tgz", - "integrity": "sha512-TDYP3CpCrxwxpiNY0UMNf096H5Ihf67BK1iKGegQl5u9SlpEDYrvnV71gWBGJm+Xm31qOy8ATgma9rm8Pe7/5Q==", - "dev": true, - "dependencies": { - "@whatwg-node/node-fetch": "^0.5.7", - "urlpattern-polyfill": "^10.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@graphql-tools/executor-http/node_modules/@whatwg-node/node-fetch": { - "version": "0.5.10", - "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.5.10.tgz", - "integrity": "sha512-KIAHepie/T1PRkUfze4t+bPlyvpxlWiXTPtcGlbIZ0vWkBJMdRmCg4ZrJ2y4XaO1eTPo1HlWYUuj1WvoIpumqg==", - "dev": true, - "dependencies": { - "@kamilkisiela/fast-url-parser": "^1.1.4", - "@whatwg-node/events": "^0.1.0", - "busboy": "^1.6.0", - "fast-querystring": "^1.1.1", - "tslib": "^2.3.1" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@graphql-tools/executor-http/node_modules/urlpattern-polyfill": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz", - "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==", - "dev": true - }, "node_modules/@graphql-tools/executor-legacy-ws": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@graphql-tools/executor-legacy-ws/-/executor-legacy-ws-1.0.6.tgz", - "integrity": "sha512-lDSxz9VyyquOrvSuCCnld3256Hmd+QI2lkmkEv7d4mdzkxkK4ddAWW1geQiWrQvWmdsmcnGGlZ7gDGbhEExwqg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@graphql-tools/executor-legacy-ws/-/executor-legacy-ws-1.1.1.tgz", + "integrity": "sha512-9J5WBd9D7+V299BsMJmgMVBsUl01rqzpfWx+if2r5k9xBYchj5delUOsx337XtNLb3Ewoy0Za24DkNYIx3Cgyg==", "dev": true, + "license": "MIT", "dependencies": { - "@graphql-tools/utils": "^10.0.13", + "@graphql-tools/utils": "^10.5.5", "@types/ws": "^8.0.0", "isomorphic-ws": "^5.0.0", "tslib": "^2.4.0", - "ws": "^8.15.0" + "ws": "^8.17.1" }, "engines": { "node": ">=16.0.0" @@ -3678,15 +3515,16 @@ } }, "node_modules/@graphql-tools/git-loader": { - "version": "8.0.5", - "resolved": "https://registry.npmjs.org/@graphql-tools/git-loader/-/git-loader-8.0.5.tgz", - "integrity": "sha512-P97/1mhruDiA6D5WUmx3n/aeGPLWj2+4dpzDOxFGGU+z9NcI/JdygMkeFpGZNHeJfw+kHfxgPcMPnxHcyhAoVA==", + "version": "8.0.8", + "resolved": "https://registry.npmjs.org/@graphql-tools/git-loader/-/git-loader-8.0.8.tgz", + "integrity": "sha512-1zGkgVDecM8I4+ymSuqOpckdAiFRbD3TVqOIcATolJ3I5a2eJhzqADZaOvMHzWWs69PPzOBzjcOj6EdVUeNBug==", "dev": true, + "license": "MIT", "dependencies": { - "@graphql-tools/graphql-tag-pluck": "8.3.0", - "@graphql-tools/utils": "^10.0.13", + "@graphql-tools/graphql-tag-pluck": "8.3.3", + "@graphql-tools/utils": "^10.5.5", "is-glob": "4.0.3", - "micromatch": "^4.0.4", + "micromatch": "^4.0.8", "tslib": "^2.4.0", "unixify": "^1.0.0" }, @@ -3698,15 +3536,16 @@ } }, "node_modules/@graphql-tools/github-loader": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@graphql-tools/github-loader/-/github-loader-8.0.1.tgz", - "integrity": "sha512-W4dFLQJ5GtKGltvh/u1apWRFKBQOsDzFxO9cJkOYZj1VzHCpRF43uLST4VbCfWve+AwBqOuKr7YgkHoxpRMkcg==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@graphql-tools/github-loader/-/github-loader-8.0.2.tgz", + "integrity": "sha512-VrhEOI+lh/vH5XyVBK3uNBYGFz9lHR5elADT44tBuBI5eyzm1N/dCaJ1nW9mVTij7deLVEKetTOHrMETVqyZ+A==", "dev": true, + "license": "MIT", "dependencies": { "@ardatan/sync-fetch": "^0.0.1", - "@graphql-tools/executor-http": "^1.0.9", - "@graphql-tools/graphql-tag-pluck": "^8.0.0", - "@graphql-tools/utils": "^10.0.13", + "@graphql-tools/executor-http": "^1.1.7", + "@graphql-tools/graphql-tag-pluck": "^8.3.3", + "@graphql-tools/utils": "^10.5.5", "@whatwg-node/fetch": "^0.9.0", "tslib": "^2.4.0", "value-or-promise": "^1.0.12" @@ -3718,58 +3557,15 @@ "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, - "node_modules/@graphql-tools/github-loader/node_modules/@whatwg-node/events": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@whatwg-node/events/-/events-0.1.1.tgz", - "integrity": "sha512-AyQEn5hIPV7Ze+xFoXVU3QTHXVbWPrzaOkxtENMPMuNL6VVHrp4hHfDt9nrQpjO7BgvuM95dMtkycX5M/DZR3w==", - "dev": true, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@graphql-tools/github-loader/node_modules/@whatwg-node/fetch": { - "version": "0.9.17", - "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.9.17.tgz", - "integrity": "sha512-TDYP3CpCrxwxpiNY0UMNf096H5Ihf67BK1iKGegQl5u9SlpEDYrvnV71gWBGJm+Xm31qOy8ATgma9rm8Pe7/5Q==", - "dev": true, - "dependencies": { - "@whatwg-node/node-fetch": "^0.5.7", - "urlpattern-polyfill": "^10.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@graphql-tools/github-loader/node_modules/@whatwg-node/node-fetch": { - "version": "0.5.10", - "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.5.10.tgz", - "integrity": "sha512-KIAHepie/T1PRkUfze4t+bPlyvpxlWiXTPtcGlbIZ0vWkBJMdRmCg4ZrJ2y4XaO1eTPo1HlWYUuj1WvoIpumqg==", - "dev": true, - "dependencies": { - "@kamilkisiela/fast-url-parser": "^1.1.4", - "@whatwg-node/events": "^0.1.0", - "busboy": "^1.6.0", - "fast-querystring": "^1.1.1", - "tslib": "^2.3.1" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@graphql-tools/github-loader/node_modules/urlpattern-polyfill": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz", - "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==", - "dev": true - }, "node_modules/@graphql-tools/graphql-file-loader": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@graphql-tools/graphql-file-loader/-/graphql-file-loader-8.0.1.tgz", - "integrity": "sha512-7gswMqWBabTSmqbaNyWSmRRpStWlcCkBc73E6NZNlh4YNuiyKOwbvSkOUYFOqFMfEL+cFsXgAvr87Vz4XrYSbA==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@graphql-tools/graphql-file-loader/-/graphql-file-loader-8.0.2.tgz", + "integrity": "sha512-uf/vkO7jIU19hOZKL/DPyE5vm3wH7nFpfNYrMGGx8XlDK7l0al/MO7HQy+4YUPENkAd8FBgRNt2Ilm1fUXCwJg==", "dev": true, + "license": "MIT", "dependencies": { - "@graphql-tools/import": "7.0.1", - "@graphql-tools/utils": "^10.0.13", + "@graphql-tools/import": "7.0.2", + "@graphql-tools/utils": "^10.5.5", "globby": "^11.0.3", "tslib": "^2.4.0", "unixify": "^1.0.0" @@ -3782,17 +3578,18 @@ } }, "node_modules/@graphql-tools/graphql-tag-pluck": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/@graphql-tools/graphql-tag-pluck/-/graphql-tag-pluck-8.3.0.tgz", - "integrity": "sha512-gNqukC+s7iHC7vQZmx1SEJQmLnOguBq+aqE2zV2+o1hxkExvKqyFli1SY/9gmukFIKpKutCIj+8yLOM+jARutw==", + "version": "8.3.3", + "resolved": "https://registry.npmjs.org/@graphql-tools/graphql-tag-pluck/-/graphql-tag-pluck-8.3.3.tgz", + "integrity": "sha512-G+8UNUa54ct/f9hNHo7Ez61BeAoaeXYhtfq8rYu0m9Upr/BCgsQmuvEgyHBRSFVkqOQj56H5aBwKW68SPrrU8g==", "dev": true, + "license": "MIT", "dependencies": { "@babel/core": "^7.22.9", "@babel/parser": "^7.16.8", "@babel/plugin-syntax-import-assertions": "^7.20.0", "@babel/traverse": "^7.16.8", "@babel/types": "^7.16.8", - "@graphql-tools/utils": "^10.0.13", + "@graphql-tools/utils": "^10.5.5", "tslib": "^2.4.0" }, "engines": { @@ -3803,12 +3600,13 @@ } }, "node_modules/@graphql-tools/import": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@graphql-tools/import/-/import-7.0.1.tgz", - "integrity": "sha512-935uAjAS8UAeXThqHfYVr4HEAp6nHJ2sximZKO1RzUTq5WoALMAhhGARl0+ecm6X+cqNUwIChJbjtaa6P/ML0w==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@graphql-tools/import/-/import-7.0.2.tgz", + "integrity": "sha512-7OpShcq/yRwCcMcTyLNIonYw9l1yD+Im/znN/l9SRsThYGhMlojEHIntn7f9IArCnHR71uZk5UQioGLUTG6E6A==", "dev": true, + "license": "MIT", "dependencies": { - "@graphql-tools/utils": "^10.0.13", + "@graphql-tools/utils": "^10.5.5", "resolve-from": "5.0.0", "tslib": "^2.4.0" }, @@ -3820,12 +3618,13 @@ } }, "node_modules/@graphql-tools/json-file-loader": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@graphql-tools/json-file-loader/-/json-file-loader-8.0.1.tgz", - "integrity": "sha512-lAy2VqxDAHjVyqeJonCP6TUemrpYdDuKt25a10X6zY2Yn3iFYGnuIDQ64cv3ytyGY6KPyPB+Kp+ZfOkNDG3FQA==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@graphql-tools/json-file-loader/-/json-file-loader-8.0.2.tgz", + "integrity": "sha512-gdsOfH+wU4LAineG3oiqw4DNrwAdmr/ZfZ1JiL3wlUsk16P78qmM8jD9H7pkdMuwVdD0e/d+QrVhbo9qQ0CcKw==", "dev": true, + "license": "MIT", "dependencies": { - "@graphql-tools/utils": "^10.0.13", + "@graphql-tools/utils": "^10.5.5", "globby": "^11.0.3", "tslib": "^2.4.0", "unixify": "^1.0.0" @@ -3838,13 +3637,14 @@ } }, "node_modules/@graphql-tools/load": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@graphql-tools/load/-/load-8.0.2.tgz", - "integrity": "sha512-S+E/cmyVmJ3CuCNfDuNF2EyovTwdWfQScXv/2gmvJOti2rGD8jTt9GYVzXaxhblLivQR9sBUCNZu/w7j7aXUCA==", + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/@graphql-tools/load/-/load-8.0.3.tgz", + "integrity": "sha512-JE/MdTMcaIQ68U9zaizXG3QkR4Qligv131JVVmVJScxA1gv0gIc+HDixa5YK1rBXYLANU1sZMk87ZVuPaUdAoQ==", "dev": true, + "license": "MIT", "dependencies": { - "@graphql-tools/schema": "^10.0.3", - "@graphql-tools/utils": "^10.0.13", + "@graphql-tools/schema": "^10.0.7", + "@graphql-tools/utils": "^10.5.5", "p-limit": "3.1.0", "tslib": "^2.4.0" }, @@ -3856,12 +3656,13 @@ } }, "node_modules/@graphql-tools/merge": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-9.0.3.tgz", - "integrity": "sha512-FeKv9lKLMwqDu0pQjPpF59GY3HReUkWXKsMIuMuJQOKh9BETu7zPEFUELvcw8w+lwZkl4ileJsHXC9+AnsT2Lw==", + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-9.0.8.tgz", + "integrity": "sha512-RG9NEp4fi0MoFi0te4ahqTMYuavQnXlpEZxxMomdCa6CI5tfekcVm/rsLF5Zt8O4HY+esDt9+4dCL+aOKvG79w==", "dev": true, + "license": "MIT", "dependencies": { - "@graphql-tools/utils": "^10.0.13", + "@graphql-tools/utils": "^10.5.5", "tslib": "^2.4.0" }, "engines": { @@ -3876,6 +3677,7 @@ "resolved": "https://registry.npmjs.org/@graphql-tools/optimize/-/optimize-2.0.0.tgz", "integrity": "sha512-nhdT+CRGDZ+bk68ic+Jw1OZ99YCDIKYA5AlVAnBHJvMawSx9YQqQAIj4refNc1/LRieGiuWvhbG3jvPVYho0Dg==", "dev": true, + "license": "MIT", "dependencies": { "tslib": "^2.4.0" }, @@ -3887,15 +3689,15 @@ } }, "node_modules/@graphql-tools/prisma-loader": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/@graphql-tools/prisma-loader/-/prisma-loader-8.0.3.tgz", - "integrity": "sha512-oZhxnMr3Jw2WAW1h9FIhF27xWzIB7bXWM8olz4W12oII4NiZl7VRkFw9IT50zME2Bqi9LGh9pkmMWkjvbOpl+Q==", + "version": "8.0.9", + "resolved": "https://registry.npmjs.org/@graphql-tools/prisma-loader/-/prisma-loader-8.0.9.tgz", + "integrity": "sha512-Xav7rPzt43L+ij8iAuWw319E8/9DEnvp637jGknGDxuRaLLmnUpozczEczMyUUD0cQeEPdEBq5XHNJ/O3XijZQ==", "dev": true, + "license": "MIT", "dependencies": { - "@graphql-tools/url-loader": "^8.0.2", - "@graphql-tools/utils": "^10.0.13", + "@graphql-tools/url-loader": "^8.0.7", + "@graphql-tools/utils": "^10.5.5", "@types/js-yaml": "^4.0.0", - "@types/json-stable-stringify": "^1.0.32", "@whatwg-node/fetch": "^0.9.0", "chalk": "^4.1.0", "debug": "^4.3.1", @@ -3905,7 +3707,6 @@ "https-proxy-agent": "^7.0.0", "jose": "^5.0.0", "js-yaml": "^4.0.0", - "json-stable-stringify": "^1.0.1", "lodash": "^4.17.20", "scuid": "^1.1.0", "tslib": "^2.4.0", @@ -3918,58 +3719,15 @@ "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, - "node_modules/@graphql-tools/prisma-loader/node_modules/@whatwg-node/events": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@whatwg-node/events/-/events-0.1.1.tgz", - "integrity": "sha512-AyQEn5hIPV7Ze+xFoXVU3QTHXVbWPrzaOkxtENMPMuNL6VVHrp4hHfDt9nrQpjO7BgvuM95dMtkycX5M/DZR3w==", - "dev": true, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@graphql-tools/prisma-loader/node_modules/@whatwg-node/fetch": { - "version": "0.9.17", - "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.9.17.tgz", - "integrity": "sha512-TDYP3CpCrxwxpiNY0UMNf096H5Ihf67BK1iKGegQl5u9SlpEDYrvnV71gWBGJm+Xm31qOy8ATgma9rm8Pe7/5Q==", - "dev": true, - "dependencies": { - "@whatwg-node/node-fetch": "^0.5.7", - "urlpattern-polyfill": "^10.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@graphql-tools/prisma-loader/node_modules/@whatwg-node/node-fetch": { - "version": "0.5.10", - "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.5.10.tgz", - "integrity": "sha512-KIAHepie/T1PRkUfze4t+bPlyvpxlWiXTPtcGlbIZ0vWkBJMdRmCg4ZrJ2y4XaO1eTPo1HlWYUuj1WvoIpumqg==", - "dev": true, - "dependencies": { - "@kamilkisiela/fast-url-parser": "^1.1.4", - "@whatwg-node/events": "^0.1.0", - "busboy": "^1.6.0", - "fast-querystring": "^1.1.1", - "tslib": "^2.3.1" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@graphql-tools/prisma-loader/node_modules/urlpattern-polyfill": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz", - "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==", - "dev": true - }, "node_modules/@graphql-tools/relay-operation-optimizer": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-7.0.1.tgz", - "integrity": "sha512-y0ZrQ/iyqWZlsS/xrJfSir3TbVYJTYmMOu4TaSz6F4FRDTQ3ie43BlKkhf04rC28pnUOS4BO9pDcAo1D30l5+A==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-7.0.2.tgz", + "integrity": "sha512-sdoGBfe6+OXcPYUBMla3KKvf56bk0wCRY2HL4qK/CNP+7752Nx6s24aBqZ5vrnB3tleddAfnG4gvy0JuHfmA+A==", "dev": true, + "license": "MIT", "dependencies": { "@ardatan/relay-compiler": "12.0.0", - "@graphql-tools/utils": "^10.0.13", + "@graphql-tools/utils": "^10.5.5", "tslib": "^2.4.0" }, "engines": { @@ -3980,13 +3738,14 @@ } }, "node_modules/@graphql-tools/schema": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-10.0.3.tgz", - "integrity": "sha512-p28Oh9EcOna6i0yLaCFOnkcBDQECVf3SCexT6ktb86QNj9idnkhI+tCxnwZDh58Qvjd2nURdkbevvoZkvxzCog==", + "version": "10.0.7", + "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-10.0.7.tgz", + "integrity": "sha512-Cz1o+rf9cd3uMgG+zI9HlM5mPlnHQUlk/UQRZyUlPDfT+944taLaokjvj7AI6GcOFVf4f2D11XthQp+0GY31jQ==", "dev": true, + "license": "MIT", "dependencies": { - "@graphql-tools/merge": "^9.0.3", - "@graphql-tools/utils": "^10.0.13", + "@graphql-tools/merge": "^9.0.8", + "@graphql-tools/utils": "^10.5.5", "tslib": "^2.4.0", "value-or-promise": "^1.0.12" }, @@ -3998,24 +3757,25 @@ } }, "node_modules/@graphql-tools/url-loader": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@graphql-tools/url-loader/-/url-loader-8.0.2.tgz", - "integrity": "sha512-1dKp2K8UuFn7DFo1qX5c1cyazQv2h2ICwA9esHblEqCYrgf69Nk8N7SODmsfWg94OEaI74IqMoM12t7eIGwFzQ==", + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/@graphql-tools/url-loader/-/url-loader-8.0.7.tgz", + "integrity": "sha512-f1mq1wb1ivn8qFDVm8GWO5Co6Y/NZVXHgEG+3rjntr7aXjnw+DXyDQ+7QJRWJRDJcP0YWLJgfrBcWo1CqI4Qow==", "dev": true, + "license": "MIT", "dependencies": { "@ardatan/sync-fetch": "^0.0.1", - "@graphql-tools/delegate": "^10.0.4", - "@graphql-tools/executor-graphql-ws": "^1.1.2", - "@graphql-tools/executor-http": "^1.0.9", - "@graphql-tools/executor-legacy-ws": "^1.0.6", - "@graphql-tools/utils": "^10.0.13", - "@graphql-tools/wrap": "^10.0.2", + "@graphql-tools/delegate": "^10.0.26", + "@graphql-tools/executor-graphql-ws": "^1.3.1", + "@graphql-tools/executor-http": "^1.1.7", + "@graphql-tools/executor-legacy-ws": "^1.1.1", + "@graphql-tools/utils": "^10.5.5", + "@graphql-tools/wrap": "^10.0.10", "@types/ws": "^8.0.0", "@whatwg-node/fetch": "^0.9.0", "isomorphic-ws": "^5.0.0", "tslib": "^2.4.0", "value-or-promise": "^1.0.11", - "ws": "^8.12.0" + "ws": "^8.17.1" }, "engines": { "node": ">=16.0.0" @@ -4024,79 +3784,37 @@ "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, - "node_modules/@graphql-tools/url-loader/node_modules/@whatwg-node/events": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@whatwg-node/events/-/events-0.1.1.tgz", - "integrity": "sha512-AyQEn5hIPV7Ze+xFoXVU3QTHXVbWPrzaOkxtENMPMuNL6VVHrp4hHfDt9nrQpjO7BgvuM95dMtkycX5M/DZR3w==", + "node_modules/@graphql-tools/utils": { + "version": "10.5.5", + "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-10.5.5.tgz", + "integrity": "sha512-LF/UDWmMT0mnobL2UZETwYghV7HYBzNaGj0SAkCYOMy/C3+6sQdbcTksnoFaKR9XIVD78jNXEGfivbB8Zd+cwA==", "dev": true, + "license": "MIT", + "dependencies": { + "@graphql-typed-document-node/core": "^3.1.1", + "cross-inspect": "1.0.1", + "dset": "^3.1.2", + "tslib": "^2.4.0" + }, "engines": { "node": ">=16.0.0" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, - "node_modules/@graphql-tools/url-loader/node_modules/@whatwg-node/fetch": { - "version": "0.9.17", - "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.9.17.tgz", - "integrity": "sha512-TDYP3CpCrxwxpiNY0UMNf096H5Ihf67BK1iKGegQl5u9SlpEDYrvnV71gWBGJm+Xm31qOy8ATgma9rm8Pe7/5Q==", + "node_modules/@graphql-tools/wrap": { + "version": "10.0.10", + "resolved": "https://registry.npmjs.org/@graphql-tools/wrap/-/wrap-10.0.10.tgz", + "integrity": "sha512-3f1CUM+EpsALjt/HofzSWCLyfY65o9VpmqCTvIwVWGOnaP82cWbZE1Ytwb+t7yAZBKqCCc+1ginp+COIPD3ULw==", "dev": true, + "license": "MIT", "dependencies": { - "@whatwg-node/node-fetch": "^0.5.7", - "urlpattern-polyfill": "^10.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@graphql-tools/url-loader/node_modules/@whatwg-node/node-fetch": { - "version": "0.5.10", - "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.5.10.tgz", - "integrity": "sha512-KIAHepie/T1PRkUfze4t+bPlyvpxlWiXTPtcGlbIZ0vWkBJMdRmCg4ZrJ2y4XaO1eTPo1HlWYUuj1WvoIpumqg==", - "dev": true, - "dependencies": { - "@kamilkisiela/fast-url-parser": "^1.1.4", - "@whatwg-node/events": "^0.1.0", - "busboy": "^1.6.0", - "fast-querystring": "^1.1.1", - "tslib": "^2.3.1" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@graphql-tools/url-loader/node_modules/urlpattern-polyfill": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz", - "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==", - "dev": true - }, - "node_modules/@graphql-tools/utils": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-10.1.2.tgz", - "integrity": "sha512-fX13CYsDnX4yifIyNdiN0cVygz/muvkreWWem6BBw130+ODbRRgfiVveL0NizCEnKXkpvdeTy9Bxvo9LIKlhrw==", - "dev": true, - "dependencies": { - "@graphql-typed-document-node/core": "^3.1.1", - "cross-inspect": "1.0.0", - "dset": "^3.1.2", - "tslib": "^2.4.0" - }, - "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" - } - }, - "node_modules/@graphql-tools/wrap": { - "version": "10.0.5", - "resolved": "https://registry.npmjs.org/@graphql-tools/wrap/-/wrap-10.0.5.tgz", - "integrity": "sha512-Cbr5aYjr3HkwdPvetZp1cpDWTGdD1Owgsb3z/ClzhmrboiK86EnQDxDvOJiQkDCPWE9lNBwj8Y4HfxroY0D9DQ==", - "dev": true, - "dependencies": { - "@graphql-tools/delegate": "^10.0.4", - "@graphql-tools/schema": "^10.0.3", - "@graphql-tools/utils": "^10.1.1", - "tslib": "^2.4.0", - "value-or-promise": "^1.0.12" + "@graphql-tools/delegate": "^10.0.26", + "@graphql-tools/schema": "^10.0.7", + "@graphql-tools/utils": "^10.5.5", + "tslib": "^2.4.0", + "value-or-promise": "^1.0.12" }, "engines": { "node": ">=16.0.0" @@ -4109,14 +3827,16 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz", "integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==", + "license": "MIT", "peerDependencies": { "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "node_modules/@headlessui/react": { - "version": "1.7.18", - "resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.7.18.tgz", - "integrity": "sha512-4i5DOrzwN4qSgNsL4Si61VMkUcWbcSKueUV7sFhpHzQcSShdlHENE5+QBntMSRvHt8NyoFO2AGG8si9lq+w4zQ==", + "version": "1.7.19", + "resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.7.19.tgz", + "integrity": "sha512-Ll+8q3OlMJfJbAKM/+/Y2q6PPYbryqNTXDbryx7SXLIDamkF6iQFbriYHga0dY44PvDhvvBWCx1Xj4U5+G4hOw==", + "license": "MIT", "dependencies": { "@tanstack/react-virtual": "^3.0.0-beta.60", "client-only": "^0.0.1" @@ -4130,9 +3850,10 @@ } }, "node_modules/@heroicons/react": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.1.3.tgz", - "integrity": "sha512-fEcPfo4oN345SoqdlCDdSa4ivjaKbk0jTd+oubcgNxnNgAfzysfwWfQUr+51wigiWHQQRiZNd1Ao0M5Y3M2EGg==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.1.5.tgz", + "integrity": "sha512-FuzFN+BsHa+7OxbvAERtgBTNeZpUjgM/MIizfVkSCL2/edriN0Hx/DWRCR//aPYwO5QX/YlgLGXk+E3PcfZwjA==", + "license": "MIT", "peerDependencies": { "react": ">= 16" } @@ -4141,51 +3862,20 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/@hookform/error-message/-/error-message-2.0.1.tgz", "integrity": "sha512-U410sAr92xgxT1idlu9WWOVjndxLdgPUHEB8Schr27C9eh7/xUnITWpCMF93s+lGiG++D4JnbSnrb5A21AdSNg==", + "license": "MIT", "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0", "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 +3885,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.1", + "resolved": "https://registry.npmjs.org/@iconify-json/mdi/-/mdi-1.2.1.tgz", + "integrity": "sha512-dSkQU78gsZV6Yxnq78+LuX7jzeFC/5NAmz7O3rh558GimGFcwMVY/OtqRowIzjqJBmMmWZft7wkFV4TrwRXjlg==", + "license": "Apache-2.0", "dependencies": { "@iconify/types": "*" } @@ -4205,12 +3896,14 @@ "node_modules/@iconify/types": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", - "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==" + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", + "license": "MIT" }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", @@ -4224,9 +3917,10 @@ } }, "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "license": "MIT", "engines": { "node": ">=12" }, @@ -4238,6 +3932,7 @@ "version": "6.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "license": "MIT", "engines": { "node": ">=12" }, @@ -4248,12 +3943,14 @@ "node_modules/@isaacs/cliui/node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" }, "node_modules/@isaacs/cliui/node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", @@ -4270,6 +3967,7 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" }, @@ -4284,6 +3982,7 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", @@ -4301,6 +4000,7 @@ "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -4310,6 +4010,7 @@ "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", "dev": true, + "license": "MIT", "dependencies": { "@sinclair/typebox": "^0.27.8" }, @@ -4318,43 +4019,48 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "license": "MIT", "dependencies": { - "@jridgewell/set-array": "^1.0.1", + "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "license": "MIT", "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + "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", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -4364,17 +4070,20 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/@kamilkisiela/fast-url-parser/-/fast-url-parser-1.1.4.tgz", "integrity": "sha512-gbkePEBupNydxCelHCESvFSFM8XPh1Zs/OAVRW/rKpEqPAl5PbOM90Si8mv9bvnR53uPD2s/FiRxdvSejpRJew==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@lezer/common": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.1.tgz", - "integrity": "sha512-yemX0ZD2xS/73llMZIK6KplkjIjf2EvAHcinDi/TfJ9hS25G0388+ClHt6/3but0oOxinTcQHJLDXh6w1crzFQ==" + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.3.tgz", + "integrity": "sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==", + "license": "MIT" }, "node_modules/@lezer/css": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.1.7.tgz", - "integrity": "sha512-7BlFFAKNn/b39jJLrhdLSX5A2k56GIJvyLqdmm7UU+7XvequY084iuKDMAEhAmAzHnwDE8FK4OQtsIUssW91tg==", + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.1.9.tgz", + "integrity": "sha512-TYwgljcDv+YrV0MZFFvYFQHCfGgbPMR6nuqLabBdmZoFH3EP1gvw8t0vae326Ne3PszQkbXfVBjCnf3ZVCr0bA==", + "license": "MIT", "dependencies": { "@lezer/common": "^1.2.0", "@lezer/highlight": "^1.0.0", @@ -4382,17 +4091,19 @@ } }, "node_modules/@lezer/highlight": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.0.tgz", - "integrity": "sha512-WrS5Mw51sGrpqjlh3d4/fOwpEV2Hd3YOkp9DBt4k8XZQcoTHZFB7sx030A6OcahF4J1nDQAa3jXlTVVYH50IFA==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.1.tgz", + "integrity": "sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==", + "license": "MIT", "dependencies": { "@lezer/common": "^1.0.0" } }, "node_modules/@lezer/html": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.8.tgz", - "integrity": "sha512-EXseJ3pUzWxE6XQBQdqWHZqqlGQRSuNMBcLb6mZWS2J2v+QZhOObD+3ZIKIcm59ntTzyor4LqFTb72iJc3k23Q==", + "version": "1.3.10", + "resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.10.tgz", + "integrity": "sha512-dqpT8nISx/p9Do3AchvYGV3qYc4/rKr3IBZxlHmpIKam56P47RSHkSF5f13Vu9hebS1jM0HmtJIwLbWz1VIY6w==", + "license": "MIT", "dependencies": { "@lezer/common": "^1.2.0", "@lezer/highlight": "^1.0.0", @@ -4400,9 +4111,10 @@ } }, "node_modules/@lezer/javascript": { - "version": "1.4.13", - "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.4.13.tgz", - "integrity": "sha512-5IBr8LIO3xJdJH1e9aj/ZNLE4LSbdsx25wFmGRAZsj2zSmwAYjx26JyU/BYOCpRQlu1jcv1z3vy4NB9+UkfRow==", + "version": "1.4.19", + "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.4.19.tgz", + "integrity": "sha512-j44kbR1QL26l6dMunZ1uhKBFteVGLVCBGNUD2sUaMnic+rbTviVuoK0CD1l9FTW31EueWvFFswCKMH7Z+M3JRA==", + "license": "MIT", "dependencies": { "@lezer/common": "^1.2.0", "@lezer/highlight": "^1.1.3", @@ -4410,30 +4122,33 @@ } }, "node_modules/@lezer/lr": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.0.tgz", - "integrity": "sha512-Wst46p51km8gH0ZUmeNrtpRYmdlRHUpN1DQd3GFAyKANi8WVz8c2jHYTf1CVScFaCjQw1iO3ZZdqGDxQPRErTg==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.2.tgz", + "integrity": "sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==", + "license": "MIT", "dependencies": { "@lezer/common": "^1.0.0" } }, "node_modules/@lezer/markdown": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@lezer/markdown/-/markdown-1.2.0.tgz", - "integrity": "sha512-d7MwsfAukZJo1GpPrcPGa3MxaFFOqNp0gbqF+3F7pTeNDOgeJN1muXzx1XXDPt+Ac+/voCzsH7qXqnn+xReG/g==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@lezer/markdown/-/markdown-1.3.1.tgz", + "integrity": "sha512-DGlzU/i8DC8k0uz1F+jeePrkATl0jWakauTzftMQOcbaMkHbNSRki/4E2tOzJWsVpoKYhe7iTJ03aepdwVUXUA==", + "license": "MIT", "dependencies": { "@lezer/common": "^1.0.0", "@lezer/highlight": "^1.0.0" } }, "node_modules/@motionone/animation": { - "version": "10.17.0", - "resolved": "https://registry.npmjs.org/@motionone/animation/-/animation-10.17.0.tgz", - "integrity": "sha512-ANfIN9+iq1kGgsZxs+Nz96uiNcPLGTXwfNo2Xz/fcJXniPYpaz/Uyrfa+7I5BPLxCP82sh7quVDudf1GABqHbg==", + "version": "10.18.0", + "resolved": "https://registry.npmjs.org/@motionone/animation/-/animation-10.18.0.tgz", + "integrity": "sha512-9z2p5GFGCm0gBsZbi8rVMOAJCtw1WqBTIPw3ozk06gDvZInBPIsQcHgYogEJ4yuHJ+akuW8g1SEIOpTOvYs8hw==", + "license": "MIT", "dependencies": { - "@motionone/easing": "^10.17.0", - "@motionone/types": "^10.17.0", - "@motionone/utils": "^10.17.0", + "@motionone/easing": "^10.18.0", + "@motionone/types": "^10.17.1", + "@motionone/utils": "^10.18.0", "tslib": "^2.3.1" } }, @@ -4441,6 +4156,7 @@ "version": "10.12.0", "resolved": "https://registry.npmjs.org/@motionone/dom/-/dom-10.12.0.tgz", "integrity": "sha512-UdPTtLMAktHiqV0atOczNYyDd/d8Cf5fFsd1tua03PqTwwCe/6lwhLSQ8a7TbnQ5SN0gm44N1slBfj+ORIhrqw==", + "license": "MIT", "dependencies": { "@motionone/animation": "^10.12.0", "@motionone/generators": "^10.12.0", @@ -4451,35 +4167,39 @@ } }, "node_modules/@motionone/easing": { - "version": "10.17.0", - "resolved": "https://registry.npmjs.org/@motionone/easing/-/easing-10.17.0.tgz", - "integrity": "sha512-Bxe2wSuLu/qxqW4rBFS5m9tMLOw+QBh8v5A7Z5k4Ul4sTj5jAOfZG5R0bn5ywmk+Fs92Ij1feZ5pmC4TeXA8Tg==", + "version": "10.18.0", + "resolved": "https://registry.npmjs.org/@motionone/easing/-/easing-10.18.0.tgz", + "integrity": "sha512-VcjByo7XpdLS4o9T8t99JtgxkdMcNWD3yHU/n6CLEz3bkmKDRZyYQ/wmSf6daum8ZXqfUAgFeCZSpJZIMxaCzg==", + "license": "MIT", "dependencies": { - "@motionone/utils": "^10.17.0", + "@motionone/utils": "^10.18.0", "tslib": "^2.3.1" } }, "node_modules/@motionone/generators": { - "version": "10.17.0", - "resolved": "https://registry.npmjs.org/@motionone/generators/-/generators-10.17.0.tgz", - "integrity": "sha512-T6Uo5bDHrZWhIfxG/2Aut7qyWQyJIWehk6OB4qNvr/jwA/SRmixwbd7SOrxZi1z5rH3LIeFFBKK1xHnSbGPZSQ==", + "version": "10.18.0", + "resolved": "https://registry.npmjs.org/@motionone/generators/-/generators-10.18.0.tgz", + "integrity": "sha512-+qfkC2DtkDj4tHPu+AFKVfR/C30O1vYdvsGYaR13W/1cczPrrcjdvYCj0VLFuRMN+lP1xvpNZHCRNM4fBzn1jg==", + "license": "MIT", "dependencies": { - "@motionone/types": "^10.17.0", - "@motionone/utils": "^10.17.0", + "@motionone/types": "^10.17.1", + "@motionone/utils": "^10.18.0", "tslib": "^2.3.1" } }, "node_modules/@motionone/types": { - "version": "10.17.0", - "resolved": "https://registry.npmjs.org/@motionone/types/-/types-10.17.0.tgz", - "integrity": "sha512-EgeeqOZVdRUTEHq95Z3t8Rsirc7chN5xFAPMYFobx8TPubkEfRSm5xihmMUkbaR2ErKJTUw3347QDPTHIW12IA==" + "version": "10.17.1", + "resolved": "https://registry.npmjs.org/@motionone/types/-/types-10.17.1.tgz", + "integrity": "sha512-KaC4kgiODDz8hswCrS0btrVrzyU2CSQKO7Ps90ibBVSQmjkrt2teqta6/sOG59v7+dPnKMAg13jyqtMKV2yJ7A==", + "license": "MIT" }, "node_modules/@motionone/utils": { - "version": "10.17.0", - "resolved": "https://registry.npmjs.org/@motionone/utils/-/utils-10.17.0.tgz", - "integrity": "sha512-bGwrki4896apMWIj9yp5rAS2m0xyhxblg6gTB/leWDPt+pb410W8lYWsxyurX+DH+gO1zsQsfx2su/c1/LtTpg==", + "version": "10.18.0", + "resolved": "https://registry.npmjs.org/@motionone/utils/-/utils-10.18.0.tgz", + "integrity": "sha512-3XVF7sgyTSI2KWvTf6uLlBJ5iAgRgmvp3bpuOiQJvInd4nZ19ET8lX5unn30SlmRH7hXbBbH+Gxd0m0klJ3Xtw==", + "license": "MIT", "dependencies": { - "@motionone/types": "^10.17.0", + "@motionone/types": "^10.17.1", "hey-listen": "^1.0.8", "tslib": "^2.3.1" } @@ -4488,6 +4208,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/@n1ru4l/push-pull-async-iterable-iterator/-/push-pull-async-iterable-iterator-3.2.0.tgz", "integrity": "sha512-3fkKj25kEjsfObL6IlKPAlHYPq/oYwUkkQ03zsTTiDjD7vg/RxjdiLeCydqtxHZP0JgsXL3D/X5oAkMGzuUp/Q==", + "license": "MIT", "engines": { "node": ">=12" } @@ -4496,6 +4217,7 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -4508,6 +4230,7 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "license": "MIT", "engines": { "node": ">= 8" } @@ -4516,6 +4239,7 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -4524,119 +4248,24 @@ "node": ">= 8" } }, - "node_modules/@parcel/watcher": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.4.0.tgz", - "integrity": "sha512-XJLGVL0DEclX5pcWa2N9SX1jCGTDd8l972biNooLFtjneuGqodupPQh6XseXIBBeVIMaaJ7bTcs3qGvXwsp4vg==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "peer": true, - "dependencies": { - "detect-libc": "^1.0.3", - "is-glob": "^4.0.3", - "micromatch": "^4.0.5", - "node-addon-api": "^7.0.0" - }, - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "@parcel/watcher-android-arm64": "2.4.0", - "@parcel/watcher-darwin-arm64": "2.4.0", - "@parcel/watcher-darwin-x64": "2.4.0", - "@parcel/watcher-freebsd-x64": "2.4.0", - "@parcel/watcher-linux-arm-glibc": "2.4.0", - "@parcel/watcher-linux-arm64-glibc": "2.4.0", - "@parcel/watcher-linux-arm64-musl": "2.4.0", - "@parcel/watcher-linux-x64-glibc": "2.4.0", - "@parcel/watcher-linux-x64-musl": "2.4.0", - "@parcel/watcher-win32-arm64": "2.4.0", - "@parcel/watcher-win32-ia32": "2.4.0", - "@parcel/watcher-win32-x64": "2.4.0" - } - }, - "node_modules/@parcel/watcher-darwin-arm64": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.4.0.tgz", - "integrity": "sha512-T/At5pansFuQ8VJLRx0C6C87cgfqIYhW2N/kBfLCUvDhCah0EnLLwaD/6MW3ux+rpgkpQAnMELOCTKlbwncwiA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@peculiar/asn1-schema": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.3.8.tgz", - "integrity": "sha512-ULB1XqHKx1WBU/tTFIA+uARuRoBVZ4pNdOA878RDrRbBfBGcSzi5HBkdScC6ZbHn8z7L8gmKCgPC1LHRrP46tA==", - "dev": true, - "dependencies": { - "asn1js": "^3.0.5", - "pvtsutils": "^1.3.5", - "tslib": "^2.6.2" - } - }, - "node_modules/@peculiar/json-schema": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/@peculiar/json-schema/-/json-schema-1.1.12.tgz", - "integrity": "sha512-coUfuoMeIB7B8/NMekxaDzLhaYmp0HZNPEjYRm9goRou8UZIC3z21s0sL9AWoCw4EG876QyO3kYrc61WNF9B/w==", - "dev": true, - "dependencies": { - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@peculiar/webcrypto": { - "version": "1.4.6", - "resolved": "https://registry.npmjs.org/@peculiar/webcrypto/-/webcrypto-1.4.6.tgz", - "integrity": "sha512-YBcMfqNSwn3SujUJvAaySy5tlYbYm6tVt9SKoXu8BaTdKGROiJDgPR3TXpZdAKUfklzm3lRapJEAltiMQtBgZg==", - "dev": true, - "dependencies": { - "@peculiar/asn1-schema": "^2.3.8", - "@peculiar/json-schema": "^1.1.12", - "pvtsutils": "^1.3.5", - "tslib": "^2.6.2", - "webcrypto-core": "^1.7.9" - }, - "engines": { - "node": ">=10.12.0" - } - }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", "optional": true, "engines": { "node": ">=14" } }, "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.48.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.48.1.tgz", + "integrity": "sha512-s9RtWoxkOLmRJdw3oFvhFbs9OJS0BzrLUc8Hf6l2UdCNd1rqeEyD4BhCJkvzeEoD1FsK4mirsWwGerhVmYKtZg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright": "1.47.0" + "playwright": "1.48.1" }, "bin": { "playwright": "cli.js" @@ -4649,32 +4278,45 @@ "version": "2.11.8", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", "funding": { "type": "opencollective", "url": "https://opencollective.com/popperjs" } }, + "node_modules/@radix-ui/number": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.0.tgz", + "integrity": "sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ==", + "license": "MIT" + }, "node_modules/@radix-ui/primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.1.tgz", - "integrity": "sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==", - "dependencies": { - "@babel/runtime": "^7.13.10" - } + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.0.tgz", + "integrity": "sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==", + "license": "MIT" }, - "node_modules/@radix-ui/react-arrow": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.0.3.tgz", - "integrity": "sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==", + "node_modules/@radix-ui/react-accordion": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-accordion/-/react-accordion-1.2.1.tgz", + "integrity": "sha512-bg/l7l5QzUjgsh8kjwDFommzAshnUsuVMV5NM56QVCm+7ZckYdd9P/ExR8xG/Oup0OajVxNLaHJ1tb8mXk+nzQ==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-primitive": "1.0.3" + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-collapsible": "1.1.1", + "@radix-ui/react-collection": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-controllable-state": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { @@ -4685,22 +4327,19 @@ } } }, - "node_modules/@radix-ui/react-collection": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.0.3.tgz", - "integrity": "sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==", + "node_modules/@radix-ui/react-arrow": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.0.tgz", + "integrity": "sha512-FmlW1rCg7hBpEBwFbjHwCW6AmWLQM6g/v0Sn8XbP9NvmSZ2San1FpQeyPtufzOMSIx7Y4dzjlHoifhp+7NkZhw==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-slot": "1.0.2" + "@radix-ui/react-primitive": "2.0.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { @@ -4711,86 +4350,151 @@ } } }, - "node_modules/@radix-ui/react-compose-refs": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz", - "integrity": "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==", + "node_modules/@radix-ui/react-collapsible": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.1.tgz", + "integrity": "sha512-1///SnrfQHJEofLokyczERxQbWfCGQlQ2XsCZMucVs6it+lq9iw4vXy+uDn1edlb58cOZOWSldnfPAYcT4O/Yg==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10" + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-presence": "1.1.1", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-controllable-state": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { "optional": true + }, + "@types/react-dom": { + "optional": true } } }, - "node_modules/@radix-ui/react-context": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.0.1.tgz", - "integrity": "sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==", + "node_modules/@radix-ui/react-collection": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.0.tgz", + "integrity": "sha512-GZsZslMJEyo1VKm5L1ZJY8tGDxZNPAoUeQUIbKeJfoi7Q4kmig5AsgLMYYuyYbfjd8fBmFORAIwYAkXMnXZgZw==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10" + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-slot": "1.1.0" }, "peerDependencies": { "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { "optional": true + }, + "@types/react-dom": { + "optional": true } } }, - "node_modules/@radix-ui/react-dialog": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.0.5.tgz", - "integrity": "sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-dismissable-layer": "1.0.5", - "@radix-ui/react-focus-guards": "1.0.1", - "@radix-ui/react-focus-scope": "1.0.4", - "@radix-ui/react-id": "1.0.1", - "@radix-ui/react-portal": "1.0.4", - "@radix-ui/react-presence": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-slot": "1.0.2", - "@radix-ui/react-use-controllable-state": "1.0.1", - "aria-hidden": "^1.1.1", - "react-remove-scroll": "2.5.5" - }, + "node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-context": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.0.tgz", + "integrity": "sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==", + "license": "MIT", "peerDependencies": { "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { "optional": true - }, - "@types/react-dom": { - "optional": true } } }, - "node_modules/@radix-ui/react-direction": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.0.1.tgz", - "integrity": "sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==", - "dependencies": { - "@babel/runtime": "^7.13.10" + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz", + "integrity": "sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz", + "integrity": "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==", + "license": "MIT", "peerDependencies": { "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.2.tgz", + "integrity": "sha512-Yj4dZtqa2o+kG61fzB0H2qUvmwBA2oyQroGLyNtBj1beo1khoQ3q1a2AO8rrQYjd8256CO9+N8L9tvsS+bnIyA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.1", + "@radix-ui/react-focus-guards": "1.1.1", + "@radix-ui/react-focus-scope": "1.1.0", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-portal": "1.1.2", + "@radix-ui/react-presence": "1.1.1", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-slot": "1.1.0", + "@radix-ui/react-use-controllable-state": "1.1.0", + "aria-hidden": "^1.1.1", + "react-remove-scroll": "2.6.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-direction": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.0.tgz", + "integrity": "sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { @@ -4799,22 +4503,22 @@ } }, "node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.5.tgz", - "integrity": "sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.1.tgz", + "integrity": "sha512-QSxg29lfr/xcev6kSz7MAlmDnzbP1eI/Dwn3Tp1ip0KT5CUELsxkekFEMVBEoykI3oV39hKT4TKZzBNMbcTZYQ==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-callback-ref": "1.0.1", - "@radix-ui/react-use-escape-keydown": "1.0.3" + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-escape-keydown": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { @@ -4826,24 +4530,24 @@ } }, "node_modules/@radix-ui/react-dropdown-menu": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.0.6.tgz", - "integrity": "sha512-i6TuFOoWmLWq+M/eCLGd/bQ2HfAX1RJgvrBQ6AQLmzfvsLdefxbWu8G9zczcPFfcSPehz9GcpF6K9QYreFV8hA==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.2.tgz", + "integrity": "sha512-GVZMR+eqK8/Kes0a36Qrv+i20bAPXSn8rCBTHx30w+3ECnR5o3xixAlqcVaYvLeyKUsm0aqyhWfmUcqufM8nYA==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-id": "1.0.1", - "@radix-ui/react-menu": "2.0.6", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-controllable-state": "1.0.1" + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-menu": "2.1.2", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-controllable-state": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { @@ -4855,15 +4559,13 @@ } }, "node_modules/@radix-ui/react-focus-guards": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.1.tgz", - "integrity": "sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==", - "dependencies": { - "@babel/runtime": "^7.13.10" - }, + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.1.tgz", + "integrity": "sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg==", + "license": "MIT", "peerDependencies": { "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { @@ -4872,20 +4574,20 @@ } }, "node_modules/@radix-ui/react-focus-scope": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.4.tgz", - "integrity": "sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.0.tgz", + "integrity": "sha512-200UD8zylvEyL8Bx+z76RJnASR2gRMuxlgFCPAe/Q/679a/r0eK3MBVYMb7vZODZcffZBdob1EGnky78xmVvcA==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-callback-ref": "1.0.1" + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-callback-ref": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { @@ -4897,16 +4599,16 @@ } }, "node_modules/@radix-ui/react-id": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.1.tgz", - "integrity": "sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.0.tgz", + "integrity": "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-layout-effect": "1.0.1" + "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { @@ -4915,18 +4617,18 @@ } }, "node_modules/@radix-ui/react-label": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.0.2.tgz", - "integrity": "sha512-N5ehvlM7qoTLx7nWPodsPYPgMzA5WM8zZChQg8nyFJKnDO5WHdba1vv5/H6IO5LtJMfD2Q3wh1qHFGNtK0w3bQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.0.tgz", + "integrity": "sha512-peLblDlFw/ngk3UWq0VnYaOLy6agTZZ+MUO/WhVfm14vJGML+xH4FAl2XQGLqdefjNb7ApRg6Yn7U42ZhmYXdw==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-primitive": "1.0.3" + "@radix-ui/react-primitive": "2.0.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { @@ -4938,35 +4640,35 @@ } }, "node_modules/@radix-ui/react-menu": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.0.6.tgz", - "integrity": "sha512-BVkFLS+bUC8HcImkRKPSiVumA1VPOOEC5WBMiT+QAVsPzW1FJzI9KnqgGxVDPBcql5xXrHkD3JOVoXWEXD8SYA==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.2.tgz", + "integrity": "sha512-lZ0R4qR2Al6fZ4yCCZzu/ReTFrylHFxIqy7OezIpWF4bL0o9biKo0pFIvkaew3TyZ9Fy5gYVrR5zCGZBVbO1zg==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-collection": "1.0.3", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-direction": "1.0.1", - "@radix-ui/react-dismissable-layer": "1.0.5", - "@radix-ui/react-focus-guards": "1.0.1", - "@radix-ui/react-focus-scope": "1.0.4", - "@radix-ui/react-id": "1.0.1", - "@radix-ui/react-popper": "1.1.3", - "@radix-ui/react-portal": "1.0.4", - "@radix-ui/react-presence": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-roving-focus": "1.0.4", - "@radix-ui/react-slot": "1.0.2", - "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-collection": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-dismissable-layer": "1.1.1", + "@radix-ui/react-focus-guards": "1.1.1", + "@radix-ui/react-focus-scope": "1.1.0", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-popper": "1.2.0", + "@radix-ui/react-portal": "1.1.2", + "@radix-ui/react-presence": "1.1.1", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-roving-focus": "1.1.0", + "@radix-ui/react-slot": "1.1.0", + "@radix-ui/react-use-callback-ref": "1.1.0", "aria-hidden": "^1.1.1", - "react-remove-scroll": "2.5.5" + "react-remove-scroll": "2.6.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { @@ -4978,32 +4680,32 @@ } }, "node_modules/@radix-ui/react-popover": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.0.7.tgz", - "integrity": "sha512-shtvVnlsxT6faMnK/a7n0wptwBD23xc1Z5mdrtKLwVEfsEMXodS0r5s0/g5P0hX//EKYZS2sxUjqfzlg52ZSnQ==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.2.tgz", + "integrity": "sha512-u2HRUyWW+lOiA2g0Le0tMmT55FGOEWHwPFt1EPfbLly7uXQExFo5duNKqG2DzmFXIdqOeNd+TpE8baHWJCyP9w==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-dismissable-layer": "1.0.5", - "@radix-ui/react-focus-guards": "1.0.1", - "@radix-ui/react-focus-scope": "1.0.4", - "@radix-ui/react-id": "1.0.1", - "@radix-ui/react-popper": "1.1.3", - "@radix-ui/react-portal": "1.0.4", - "@radix-ui/react-presence": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-slot": "1.0.2", - "@radix-ui/react-use-controllable-state": "1.0.1", + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.1", + "@radix-ui/react-focus-guards": "1.1.1", + "@radix-ui/react-focus-scope": "1.1.0", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-popper": "1.2.0", + "@radix-ui/react-portal": "1.1.2", + "@radix-ui/react-presence": "1.1.1", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-slot": "1.1.0", + "@radix-ui/react-use-controllable-state": "1.1.0", "aria-hidden": "^1.1.1", - "react-remove-scroll": "2.5.5" + "react-remove-scroll": "2.6.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { @@ -5015,27 +4717,27 @@ } }, "node_modules/@radix-ui/react-popper": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.1.3.tgz", - "integrity": "sha512-cKpopj/5RHZWjrbF2846jBNacjQVwkP068DfmgrNJXpvVWrOvlAmE9xSiy5OqeE+Gi8D9fP+oDhUnPqNMY8/5w==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.0.tgz", + "integrity": "sha512-ZnRMshKF43aBxVWPWvbj21+7TQCvhuULWJ4gNIKYpRlQt5xGRhLx66tMp8pya2UkGHTSlhpXwmjqltDYHhw7Vg==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10", "@floating-ui/react-dom": "^2.0.0", - "@radix-ui/react-arrow": "1.0.3", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-callback-ref": "1.0.1", - "@radix-ui/react-use-layout-effect": "1.0.1", - "@radix-ui/react-use-rect": "1.0.1", - "@radix-ui/react-use-size": "1.0.1", - "@radix-ui/rect": "1.0.1" + "@radix-ui/react-arrow": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0", + "@radix-ui/react-use-rect": "1.1.0", + "@radix-ui/react-use-size": "1.1.0", + "@radix-ui/rect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { @@ -5046,19 +4748,35 @@ } } }, + "node_modules/@radix-ui/react-popper/node_modules/@radix-ui/react-context": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.0.tgz", + "integrity": "sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-portal": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.4.tgz", - "integrity": "sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.2.tgz", + "integrity": "sha512-WeDYLGPxJb/5EGBoedyJbT0MpoULmwnIPMJMSldkuiMsBAv7N1cRdsTWZWht9vpPOiN3qyiGAtbK2is47/uMFg==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-primitive": "1.0.3" + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { @@ -5070,19 +4788,19 @@ } }, "node_modules/@radix-ui/react-presence": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.0.1.tgz", - "integrity": "sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.1.tgz", + "integrity": "sha512-IeFXVi4YS1K0wVZzXNrbaaUvIJ3qdY+/Ih4eHFhWA9SwGR9UDX7Ck8abvL57C4cv3wwMvUE0OG69Qc3NCcTe/A==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-use-layout-effect": "1.0.1" + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { @@ -5094,18 +4812,18 @@ } }, "node_modules/@radix-ui/react-primitive": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz", - "integrity": "sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.0.tgz", + "integrity": "sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-slot": "1.0.2" + "@radix-ui/react-slot": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { @@ -5117,19 +4835,19 @@ } }, "node_modules/@radix-ui/react-progress": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.0.3.tgz", - "integrity": "sha512-5G6Om/tYSxjSeEdrb1VfKkfZfn/1IlPWd731h2RfPuSbIfNUgfqAwbKfJCg/PP6nuUCTrYzalwHSpSinoWoCag==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.0.tgz", + "integrity": "sha512-aSzvnYpP725CROcxAOEBVZZSIQVQdHgBr2QQFKySsaD14u8dNT0batuXI+AAGDdAHfXH8rbnHmjYFqVJ21KkRg==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-primitive": "1.0.3" + "@radix-ui/react-context": "1.1.0", + "@radix-ui/react-primitive": "2.0.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { @@ -5140,27 +4858,42 @@ } } }, + "node_modules/@radix-ui/react-progress/node_modules/@radix-ui/react-context": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.0.tgz", + "integrity": "sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-roving-focus": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.0.4.tgz", - "integrity": "sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.0.tgz", + "integrity": "sha512-EA6AMGeq9AEeQDeSH0aZgG198qkfHSbvWTf1HvoDmOB5bBG/qTxjYMWUKMnYiV6J/iP/J8MEFSuB2zRU2n7ODA==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-collection": "1.0.3", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-direction": "1.0.1", - "@radix-ui/react-id": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-callback-ref": "1.0.1", - "@radix-ui/react-use-controllable-state": "1.0.1" + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-collection": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.0", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-controllable-state": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { @@ -5171,17 +4904,14 @@ } } }, - "node_modules/@radix-ui/react-slot": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz", - "integrity": "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.1" - }, + "node_modules/@radix-ui/react-roving-focus/node_modules/@radix-ui/react-context": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.0.tgz", + "integrity": "sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==", + "license": "MIT", "peerDependencies": { "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { @@ -5189,30 +4919,27 @@ } } }, - "node_modules/@radix-ui/react-tooltip": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.0.7.tgz", - "integrity": "sha512-lPh5iKNFVQ/jav/j6ZrWq3blfDJ0OH9R6FlNUHPMqdLuQ9vwDgFsRxvl8b7Asuy5c8xmoojHUxKHQSOAvMHxyw==", + "node_modules/@radix-ui/react-scroll-area": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.0.tgz", + "integrity": "sha512-q2jMBdsJ9zB7QG6ngQNzNwlvxLQqONyL58QbEGwuyRZZb/ARQwk3uQVbCF7GvQVOtV6EU/pDxAw3zRzJZI3rpQ==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-dismissable-layer": "1.0.5", - "@radix-ui/react-id": "1.0.1", - "@radix-ui/react-popper": "1.1.3", - "@radix-ui/react-portal": "1.0.4", - "@radix-ui/react-presence": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-slot": "1.0.2", - "@radix-ui/react-use-controllable-state": "1.0.1", - "@radix-ui/react-visually-hidden": "1.0.3" + "@radix-ui/number": "1.1.0", + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-presence": "1.1.1", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { @@ -5223,16 +4950,66 @@ } } }, - "node_modules/@radix-ui/react-use-callback-ref": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz", - "integrity": "sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==", + "node_modules/@radix-ui/react-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz", + "integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10" + "@radix-ui/react-compose-refs": "1.1.0" }, "peerDependencies": { "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tooltip": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.1.3.tgz", + "integrity": "sha512-Z4w1FIS0BqVFI2c1jZvb/uDVJijJjJ2ZMuPV81oVgTZ7g3BZxobplnMVvXtFWgtozdvYJ+MFWtwkM5S2HnAong==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.1", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-popper": "1.2.0", + "@radix-ui/react-portal": "1.1.2", + "@radix-ui/react-presence": "1.1.1", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-slot": "1.1.0", + "@radix-ui/react-use-controllable-state": "1.1.0", + "@radix-ui/react-visually-hidden": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz", + "integrity": "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { @@ -5241,16 +5018,16 @@ } }, "node_modules/@radix-ui/react-use-controllable-state": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.1.tgz", - "integrity": "sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.0.tgz", + "integrity": "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-callback-ref": "1.0.1" + "@radix-ui/react-use-callback-ref": "1.1.0" }, "peerDependencies": { "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { @@ -5259,16 +5036,16 @@ } }, "node_modules/@radix-ui/react-use-escape-keydown": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.3.tgz", - "integrity": "sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.0.tgz", + "integrity": "sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-callback-ref": "1.0.1" + "@radix-ui/react-use-callback-ref": "1.1.0" }, "peerDependencies": { "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { @@ -5277,15 +5054,13 @@ } }, "node_modules/@radix-ui/react-use-layout-effect": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz", - "integrity": "sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==", - "dependencies": { - "@babel/runtime": "^7.13.10" - }, + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.0.tgz", + "integrity": "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==", + "license": "MIT", "peerDependencies": { "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { @@ -5294,16 +5069,16 @@ } }, "node_modules/@radix-ui/react-use-rect": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.0.1.tgz", - "integrity": "sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.0.tgz", + "integrity": "sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/rect": "1.0.1" + "@radix-ui/rect": "1.1.0" }, "peerDependencies": { "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { @@ -5312,16 +5087,16 @@ } }, "node_modules/@radix-ui/react-use-size": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.0.1.tgz", - "integrity": "sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.0.tgz", + "integrity": "sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-layout-effect": "1.0.1" + "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { @@ -5330,18 +5105,18 @@ } }, "node_modules/@radix-ui/react-visually-hidden": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.0.3.tgz", - "integrity": "sha512-D4w41yN5YRKtu464TLnByKzMDG/JlMPHtfZgQAu9v6mNakUqGUI9vUrfQKz8NK41VMm/xbZbh76NUTVtIYqOMA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.1.0.tgz", + "integrity": "sha512-N8MDZqtgCgG5S3aV60INAB475osJousYpZ4cTJ2cFbMpdHS5Y6loLTH8LPtkj2QN0x93J30HT/M3qJXM0+lyeQ==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-primitive": "1.0.3" + "@radix-ui/react-primitive": "2.0.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { @@ -5353,49 +5128,44 @@ } }, "node_modules/@radix-ui/rect": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.0.1.tgz", - "integrity": "sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==", - "dependencies": { - "@babel/runtime": "^7.13.10" - } + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.0.tgz", + "integrity": "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==", + "license": "MIT" }, "node_modules/@redocly/ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/@redocly/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-9GWx27t7xWhDIR02PA18nzBdLcKQRgc46xNQvjFkrYk4UOmvKhJ/dawwiX0cCOeetN5LcaaiqQbVOWYK62SGHw==", + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/@redocly/ajv/-/ajv-8.11.2.tgz", + "integrity": "sha512-io1JpnwtIcvojV7QKDUSIuMN/ikdOUd1ReEnUnMKGfDVridQZ31J0MmIuqwuRjWDZfmvr+Q0MqCcfHM2gTivOg==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "uri-js-replace": "^1.0.1" }, "funding": { "type": "github", "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@redocly/ajv/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, "node_modules/@redocly/config": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/@redocly/config/-/config-0.6.3.tgz", - "integrity": "sha512-hGWJgCsXRw0Ow4rplqRlUQifZvoSwZipkYnt11e3SeH1Eb23VUIDBcRuaQOUqy1wn0eevXkU2GzzQ8fbKdQ7Mg==", - "dev": true + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/@redocly/config/-/config-0.12.1.tgz", + "integrity": "sha512-RW3rSirfsPdr0uvATijRDU3f55SuZV3m7/ppdTDvGw4IB0cmeZRkFmqTrchxMqWP50Gfg1tpHnjdxUCNo0E2qg==", + "dev": true, + "license": "MIT" }, "node_modules/@redocly/openapi-core": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.18.0.tgz", - "integrity": "sha512-kcbt7w23pcVYGLnJkh2LZpXF1OX5RDM4DLOtwPug2HvRE8ow/YfY8ZEM1YCFlA41D8rBPBVP918cYeIx4BVUbw==", + "version": "1.25.7", + "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.25.7.tgz", + "integrity": "sha512-qidGKk4Bq0Ud0O8gRuXnDSLwVopwrf5+roNvpkvdQPVIHFSYJ5dscJkThdsn7OW8bNqahumQPWWczEh9l93FZw==", "dev": true, + "license": "MIT", "dependencies": { - "@redocly/ajv": "^8.11.0", - "@redocly/config": "^0.6.2", + "@redocly/ajv": "^8.11.2", + "@redocly/config": "^0.12.1", "colorette": "^1.2.0", "https-proxy-agent": "^7.0.4", "js-levenshtein": "^1.1.6", @@ -5416,6 +5186,7 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } @@ -5424,13 +5195,15 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@redocly/openapi-core/node_modules/minimatch": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -5439,23 +5212,26 @@ } }, "node_modules/@remix-run/router": { - "version": "1.15.3", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.3.tgz", - "integrity": "sha512-Oy8rmScVrVxWZVOpEF57ovlnhpZ8CCPlnIIumVcV9nFdiSIrus99+Lw78ekXyGvVDlIsFJbSfmSovJUhCWYV3w==", + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.20.0.tgz", + "integrity": "sha512-mUnk8rPJBI9loFDZ+YzPGdeniYK+FTmRD1TMCz7ev2SNIozyKKpnGgsxO34u6Z4z/t0ITuu7voi/AshfsGsgFg==", + "license": "MIT", "engines": { "node": ">=14.0.0" } }, "node_modules/@repeaterjs/repeater": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@repeaterjs/repeater/-/repeater-3.0.5.tgz", - "integrity": "sha512-l3YHBLAol6d/IKnB9LhpD0cEZWAoe3eFKUyTYWmFmCO2Q/WOckxLQAUyMZWwZV2M/m3+4vgRoaolFqaII82/TA==", - "dev": true + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@repeaterjs/repeater/-/repeater-3.0.6.tgz", + "integrity": "sha512-Javneu5lsuhwNCryN+pXH93VPQ8g0dBX7wItHFgYiwQmzE1sVdg5tWHiOgHywzL2W21XQopa7IwIEnNbmeUJYA==", + "dev": true, + "license": "MIT" }, "node_modules/@rollup/pluginutils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz", - "integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.2.tgz", + "integrity": "sha512-/FIdS3PyZ39bjZlwqFnWqCOVnW7o963LtKMwQOD0NhQqw22gSr2YY1afu3FxRip4ZCZNsD5jq6Aaz6QV3D/Njw==", + "license": "MIT", "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", @@ -5474,9 +5250,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.4.tgz", - "integrity": "sha512-Fxamp4aEZnfPOcGA8KSNEohV8hX7zVHOemC8jVBoBUHu5zpJK/Eu3uJwt6BMgy9fkvzxDaurgj96F/NiLukF2w==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.0.tgz", + "integrity": "sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==", "cpu": [ "arm" ], @@ -5487,9 +5263,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.22.4.tgz", - "integrity": "sha512-VXoK5UMrgECLYaMuGuVTOx5kcuap1Jm8g/M83RnCHBKOqvPPmROFJGQaZhGccnsFtfXQ3XYa4/jMCJvZnbJBdA==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.0.tgz", + "integrity": "sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==", "cpu": [ "arm64" ], @@ -5500,9 +5276,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.22.4.tgz", - "integrity": "sha512-xMM9ORBqu81jyMKCDP+SZDhnX2QEVQzTcC6G18KlTQEzWK8r/oNZtKuZaCcHhnsa6fEeOBionoyl5JsAbE/36Q==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.0.tgz", + "integrity": "sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==", "cpu": [ "arm64" ], @@ -5513,9 +5289,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.22.4.tgz", - "integrity": "sha512-aJJyYKQwbHuhTUrjWjxEvGnNNBCnmpHDvrb8JFDbeSH3m2XdHcxDd3jthAzvmoI8w/kSjd2y0udT+4okADsZIw==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.0.tgz", + "integrity": "sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==", "cpu": [ "x64" ], @@ -5526,9 +5302,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.22.4.tgz", - "integrity": "sha512-j63YtCIRAzbO+gC2L9dWXRh5BFetsv0j0va0Wi9epXDgU/XUi5dJKo4USTttVyK7fGw2nPWK0PbAvyliz50SCQ==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.0.tgz", + "integrity": "sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==", "cpu": [ "arm" ], @@ -5539,9 +5315,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.22.4.tgz", - "integrity": "sha512-dJnWUgwWBX1YBRsuKKMOlXCzh2Wu1mlHzv20TpqEsfdZLb3WoJW2kIEsGwLkroYf24IrPAvOT/ZQ2OYMV6vlrg==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.0.tgz", + "integrity": "sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==", "cpu": [ "arm" ], @@ -5552,9 +5328,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.22.4.tgz", - "integrity": "sha512-AdPRoNi3NKVLolCN/Sp4F4N1d98c4SBnHMKoLuiG6RXgoZ4sllseuGioszumnPGmPM2O7qaAX/IJdeDU8f26Aw==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.0.tgz", + "integrity": "sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==", "cpu": [ "arm64" ], @@ -5565,9 +5341,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.22.4.tgz", - "integrity": "sha512-Gl0AxBtDg8uoAn5CCqQDMqAx22Wx22pjDOjBdmG0VIWX3qUBHzYmOKh8KXHL4UpogfJ14G4wk16EQogF+v8hmA==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.0.tgz", + "integrity": "sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==", "cpu": [ "arm64" ], @@ -5578,9 +5354,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.22.4.tgz", - "integrity": "sha512-3aVCK9xfWW1oGQpTsYJJPF6bfpWfhbRnhdlyhak2ZiyFLDaayz0EP5j9V1RVLAAxlmWKTDfS9wyRyY3hvhPoOg==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.0.tgz", + "integrity": "sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==", "cpu": [ "ppc64" ], @@ -5591,9 +5367,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.22.4.tgz", - "integrity": "sha512-ePYIir6VYnhgv2C5Xe9u+ico4t8sZWXschR6fMgoPUK31yQu7hTEJb7bCqivHECwIClJfKgE7zYsh1qTP3WHUA==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.0.tgz", + "integrity": "sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==", "cpu": [ "riscv64" ], @@ -5604,9 +5380,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.22.4.tgz", - "integrity": "sha512-GqFJ9wLlbB9daxhVlrTe61vJtEY99/xB3C8e4ULVsVfflcpmR6c8UZXjtkMA6FhNONhj2eA5Tk9uAVw5orEs4Q==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.0.tgz", + "integrity": "sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==", "cpu": [ "s390x" ], @@ -5617,9 +5393,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.22.4.tgz", - "integrity": "sha512-87v0ol2sH9GE3cLQLNEy0K/R0pz1nvg76o8M5nhMR0+Q+BBGLnb35P0fVz4CQxHYXaAOhE8HhlkaZfsdUOlHwg==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.0.tgz", + "integrity": "sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==", "cpu": [ "x64" ], @@ -5630,9 +5406,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.22.4.tgz", - "integrity": "sha512-UV6FZMUgePDZrFjrNGIWzDo/vABebuXBhJEqrHxrGiU6HikPy0Z3LfdtciIttEUQfuDdCn8fqh7wiFJjCNwO+g==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.0.tgz", + "integrity": "sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==", "cpu": [ "x64" ], @@ -5643,9 +5419,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.22.4.tgz", - "integrity": "sha512-BjI+NVVEGAXjGWYHz/vv0pBqfGoUH0IGZ0cICTn7kB9PyjrATSkX+8WkguNjWoj2qSr1im/+tTGRaY+4/PdcQw==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.0.tgz", + "integrity": "sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==", "cpu": [ "arm64" ], @@ -5656,9 +5432,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.22.4.tgz", - "integrity": "sha512-SiWG/1TuUdPvYmzmYnmd3IEifzR61Tragkbx9D3+R8mzQqDBz8v+BvZNDlkiTtI9T15KYZhP0ehn3Dld4n9J5g==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.0.tgz", + "integrity": "sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==", "cpu": [ "ia32" ], @@ -5669,9 +5445,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.22.4.tgz", - "integrity": "sha512-j8pPKp53/lq9lMXN57S8cFz0MynJk8OWNuUnXct/9KCpKU7DgU3bYMJhwWmcqC0UU29p8Lr0/7KEVcaM6bf47Q==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.0.tgz", + "integrity": "sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==", "cpu": [ "x64" ], @@ -5685,12 +5461,14 @@ "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@svgr/babel-plugin-add-jsx-attribute": { "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": ">=14" }, @@ -5706,6 +5484,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" }, @@ -5721,6 +5500,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" }, @@ -5736,6 +5516,7 @@ "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": ">=14" }, @@ -5751,6 +5532,7 @@ "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": ">=14" }, @@ -5766,6 +5548,7 @@ "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": ">=14" }, @@ -5781,6 +5564,7 @@ "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": ">=14" }, @@ -5796,6 +5580,7 @@ "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" }, @@ -5811,6 +5596,7 @@ "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": "8.0.0", "@svgr/babel-plugin-remove-jsx-attribute": "8.0.0", @@ -5836,6 +5622,7 @@ "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.21.3", "@svgr/babel-preset": "8.1.0", @@ -5855,6 +5642,7 @@ "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.21.3", "entities": "^4.4.0" @@ -5871,6 +5659,7 @@ "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.21.3", "@svgr/babel-preset": "8.1.0", @@ -5892,6 +5681,7 @@ "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": "^8.1.3", "deepmerge": "^4.3.1", @@ -5912,6 +5702,7 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/@svgr/rollup/-/rollup-8.1.0.tgz", "integrity": "sha512-0XR1poYvPQoPpmfDYLEqUGu5ePAQ4pdgN3VFsZBNAeze7qubVpsIY1o1R6PZpKep/DKu33GSm2NhwpCLkMs2Cw==", + "license": "MIT", "dependencies": { "@babel/core": "^7.21.3", "@babel/plugin-transform-react-constant-elements": "^7.21.3", @@ -5932,22 +5723,24 @@ } }, "node_modules/@tailwindcss/forms": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.7.tgz", - "integrity": "sha512-QE7X69iQI+ZXwldE+rzasvbJiyV/ju1FGHH0Qn2W3FKbuYtqp8LKcy6iSw79fVUT5/Vvf+0XgLCeYVG+UV6hOw==", + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.9.tgz", + "integrity": "sha512-tM4XVr2+UVTxXJzey9Twx48c1gcxFStqn1pQz0tRsX8o3DvxhN5oY5pvyAbUx7VTaZxpej4Zzvc6h+1RJBzpIg==", + "license": "MIT", "dependencies": { "mini-svg-data-uri": "^1.2.3" }, "peerDependencies": { - "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1" + "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20" } }, "node_modules/@tanstack/react-virtual": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.0.2.tgz", - "integrity": "sha512-9XbRLPKgnhMwwmuQMnJMv+5a9sitGNCSEtf/AZXzmJdesYk7XsjYHaEDny+IrJzvPNwZliIIDwCRiaUqR3zzCA==", + "version": "3.10.8", + "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.10.8.tgz", + "integrity": "sha512-VbzbVGSsZlQktyLrP5nxE+vE1ZR+U0NFAWPbJLoG2+DKPwd2D7dVICTVIIaYlJqX1ZCEnYDbaOpmMwbsyhBoIA==", + "license": "MIT", "dependencies": { - "@tanstack/virtual-core": "3.0.0" + "@tanstack/virtual-core": "3.10.8" }, "funding": { "type": "github", @@ -5959,9 +5752,10 @@ } }, "node_modules/@tanstack/virtual-core": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.0.0.tgz", - "integrity": "sha512-SYXOBTjJb05rXa2vl55TTwO40A6wKu0R5i1qQwhJYNDIqaIGF7D0HsLw+pJAyi2OvntlEIVusx3xtbbgSUi6zg==", + "version": "3.10.8", + "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.10.8.tgz", + "integrity": "sha512-PBu00mtt95jbKFi6Llk9aik8bnR3tR/oQP1o3TSi+iG//+Q2RTIzCEgKkHG8BB86kxMNW6O8wku+Lmi+QFR6jA==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/tannerlinsley" @@ -5972,6 +5766,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 +5782,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", @@ -6008,44 +5804,23 @@ "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" } }, - "node_modules/@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "devOptional": 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 - }, - "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 - }, - "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 - }, "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", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "license": "MIT", "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", @@ -6058,6 +5833,7 @@ "version": "7.6.8", "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "license": "MIT", "dependencies": { "@babel/types": "^7.0.0" } @@ -6066,15 +5842,17 @@ "version": "7.4.4", "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "license": "MIT", "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" } }, "node_modules/@types/babel__traverse": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.5.tgz", - "integrity": "sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==", + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", + "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", + "license": "MIT", "dependencies": { "@babel/types": "^7.20.7" } @@ -6083,6 +5861,7 @@ "version": "5.60.15", "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.15.tgz", "integrity": "sha512-dTOvwEQ+ouKJ/rE9LT1Ue2hmP6H1mZv5+CCnNWu2qtiOe2LQa9lCprEY20HxiDmV/Bxh+dXjywmy5aKvoGjULA==", + "license": "MIT", "dependencies": { "@types/tern": "*" } @@ -6090,22 +5869,26 @@ "node_modules/@types/d3-array": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz", - "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==" + "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==", + "license": "MIT" }, "node_modules/@types/d3-color": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", - "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==" + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "license": "MIT" }, "node_modules/@types/d3-ease": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", - "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==" + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", + "license": "MIT" }, "node_modules/@types/d3-interpolate": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "license": "MIT", "dependencies": { "@types/d3-color": "*" } @@ -6113,12 +5896,14 @@ "node_modules/@types/d3-path": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.0.tgz", - "integrity": "sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ==" + "integrity": "sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ==", + "license": "MIT" }, "node_modules/@types/d3-scale": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz", "integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==", + "license": "MIT", "dependencies": { "@types/d3-time": "*" } @@ -6127,6 +5912,7 @@ "version": "3.1.6", "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.6.tgz", "integrity": "sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==", + "license": "MIT", "dependencies": { "@types/d3-path": "*" } @@ -6134,30 +5920,35 @@ "node_modules/@types/d3-time": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.3.tgz", - "integrity": "sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==" + "integrity": "sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==", + "license": "MIT" }, "node_modules/@types/d3-timer": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", - "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==" + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "license": "MIT" }, "node_modules/@types/debug": { "version": "4.1.12", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "license": "MIT", "dependencies": { "@types/ms": "*" } }, "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "license": "MIT" }, "node_modules/@types/estree-jsx": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.4.tgz", - "integrity": "sha512-5idy3hvI9lAMqsyilBM+N+boaCf1MgoefbDxN6KEO5aK17TOHwFAYT9sjxzeKAiIWRUBgLxmZ9mPcnzZXtTcRQ==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "license": "MIT", "dependencies": { "@types/estree": "*" } @@ -6166,47 +5957,33 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "license": "MIT", "dependencies": { "@types/unist": "*" } }, - "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==", - "dev": true - }, "node_modules/@types/js-yaml": { "version": "4.0.9", "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", - "dev": true - }, - "node_modules/@types/json-stable-stringify": { - "version": "1.0.36", - "resolved": "https://registry.npmjs.org/@types/json-stable-stringify/-/json-stable-stringify-1.0.36.tgz", - "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 + "dev": true, + "license": "MIT" }, "node_modules/@types/loadable__component": { "version": "5.13.9", "resolved": "https://registry.npmjs.org/@types/loadable__component/-/loadable__component-5.13.9.tgz", "integrity": "sha512-QWOtIkwZqHNdQj3nixQ8oyihQiTMKZLk/DNuvNxMSbTfxf47w+kqcbnxlUeBgAxdOtW0Dh48dTAIp83iJKtnrQ==", "dev": true, + "license": "MIT", "dependencies": { "@types/react": "*" } }, "node_modules/@types/mdast": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.3.tgz", - "integrity": "sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", "dependencies": { "@types/unist": "*" } @@ -6214,51 +5991,58 @@ "node_modules/@types/ms": { "version": "0.7.34", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", - "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==" + "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==", + "license": "MIT" }, "node_modules/@types/node": { - "version": "20.12.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.3.tgz", - "integrity": "sha512-sD+ia2ubTeWrOu+YMF+MTAB7E+O7qsMqAbMfW7DG3K1URwhZ5hN1pLlRVGbf4wDFzSfikL05M17EyorS86jShw==", + "version": "20.16.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.12.tgz", + "integrity": "sha512-LfPFB0zOeCeCNQV3i+67rcoVvoN5n0NVuR2vLG0O5ySQMgchuZlC4lgz546ZOJyDtj5KIgOxy+lacOimfqZAIA==", "devOptional": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~6.19.2" } }, "node_modules/@types/prismjs": { - "version": "1.26.3", - "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.3.tgz", - "integrity": "sha512-A0D0aTXvjlqJ5ZILMz3rNfDBOx9hHxLZYv2by47Sm/pqW35zzjusrZTryatjN/Rf8Us2gZrJD+KeHbUSTux1Cw==", - "dev": true + "version": "1.26.4", + "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.4.tgz", + "integrity": "sha512-rlAnzkW2sZOjbqZ743IHUhFcvzaGbqijwOu8QZnZCjfQzBqFE3s4lOTJEsxikImav9uzz/42I+O7YUs1mWgMlg==", + "dev": true, + "license": "MIT" }, "node_modules/@types/prop-types": { - "version": "15.7.11", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", - "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==" + "version": "15.7.13", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", + "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==", + "license": "MIT" }, "node_modules/@types/ramda": { "version": "0.29.12", "resolved": "https://registry.npmjs.org/@types/ramda/-/ramda-0.29.12.tgz", "integrity": "sha512-sgIEjpJhdQPB52gDF4aphs9nl0xe54CR22DPdWqT8gQHjZYmVApgA0R3/CpMbl0Y8az2TEZrPNL2zy0EvjbkLA==", "dev": true, + "license": "MIT", "dependencies": { "types-ramda": "^0.29.10" } }, "node_modules/@types/react": { - "version": "18.2.74", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.74.tgz", - "integrity": "sha512-9AEqNZZyBx8OdZpxzQlaFEVCSFUM2YXJH46yPOiOpm078k6ZLOCcuAzGum/zK8YBwY+dbahVNbHrbgrAwIRlqw==", + "version": "18.3.11", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.11.tgz", + "integrity": "sha512-r6QZ069rFTjrEYgFdOck1gK7FLVsgJE7tTz0pQBczlBNUhBNk0MQH4UbnFSwjpQLMkLzgqvBBa+qGpLje16eTQ==", + "license": "MIT", "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" } }, "node_modules/@types/react-datepicker": { - "version": "4.19.5", - "resolved": "https://registry.npmjs.org/@types/react-datepicker/-/react-datepicker-4.19.5.tgz", - "integrity": "sha512-tKpuj19p9T4sBQm3Bw13CPuhalo4CFOe/LcSUGJ5z6DmHoiBX3uq33iMKePeSEq7OxyU8O1rh5emAm92nyXZLg==", + "version": "4.19.6", + "resolved": "https://registry.npmjs.org/@types/react-datepicker/-/react-datepicker-4.19.6.tgz", + "integrity": "sha512-uH5fzxt9eXxnc+hDCy/iRSFqU2+9lR/q2lAmaG4WILMai1o3IOdpcV+VSypzBFJLTEC2jrfeDXcdol0CJVMq4g==", "dev": true, + "license": "MIT", "dependencies": { "@popperjs/core": "^2.9.2", "@types/react": "*", @@ -6271,6 +6055,7 @@ "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/runtime": "^7.21.0" }, @@ -6283,19 +6068,21 @@ } }, "node_modules/@types/react-dom": { - "version": "18.2.23", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.23.tgz", - "integrity": "sha512-ZQ71wgGOTmDYpnav2knkjr3qXdAFu0vsk8Ci5w3pGAIdj7/kKAyn+VsQDhXsmzzzepAiI9leWMmubXz690AI/A==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==", "devOptional": true, + "license": "MIT", "dependencies": { "@types/react": "*" } }, "node_modules/@types/react-test-renderer": { - "version": "18.0.7", - "resolved": "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-18.0.7.tgz", - "integrity": "sha512-1+ANPOWc6rB3IkSnElhjv6VLlKg2dSv/OWClUyZimbLsQyBn8Js9Vtdsi3UICJ2rIQ3k2la06dkB+C92QfhKmg==", + "version": "18.3.0", + "resolved": "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-18.3.0.tgz", + "integrity": "sha512-HW4MuEYxfDbOHQsVlY/XtOvNHftCVEPhJF2pQXXwcUiUF+Oyb0usgp48HSgpK5rt8m9KZb22yqOeZm+rrVG8gw==", "dev": true, + "license": "MIT", "dependencies": { "@types/react": "*" } @@ -6305,6 +6092,7 @@ "resolved": "https://registry.npmjs.org/@types/sha1/-/sha1-1.1.5.tgz", "integrity": "sha512-eE1PzjW7u2VfxI+bTsvjzjBfpwqvxSpgfUmnRNVY+PJU1NBsdGZlaO/qnVnPKHzzpgIl9YyBIxvrgBvt1mzt2A==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } @@ -6313,32 +6101,37 @@ "version": "8.1.1", "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz", "integrity": "sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/sizzle": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.8.tgz", "integrity": "sha512-0vWLNK2D5MT9dg0iOo8GlKguPAU02QjmZitPEsXRuJXU/OGIOt9vT9Fc26wtYuavLxtO45v9PGleoL9Z0k1LHg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/tern": { "version": "0.23.9", "resolved": "https://registry.npmjs.org/@types/tern/-/tern-0.23.9.tgz", "integrity": "sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw==", + "license": "MIT", "dependencies": { "@types/estree": "*" } }, "node_modules/@types/unist": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", - "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==" + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" }, "node_modules/@types/ws": { - "version": "8.5.10", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", - "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", + "version": "8.5.12", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.12.tgz", + "integrity": "sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } @@ -6348,326 +6141,111 @@ "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", "dev": true, + "license": "MIT", "optional": true, "dependencies": { "@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.3.2", + "resolved": "https://registry.npmjs.org/@uiw/color-convert/-/color-convert-2.3.2.tgz", + "integrity": "sha512-Txs0oAcOGhvM15yi7NqDJSws6htpuGx75EblFlZmh4h4AyUYXaeN2HNcOAUt835M3SN1j7rqMC+XERIE4r776Q==", + "license": "MIT", "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, + "node_modules/@uiw/react-color": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@uiw/react-color/-/react-color-2.3.2.tgz", + "integrity": "sha512-PfyzYFIa16MKVd2FLFmV/+moznibDCDZB6AUwV6lrL7Mz+/jjcW1JDDdCRWMHC8e7uoC3EmnYAmnpj2eF0w2vw==", + "license": "MIT", "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" + "@uiw/color-convert": "2.3.2", + "@uiw/react-color-alpha": "2.3.2", + "@uiw/react-color-block": "2.3.2", + "@uiw/react-color-chrome": "2.3.2", + "@uiw/react-color-circle": "2.3.2", + "@uiw/react-color-colorful": "2.3.2", + "@uiw/react-color-compact": "2.3.2", + "@uiw/react-color-editable-input": "2.3.2", + "@uiw/react-color-editable-input-hsla": "2.3.2", + "@uiw/react-color-editable-input-rgba": "2.3.2", + "@uiw/react-color-github": "2.3.2", + "@uiw/react-color-hue": "2.3.2", + "@uiw/react-color-material": "2.3.2", + "@uiw/react-color-name": "2.3.2", + "@uiw/react-color-saturation": "2.3.2", + "@uiw/react-color-shade-slider": "2.3.2", + "@uiw/react-color-sketch": "2.3.2", + "@uiw/react-color-slider": "2.3.2", + "@uiw/react-color-swatch": "2.3.2", + "@uiw/react-color-wheel": "2.3.2" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "url": "https://jaywcjlove.github.io/#/sponsor" }, "peerDependencies": { - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" } }, - "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, + "node_modules/@uiw/react-color-alpha": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@uiw/react-color-alpha/-/react-color-alpha-2.3.2.tgz", + "integrity": "sha512-+yh+KEpNKjxNFFODQrB3Lki2hu6kznoSCngHgptlWBUtAC3e/e7tIiTTedSpCGr7fwIpC0CWrKwxENA3tyY/2Q==", + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "7.16.0", - "@typescript-eslint/visitor-keys": "7.16.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" + "@uiw/color-convert": "2.3.2", + "@uiw/react-drag-event-interactive": "2.3.2" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "url": "https://jaywcjlove.github.io/#/sponsor" + }, + "peerDependencies": { + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" } }, - "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, + "node_modules/@uiw/react-color-block": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@uiw/react-color-block/-/react-color-block-2.3.2.tgz", + "integrity": "sha512-eic08WG6IFBEWrsE/U9aMuZxW9gSdX4s5iD5TyZMlHlUiGIneGXEOOSHNqlIfA7Dxbs1STYQbEQU/aSx6APYLw==", + "license": "MIT", "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" + "@uiw/color-convert": "2.3.2", + "@uiw/react-color-editable-input": "2.3.2", + "@uiw/react-color-swatch": "2.3.2" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "url": "https://jaywcjlove.github.io/#/sponsor" }, "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==", - "dependencies": { - "@uiw/color-convert": "2.1.1", - "@uiw/react-color-alpha": "2.1.1", - "@uiw/react-color-block": "2.1.1", - "@uiw/react-color-chrome": "2.1.1", - "@uiw/react-color-circle": "2.1.1", - "@uiw/react-color-colorful": "2.1.1", - "@uiw/react-color-compact": "2.1.1", - "@uiw/react-color-editable-input": "2.1.1", - "@uiw/react-color-editable-input-hsla": "2.1.1", - "@uiw/react-color-editable-input-rgba": "2.1.1", - "@uiw/react-color-github": "2.1.1", - "@uiw/react-color-hue": "2.1.1", - "@uiw/react-color-material": "2.1.1", - "@uiw/react-color-name": "2.1.1", - "@uiw/react-color-saturation": "2.1.1", - "@uiw/react-color-shade-slider": "2.1.1", - "@uiw/react-color-sketch": "2.1.1", - "@uiw/react-color-slider": "2.1.1", - "@uiw/react-color-swatch": "2.1.1", - "@uiw/react-color-wheel": "2.1.1" - }, - "funding": { - "url": "https://jaywcjlove.github.io/#/sponsor" - }, - "peerDependencies": { - "@babel/runtime": ">=7.19.0", - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/@uiw/react-color-alpha": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@uiw/react-color-alpha/-/react-color-alpha-2.1.1.tgz", - "integrity": "sha512-6wvWLn4Dgb3jIaveLdjhSg2RJIWKJbRU/uHSFtEd8rvXebRt9P7NFr5YsnkHDBUitx9KFxRL6kaI/GQCYU+8nA==", - "dependencies": { - "@uiw/color-convert": "2.1.1", - "@uiw/react-drag-event-interactive": "2.1.1" - }, - "funding": { - "url": "https://jaywcjlove.github.io/#/sponsor" - }, - "peerDependencies": { - "@babel/runtime": ">=7.19.0", - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/@uiw/react-color-block": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@uiw/react-color-block/-/react-color-block-2.1.1.tgz", - "integrity": "sha512-c4xA42z7aLou8jBjxwLgUYZ+SiaZbVMADPLE/CcBi8EY/NcvvvtrL2wJGqE0g2Aqfey5RjB7nFxUeqSG1N00aA==", - "dependencies": { - "@uiw/color-convert": "2.1.1", - "@uiw/react-color-editable-input": "2.1.1", - "@uiw/react-color-swatch": "2.1.1" - }, - "funding": { - "url": "https://jaywcjlove.github.io/#/sponsor" - }, - "peerDependencies": { - "@babel/runtime": ">=7.19.0", - "react": ">=16.9.0", - "react-dom": ">=16.9.0" + "@babel/runtime": ">=7.19.0", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" } }, "node_modules/@uiw/react-color-chrome": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@uiw/react-color-chrome/-/react-color-chrome-2.1.1.tgz", - "integrity": "sha512-tv51lG6Wol8skiclLXXc8yf5nAVig5OjYtuNxsnFr165GP1YJ/mdnS7OIprYF/wP5mz66W7K0iz/8hAIof5/ug==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@uiw/react-color-chrome/-/react-color-chrome-2.3.2.tgz", + "integrity": "sha512-WvA8dg2y+vgoyy8GFBM3B1+SeJ29ov5OVEei1kACMKxThADPdI4w3RRmhYIMnSeFGVW3bGuBMq6JimjIKZirCQ==", + "license": "MIT", "dependencies": { - "@uiw/color-convert": "2.1.1", - "@uiw/react-color-alpha": "2.1.1", - "@uiw/react-color-editable-input": "2.1.1", - "@uiw/react-color-editable-input-hsla": "2.1.1", - "@uiw/react-color-editable-input-rgba": "2.1.1", - "@uiw/react-color-github": "2.1.1", - "@uiw/react-color-hue": "2.1.1", - "@uiw/react-color-saturation": "2.1.1" + "@uiw/color-convert": "2.3.2", + "@uiw/react-color-alpha": "2.3.2", + "@uiw/react-color-editable-input": "2.3.2", + "@uiw/react-color-editable-input-hsla": "2.3.2", + "@uiw/react-color-editable-input-rgba": "2.3.2", + "@uiw/react-color-github": "2.3.2", + "@uiw/react-color-hue": "2.3.2", + "@uiw/react-color-saturation": "2.3.2" }, "funding": { "url": "https://jaywcjlove.github.io/#/sponsor" @@ -6679,12 +6257,13 @@ } }, "node_modules/@uiw/react-color-circle": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@uiw/react-color-circle/-/react-color-circle-2.1.1.tgz", - "integrity": "sha512-t/Wr6eT9GLOzywaTmJclOZ3NuirJlMk8eVxoLKM7eRePbN5WowIEMwug/towSU3YrBrEbSSWjZRhfVUqdh7kMw==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@uiw/react-color-circle/-/react-color-circle-2.3.2.tgz", + "integrity": "sha512-lndeyFmvKNZ5MBwL9BqkfceuhOnIGQW3gB9wKRPxiKmux9maxllirTwWNvv/6vgGFMnfaR89NO7pjTyf7mNhOA==", + "license": "MIT", "dependencies": { - "@uiw/color-convert": "2.1.1", - "@uiw/react-color-swatch": "2.1.1" + "@uiw/color-convert": "2.3.2", + "@uiw/react-color-swatch": "2.3.2" }, "funding": { "url": "https://jaywcjlove.github.io/#/sponsor" @@ -6696,14 +6275,15 @@ } }, "node_modules/@uiw/react-color-colorful": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@uiw/react-color-colorful/-/react-color-colorful-2.1.1.tgz", - "integrity": "sha512-7cGRtYv+llXO7Tmpfska9HxjQAbkqBP5P63wned6JD/0lOM4KXELxhWI3044nO/Osi5r3FDsGh0HRqiLgUK75Q==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@uiw/react-color-colorful/-/react-color-colorful-2.3.2.tgz", + "integrity": "sha512-Rr1qa4Uo588CZjOx6OZIXjo7+CNcnqogU0Nel6L5zzwL+3CNBC3GfS4Or4db/ZxFhN49qt6NqsQa/ykA2/ixpg==", + "license": "MIT", "dependencies": { - "@uiw/color-convert": "2.1.1", - "@uiw/react-color-alpha": "2.1.1", - "@uiw/react-color-hue": "2.1.1", - "@uiw/react-color-saturation": "2.1.1" + "@uiw/color-convert": "2.3.2", + "@uiw/react-color-alpha": "2.3.2", + "@uiw/react-color-hue": "2.3.2", + "@uiw/react-color-saturation": "2.3.2" }, "funding": { "url": "https://jaywcjlove.github.io/#/sponsor" @@ -6715,14 +6295,15 @@ } }, "node_modules/@uiw/react-color-compact": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@uiw/react-color-compact/-/react-color-compact-2.1.1.tgz", - "integrity": "sha512-Af9skc0Bx3lot2zg58SlpMoQEKqyMbKvr7y2O541Hodc89ykgEDJZXqLrKiKnacBWMZxSAcAHoSFf70RLeoTlw==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@uiw/react-color-compact/-/react-color-compact-2.3.2.tgz", + "integrity": "sha512-3/eQkndGBIBr91mkPoOgS8NwTxpz5wEt5UK6CSvbC+zOQtEzZaWq+XpB7ieoUjla3yiKgdBauu3iYTwo1LOFEw==", + "license": "MIT", "dependencies": { - "@uiw/color-convert": "2.1.1", - "@uiw/react-color-editable-input": "2.1.1", - "@uiw/react-color-editable-input-rgba": "2.1.1", - "@uiw/react-color-swatch": "2.1.1" + "@uiw/color-convert": "2.3.2", + "@uiw/react-color-editable-input": "2.3.2", + "@uiw/react-color-editable-input-rgba": "2.3.2", + "@uiw/react-color-swatch": "2.3.2" }, "funding": { "url": "https://jaywcjlove.github.io/#/sponsor" @@ -6734,9 +6315,10 @@ } }, "node_modules/@uiw/react-color-editable-input": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@uiw/react-color-editable-input/-/react-color-editable-input-2.1.1.tgz", - "integrity": "sha512-mEohydHWV49iQ3RuH/3My20T7wDtOPzzGEBOMJeHIxMnN6FMwl9U1bAAgDb2ovnt5Ws0PaCWcBjNKHARpVSZ1Q==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@uiw/react-color-editable-input/-/react-color-editable-input-2.3.2.tgz", + "integrity": "sha512-DDl9pCN7hH5Q+OB6LiFGFmkqIQw81EDIEvDi6rr6G9U+k+oqZ9JCBKSZ9qNKSa4MqnuISOkFv0vL3Jh8fSyqcg==", + "license": "MIT", "funding": { "url": "https://jaywcjlove.github.io/#/sponsor" }, @@ -6747,12 +6329,13 @@ } }, "node_modules/@uiw/react-color-editable-input-hsla": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@uiw/react-color-editable-input-hsla/-/react-color-editable-input-hsla-2.1.1.tgz", - "integrity": "sha512-2Eqcd0hUa5qLVxT062Vf9vsxS2/6X+AJ6f6Wfs6/UAM6iUWqWPkJxajRmEtFfB6Zv5bcaYjhSZNGgeEf7azAvQ==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@uiw/react-color-editable-input-hsla/-/react-color-editable-input-hsla-2.3.2.tgz", + "integrity": "sha512-lLO8K+Zv5L9HKBgM3zYSqeLKisBkpXCxlQmF8iCKYcIgeRilM26qqylskA4n6pVixfSooL0hyL/HwfNmbCIrrg==", + "license": "MIT", "dependencies": { - "@uiw/color-convert": "2.1.1", - "@uiw/react-color-editable-input-rgba": "2.1.1" + "@uiw/color-convert": "2.3.2", + "@uiw/react-color-editable-input-rgba": "2.3.2" }, "funding": { "url": "https://jaywcjlove.github.io/#/sponsor" @@ -6764,12 +6347,13 @@ } }, "node_modules/@uiw/react-color-editable-input-rgba": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@uiw/react-color-editable-input-rgba/-/react-color-editable-input-rgba-2.1.1.tgz", - "integrity": "sha512-6YtDaBWTXu27MK6s3HZty0qg3mYb4GN/8dI8T39R/qEiMX/SButMfC09pnygN74InyuG8MzobUg2GowfTRUG5A==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@uiw/react-color-editable-input-rgba/-/react-color-editable-input-rgba-2.3.2.tgz", + "integrity": "sha512-HV0+5zzpaNG5v6EyVgmPfInd9UzYknQI7gdsVmmXKzB13L3RFhiv77r6o+q3IZMEnoDZ3U92uX4FeRaM1NrwYw==", + "license": "MIT", "dependencies": { - "@uiw/color-convert": "2.1.1", - "@uiw/react-color-editable-input": "2.1.1" + "@uiw/color-convert": "2.3.2", + "@uiw/react-color-editable-input": "2.3.2" }, "funding": { "url": "https://jaywcjlove.github.io/#/sponsor" @@ -6781,12 +6365,13 @@ } }, "node_modules/@uiw/react-color-github": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@uiw/react-color-github/-/react-color-github-2.1.1.tgz", - "integrity": "sha512-5balACHzjVqrkdEsGXI2Ir4iXQrTAHQ7uRzqY+op41uuciIb8yGI1PecJd2qIjUJhk/kZ4nmp6KAjQEAK2iVIQ==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@uiw/react-color-github/-/react-color-github-2.3.2.tgz", + "integrity": "sha512-3QxpEOKYXbbV/L1cYsf7nhoOnl19/zWTpRRgda8LOe3SuRhFrFM3ZLa+UJUEXgO1cg9h64gxSKINh2st/FawpQ==", + "license": "MIT", "dependencies": { - "@uiw/color-convert": "2.1.1", - "@uiw/react-color-swatch": "2.1.1" + "@uiw/color-convert": "2.3.2", + "@uiw/react-color-swatch": "2.3.2" }, "funding": { "url": "https://jaywcjlove.github.io/#/sponsor" @@ -6798,12 +6383,13 @@ } }, "node_modules/@uiw/react-color-hue": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@uiw/react-color-hue/-/react-color-hue-2.1.1.tgz", - "integrity": "sha512-IvN2acCV35yRfmbscUQbNfwjKF+g51kMONv9j0zxDlTct2R0x4gatsVjA1tTpLv5UCIkFvhw80xg04QATrJ4Nw==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@uiw/react-color-hue/-/react-color-hue-2.3.2.tgz", + "integrity": "sha512-aAveo++GAghw09Ngc8Zzwxhj9mGaJfw8q40fDGFrVNxdrwrAjySIKHzlOSg5kw6WnEp4tUjhkMXDfCZWUhqmPA==", + "license": "MIT", "dependencies": { - "@uiw/color-convert": "2.1.1", - "@uiw/react-color-alpha": "2.1.1" + "@uiw/color-convert": "2.3.2", + "@uiw/react-color-alpha": "2.3.2" }, "funding": { "url": "https://jaywcjlove.github.io/#/sponsor" @@ -6815,13 +6401,14 @@ } }, "node_modules/@uiw/react-color-material": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@uiw/react-color-material/-/react-color-material-2.1.1.tgz", - "integrity": "sha512-Pcp/kpBnTGYXqP0up3rqTdJWKtnD2XdiA5Zdh5bdwqssI+qHo0cVELXOnpwU3LiSCZykTDauvGoOqTWprvox4g==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@uiw/react-color-material/-/react-color-material-2.3.2.tgz", + "integrity": "sha512-fb+bVCwRoeb4INAFEEYU26GWU7+/695DFz7C/dA2RLUa279NhVtNaOtISULT+u+Aerf2dR6GrjBk5wdgNqRqPg==", + "license": "MIT", "dependencies": { - "@uiw/color-convert": "2.1.1", - "@uiw/react-color-editable-input": "2.1.1", - "@uiw/react-color-editable-input-rgba": "2.1.1" + "@uiw/color-convert": "2.3.2", + "@uiw/react-color-editable-input": "2.3.2", + "@uiw/react-color-editable-input-rgba": "2.3.2" }, "funding": { "url": "https://jaywcjlove.github.io/#/sponsor" @@ -6833,9 +6420,10 @@ } }, "node_modules/@uiw/react-color-name": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@uiw/react-color-name/-/react-color-name-2.1.1.tgz", - "integrity": "sha512-k+19NgPHPZ88oqzCfAcVd7fT4F6XywkeZkX3DDyRG3Skc8zuGdIS2xT7Ne7ZSQb31UT0+UfuOiwOST+r+kGnFA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@uiw/react-color-name/-/react-color-name-2.3.2.tgz", + "integrity": "sha512-ZLt6ypbsGbo48wSgtFa5t+egZ57VmWIriyW/6rMNK5nMB8Y9Da7tqT2dL4WfwPzrG7I/97qafinfnGzPAuHdsw==", + "license": "MIT", "dependencies": { "colors-named": "^1.0.1", "colors-named-hex": "^1.0.1" @@ -6848,12 +6436,13 @@ } }, "node_modules/@uiw/react-color-saturation": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@uiw/react-color-saturation/-/react-color-saturation-2.1.1.tgz", - "integrity": "sha512-lg3ElCNuiHt7wsfR9FQpgFcg9zht+GAuVhemvgLq6twR62ZUgFd58in42T1F8l2ZpimXu8SgLGEtvc7XB2i8CQ==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@uiw/react-color-saturation/-/react-color-saturation-2.3.2.tgz", + "integrity": "sha512-aDKMhjylBUb4dH4oTQYz+U4mhpOebbQ2J0Y8y5aX1tfZ3fZuBqnXtWzu7Ffj3ksdKwkQHPddxT5IDTbAChF/og==", + "license": "MIT", "dependencies": { - "@uiw/color-convert": "2.1.1", - "@uiw/react-drag-event-interactive": "2.1.1" + "@uiw/color-convert": "2.3.2", + "@uiw/react-drag-event-interactive": "2.3.2" }, "funding": { "url": "https://jaywcjlove.github.io/#/sponsor" @@ -6865,12 +6454,13 @@ } }, "node_modules/@uiw/react-color-shade-slider": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@uiw/react-color-shade-slider/-/react-color-shade-slider-2.1.1.tgz", - "integrity": "sha512-7DO2d53GGFR6fXPS7g3hUNTlQCwNjKSQum90h4HeJa5jxIQiuSpeMVPo6IoG8EAuP88Au5C55SYV+qjNgLvG8Q==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@uiw/react-color-shade-slider/-/react-color-shade-slider-2.3.2.tgz", + "integrity": "sha512-nM8AJPpq9UnC7LWjQEZ28bIm8gMRqRzk3dXfGQ4X3t3040d11o4sbl23pTmWTjn6P+3+MP/L2FhmvLSTUkMp/g==", + "license": "MIT", "dependencies": { - "@uiw/color-convert": "2.1.1", - "@uiw/react-color-alpha": "2.1.1" + "@uiw/color-convert": "2.3.2", + "@uiw/react-color-alpha": "2.3.2" }, "funding": { "url": "https://jaywcjlove.github.io/#/sponsor" @@ -6882,17 +6472,18 @@ } }, "node_modules/@uiw/react-color-sketch": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@uiw/react-color-sketch/-/react-color-sketch-2.1.1.tgz", - "integrity": "sha512-hiHhwSJnMzRs9mUfOiqekPosQQ60mhzRN1LDfD/z4sW7GHxWgV9sl9jdoBeN3RRS0O4i/qHjNrJqTad6D4rV7g==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@uiw/react-color-sketch/-/react-color-sketch-2.3.2.tgz", + "integrity": "sha512-CMCbzarRVEqZLCi84AE4RQrHapvZQTXFylm7A7mfA28qInFFyF3jZsqRu6Y5sc33rPmL4AeSURbgPljmolxQ1g==", + "license": "MIT", "dependencies": { - "@uiw/color-convert": "2.1.1", - "@uiw/react-color-alpha": "2.1.1", - "@uiw/react-color-editable-input": "2.1.1", - "@uiw/react-color-editable-input-rgba": "2.1.1", - "@uiw/react-color-hue": "2.1.1", - "@uiw/react-color-saturation": "2.1.1", - "@uiw/react-color-swatch": "2.1.1" + "@uiw/color-convert": "2.3.2", + "@uiw/react-color-alpha": "2.3.2", + "@uiw/react-color-editable-input": "2.3.2", + "@uiw/react-color-editable-input-rgba": "2.3.2", + "@uiw/react-color-hue": "2.3.2", + "@uiw/react-color-saturation": "2.3.2", + "@uiw/react-color-swatch": "2.3.2" }, "funding": { "url": "https://jaywcjlove.github.io/#/sponsor" @@ -6904,12 +6495,13 @@ } }, "node_modules/@uiw/react-color-slider": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@uiw/react-color-slider/-/react-color-slider-2.1.1.tgz", - "integrity": "sha512-J0nqSpiJS4lZCUudAFo8sFMTZgiPgQ0iR4ADx1Hc5vGJr5KfpGwOVq68cUvqiAqXplUQZPVcjwoBhxl4M4fCzg==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@uiw/react-color-slider/-/react-color-slider-2.3.2.tgz", + "integrity": "sha512-Mf9w9YwI+nYxagoTPsxzEHX+/EkVTH0Ak84V6CgcmHVxM3zGESdpalZ+9B6NFWjy9nP7Oa2lK1i4cyZr8D7v2g==", + "license": "MIT", "dependencies": { - "@uiw/color-convert": "2.1.1", - "@uiw/react-color-alpha": "2.1.1" + "@uiw/color-convert": "2.3.2", + "@uiw/react-color-alpha": "2.3.2" }, "funding": { "url": "https://jaywcjlove.github.io/#/sponsor" @@ -6921,11 +6513,12 @@ } }, "node_modules/@uiw/react-color-swatch": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@uiw/react-color-swatch/-/react-color-swatch-2.1.1.tgz", - "integrity": "sha512-soKsfVgflcKSBx47PaBocKZ0beIXfk9ruE4r9778mGnDDpxc2RC5zPNfvzQkSLKW+siXIS0cscuvb/8s1zK5jw==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@uiw/react-color-swatch/-/react-color-swatch-2.3.2.tgz", + "integrity": "sha512-AjkEcSdlpxiFm9yull4WDujuHr0tD9/+XkLtcus/MH768zSQbb+rj6cY1nZ8L8FI6LRDGRaVJqFaXm4ZOAaIZw==", + "license": "MIT", "dependencies": { - "@uiw/color-convert": "2.1.1" + "@uiw/color-convert": "2.3.2" }, "funding": { "url": "https://jaywcjlove.github.io/#/sponsor" @@ -6937,12 +6530,13 @@ } }, "node_modules/@uiw/react-color-wheel": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@uiw/react-color-wheel/-/react-color-wheel-2.1.1.tgz", - "integrity": "sha512-88TYL9GCStBNULEkZ6qlQt8z/jnAf1ZSJwpbVK1JGUwogPJaMAJt8FRSUfzTNpIwYA8ymgK9Y1seng6Z8YkS9Q==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@uiw/react-color-wheel/-/react-color-wheel-2.3.2.tgz", + "integrity": "sha512-AwZZusWq+nlNNEjif6ruryrgc/cVuQ0x6XbdIVUbiQekfHFv+LunHnMS4EtmX+yPiOVihTvBp8NpfrdN2jJ8hw==", + "license": "MIT", "dependencies": { - "@uiw/color-convert": "2.1.1", - "@uiw/react-drag-event-interactive": "2.1.1" + "@uiw/color-convert": "2.3.2", + "@uiw/react-drag-event-interactive": "2.3.2" }, "funding": { "url": "https://jaywcjlove.github.io/#/sponsor" @@ -6954,9 +6548,10 @@ } }, "node_modules/@uiw/react-drag-event-interactive": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@uiw/react-drag-event-interactive/-/react-drag-event-interactive-2.1.1.tgz", - "integrity": "sha512-hJjoJg9ZASzhY6HFwZSnNhx+BJ5rfqqUnpTm6ZtfjCO5DKRZW3CDios0cmMu2Ojvdu0a9qE9x8CURCUuoCzqxw==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@uiw/react-drag-event-interactive/-/react-drag-event-interactive-2.3.2.tgz", + "integrity": "sha512-lG5pJCtqbYBv7Dj0r12PE9q9yg7P2CzlQodw5ZHPY9GCSZVXHJc0g4lGvCbe/4Y8HYqM8aU4CYS8LplpX+mIQw==", + "license": "MIT", "funding": { "url": "https://jaywcjlove.github.io/#/sponsor" }, @@ -6969,18 +6564,20 @@ "node_modules/@ungap/structured-clone": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==" + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "license": "ISC" }, "node_modules/@vitejs/plugin-react": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.2.1.tgz", - "integrity": "sha512-oojO9IDc4nCUUi8qIR11KoQm0XFFLIwsRBwHRR4d/88IWghn1y6ckz/bJ8GHDCsYEJee8mDzqtJxh15/cisJNQ==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.2.tgz", + "integrity": "sha512-hieu+o05v4glEBucTcKMK3dlES0OeJlD9YVOAPraVMOInBCwzumaIFiUjr4bHK7NPgnAHgiskUoceKercrN8vg==", + "license": "MIT", "dependencies": { - "@babel/core": "^7.23.5", - "@babel/plugin-transform-react-jsx-self": "^7.23.3", - "@babel/plugin-transform-react-jsx-source": "^7.23.3", + "@babel/core": "^7.25.2", + "@babel/plugin-transform-react-jsx-self": "^7.24.7", + "@babel/plugin-transform-react-jsx-source": "^7.24.7", "@types/babel__core": "^7.20.5", - "react-refresh": "^0.14.0" + "react-refresh": "^0.14.2" }, "engines": { "node": "^14.18.0 || >=16.0.0" @@ -6990,10 +6587,11 @@ } }, "node_modules/@vitest/coverage-v8": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-1.4.0.tgz", - "integrity": "sha512-4hDGyH1SvKpgZnIByr9LhGgCEuF9DKM34IBLCC/fVfy24Z3+PZ+Ii9hsVBsHvY1umM1aGPEjceRkzxCfcQ10wg==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-1.6.0.tgz", + "integrity": "sha512-KvapcbMY/8GYIG0rlwwOKCVNRc0OL20rrhFkg/CHNzncV03TE2XWvO5w9uZYoxNiMEBacAJt3unSOiZ7svePew==", "dev": true, + "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.1", "@bcoe/v8-coverage": "^0.2.3", @@ -7007,24 +6605,24 @@ "picocolors": "^1.0.0", "std-env": "^3.5.0", "strip-literal": "^2.0.0", - "test-exclude": "^6.0.0", - "v8-to-istanbul": "^9.2.0" + "test-exclude": "^6.0.0" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "vitest": "1.4.0" + "vitest": "1.6.0" } }, "node_modules/@vitest/expect": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.4.0.tgz", - "integrity": "sha512-Jths0sWCJZ8BxjKe+p+eKsoqev1/T8lYcrjavEaz8auEJ4jAVY0GwW3JKmdVU4mmNPLPHixh4GNXP7GFtAiDHA==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.6.0.tgz", + "integrity": "sha512-ixEvFVQjycy/oNgHjqsL6AZCDduC+tflRluaHIzKIsdbzkLn2U/iBnVeJwB6HsIjQBdfMR8Z0tRxKUsvFJEeWQ==", "dev": true, + "license": "MIT", "dependencies": { - "@vitest/spy": "1.4.0", - "@vitest/utils": "1.4.0", + "@vitest/spy": "1.6.0", + "@vitest/utils": "1.6.0", "chai": "^4.3.10" }, "funding": { @@ -7032,12 +6630,13 @@ } }, "node_modules/@vitest/runner": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.4.0.tgz", - "integrity": "sha512-EDYVSmesqlQ4RD2VvWo3hQgTJ7ZrFQ2VSJdfiJiArkCerDAGeyF1i6dHkmySqk573jLp6d/cfqCN+7wUB5tLgg==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.6.0.tgz", + "integrity": "sha512-P4xgwPjwesuBiHisAVz/LSSZtDjOTPYZVmNAnpHHSR6ONrf8eCJOFRvUwdHn30F5M1fxhqtl7QZQUk2dprIXAg==", "dev": true, + "license": "MIT", "dependencies": { - "@vitest/utils": "1.4.0", + "@vitest/utils": "1.6.0", "p-limit": "^5.0.0", "pathe": "^1.1.1" }, @@ -7050,6 +6649,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 +6661,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" }, @@ -7073,10 +6674,11 @@ } }, "node_modules/@vitest/snapshot": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.4.0.tgz", - "integrity": "sha512-saAFnt5pPIA5qDGxOHxJ/XxhMFKkUSBJmVt5VgDsAqPTX6JP326r5C/c9UuCMPoXNzuudTPsYDZCoJ5ilpqG2A==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.6.0.tgz", + "integrity": "sha512-+Hx43f8Chus+DCmygqqfetcAZrDJwvTj0ymqjQq4CvmpKFSTVteEOBzCusu1x2tt4OJcvBflyHUE0DZSLgEMtQ==", "dev": true, + "license": "MIT", "dependencies": { "magic-string": "^0.30.5", "pathe": "^1.1.1", @@ -7091,6 +6693,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 +6706,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 +6717,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==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.6.0.tgz", + "integrity": "sha512-leUTap6B/cqi/bQkXUu6bQV5TZPx7pmMBKBQiI0rJA8c3pB56ZsaTbREnF7CJfmvAS4V2cXIBAh/3rVwrrCYgw==", "dev": true, + "license": "MIT", "dependencies": { "tinyspy": "^2.2.0" }, @@ -7131,10 +6737,11 @@ } }, "node_modules/@vitest/utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.4.0.tgz", - "integrity": "sha512-mx3Yd1/6e2Vt/PUC98DcqTirtfxUyAZ32uK82r8rZzbtBeBo+nqgnjx/LvqQdWsrvNtm14VmurNgcf4nqY5gJg==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.6.0.tgz", + "integrity": "sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw==", "dev": true, + "license": "MIT", "dependencies": { "diff-sequences": "^29.6.3", "estree-walker": "^3.0.3", @@ -7150,6 +6757,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 +6770,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 +6780,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,47 +6791,47 @@ } }, "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 - }, - "node_modules/@whatwg-node/events": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/@whatwg-node/events/-/events-0.0.3.tgz", - "integrity": "sha512-IqnKIDWfXBJkvy/k6tzskWTc2NK3LcqHlb+KHGCrjOCH4jfQckRX0NAiIcC/vIqQkzLYw2r2CTSwAxcrtcD6lA==", - "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/fetch": { - "version": "0.8.8", - "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.8.8.tgz", - "integrity": "sha512-CdcjGC2vdKhc13KKxgsc6/616BQ7ooDIgPeTuAiE8qfCnS0mGzcfCOoZXypQSz73nxI+GWc7ZReIAVhxoE1KCg==", + "version": "0.9.21", + "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.9.21.tgz", + "integrity": "sha512-Wt0jPb+04JjobK0pAAN7mEHxVHcGA9HoP3OyCsZtyAecNQeADXCZ1MihFwVwjsgaRYuGVmNlsCmLxlG6mor8Gw==", "dev": true, + "license": "MIT", "dependencies": { - "@peculiar/webcrypto": "^1.4.0", - "@whatwg-node/node-fetch": "^0.3.6", - "busboy": "^1.6.0", - "urlpattern-polyfill": "^8.0.0", - "web-streams-polyfill": "^3.2.1" + "@whatwg-node/node-fetch": "^0.5.23", + "urlpattern-polyfill": "^10.0.0" + }, + "engines": { + "node": ">=18.0.0" } }, "node_modules/@whatwg-node/node-fetch": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.3.6.tgz", - "integrity": "sha512-w9wKgDO4C95qnXZRwZTfCmLWqyRnooGjcIwG0wADWjw9/HN0p7dtvtgSvItZtUyNteEvgTrd8QojNEqV6DAGTA==", + "version": "0.5.26", + "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.5.26.tgz", + "integrity": "sha512-4jXDeZ4IH4bylZ6wu14VEx0aDXXhrN4TC279v9rPmn08g4EYekcYf8wdcOOnS9STjDkb6x77/6xBUTqxGgjr8g==", "dev": true, + "license": "MIT", "dependencies": { - "@whatwg-node/events": "^0.0.3", + "@kamilkisiela/fast-url-parser": "^1.1.4", "busboy": "^1.6.0", "fast-querystring": "^1.1.1", - "fast-url-parser": "^1.1.3", - "tslib": "^2.3.1" + "tslib": "^2.6.3" + }, + "engines": { + "node": ">=18.0.0" } }, "node_modules/@wry/caches": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@wry/caches/-/caches-1.0.1.tgz", "integrity": "sha512-bXuaUNLVVkD20wcGBWRyo7j9N3TxePEWFZj2Y+r9OoUzfqmavM84+mFykRicNsBqatba5JLay1t48wxaXaWnlA==", + "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, @@ -7233,6 +6843,7 @@ "version": "0.7.4", "resolved": "https://registry.npmjs.org/@wry/context/-/context-0.7.4.tgz", "integrity": "sha512-jmT7Sb4ZQWI5iyu3lobQxICu2nC/vbUhP0vIdd6tHC9PTfenmRmuIFqktc6GH9cgi+ZHnsLWPvfSvc4DrYmKiQ==", + "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, @@ -7244,6 +6855,7 @@ "version": "0.5.7", "resolved": "https://registry.npmjs.org/@wry/equality/-/equality-0.5.7.tgz", "integrity": "sha512-BRFORjsTuQv5gxcXsuDXx6oGRhuVsEGwZy6LOzRRfgu+eSfxbhUQ9L9YtSEIuIjY/o7g3iWFjrc5eSY1GXP2Dw==", + "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, @@ -7255,6 +6867,7 @@ "version": "0.5.0", "resolved": "https://registry.npmjs.org/@wry/trie/-/trie-0.5.0.tgz", "integrity": "sha512-FNoYzHawTMk/6KMQoEG5O4PuioX19UbwdQKF44yw0nLfOypfQdjtfZzo/UIJWAJ23sNIFbD1Ug9lbaDGMwbqQA==", + "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, @@ -7263,10 +6876,11 @@ } }, "node_modules/acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", - "devOptional": true, + "version": "8.13.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.13.0.tgz", + "integrity": "sha512-8zSiw54Oxrdym50NlZ9sUusyO1Z1ZchgRLWRaK6c86XJFClyCgFKetdowBg5bKxyp/u+CDBJG4Mpp0m3HLZl9w==", + "dev": true, + "license": "MIT", "bin": { "acorn": "bin/acorn" }, @@ -7274,20 +6888,15 @@ "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", - "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", - "devOptional": true, + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, "engines": { "node": ">=0.4.0" } @@ -7297,6 +6906,7 @@ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", "dev": true, + "license": "MIT", "dependencies": { "debug": "^4.3.4" }, @@ -7309,6 +6919,7 @@ "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", "dev": true, + "license": "MIT", "dependencies": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" @@ -7317,27 +6928,12 @@ "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", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -7347,6 +6943,7 @@ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dev": true, + "license": "MIT", "dependencies": { "type-fest": "^0.21.3" }, @@ -7361,6 +6958,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", "engines": { "node": ">=8" } @@ -7369,6 +6967,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -7382,12 +6981,14 @@ "node_modules/any-promise": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==" + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "license": "MIT" }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "license": "ISC", "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -7414,22 +7015,26 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "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", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" }, "node_modules/aria-hidden": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.4.tgz", "integrity": "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==", + "license": "MIT", "dependencies": { "tslib": "^2.0.0" }, @@ -7442,6 +7047,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" } @@ -7451,6 +7057,7 @@ "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", "dev": true, + "license": "MIT", "dependencies": { "call-bind": "^1.0.5", "is-array-buffer": "^3.0.4" @@ -7462,192 +7069,39 @@ "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", "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true, + "license": "MIT", "engines": { "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", "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/asn1": { "version": "0.2.6", "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" } }, - "node_modules/asn1js": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.5.tgz", - "integrity": "sha512-FVnvrKJwpt9LP2lAMl8qZswRNm3T4q9CON+bxldk2iwk3FFpuwhx2FfinyitizWHsVYyaY+y5JzDR0rCMV5yTQ==", - "dev": true, - "dependencies": { - "pvtsutils": "^1.3.2", - "pvutils": "^1.1.3", - "tslib": "^2.4.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, "node_modules/assert-plus": { "version": "1.0.0", "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 +7111,7 @@ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", "dev": true, + "license": "MIT", "engines": { "node": "*" } @@ -7666,27 +7121,31 @@ "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/async": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", - "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", - "dev": true + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/at-least-node": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", "dev": true, + "license": "ISC", "engines": { "node": ">= 4.0.0" } @@ -7696,6 +7155,7 @@ "resolved": "https://registry.npmjs.org/auto-bind/-/auto-bind-4.0.0.tgz", "integrity": "sha512-Hdw8qdNiqdJ8LqT0iK0sVzkFbzg6fhnQqqfWhBDxcHZvU75+B+ayzTy8x+k5Ix0Y92XOhOUlx74ps+bA6BeYMQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" }, @@ -7704,9 +7164,9 @@ } }, "node_modules/autoprefixer": { - "version": "10.4.19", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz", - "integrity": "sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==", + "version": "10.4.20", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", + "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", "funding": [ { "type": "opencollective", @@ -7721,12 +7181,13 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "browserslist": "^4.23.0", - "caniuse-lite": "^1.0.30001599", + "browserslist": "^4.23.3", + "caniuse-lite": "^1.0.30001646", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", + "picocolors": "^1.0.1", "postcss-value-parser": "^4.2.0" }, "bin": { @@ -7744,6 +7205,7 @@ "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", "dev": true, + "license": "MIT", "dependencies": { "possible-typed-array-names": "^1.0.0" }, @@ -7759,23 +7221,26 @@ "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", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.10.tgz", - "integrity": "sha512-rpIuu//y5OX6jVU+a5BCn1R5RSZYWAl2Nar76iwaOdycqb6JPxediskWFMMl7stfwNJR4b7eiQvh5fB5TEQJTQ==", + "version": "0.4.11", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz", + "integrity": "sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==", + "license": "MIT", "dependencies": { "@babel/compat-data": "^7.22.6", - "@babel/helper-define-polyfill-provider": "^0.6.1", + "@babel/helper-define-polyfill-provider": "^0.6.2", "semver": "^6.3.1" }, "peerDependencies": { @@ -7783,23 +7248,25 @@ } }, "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.4.tgz", - "integrity": "sha512-25J6I8NGfa5YkCDogHRID3fVCadIR8/pGl1/spvCkzb6lVn6SR3ojpx9nOn9iEBcUsjY24AmdKm5khcfKdylcg==", + "version": "0.10.6", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz", + "integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==", + "license": "MIT", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.1", - "core-js-compat": "^3.36.1" + "@babel/helper-define-polyfill-provider": "^0.6.2", + "core-js-compat": "^3.38.0" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.1.tgz", - "integrity": "sha512-JfTApdE++cgcTWjsiCQlLyFBMbTUft9ja17saCc93lgV33h4tuCVj7tlvu//qpLwaG+3yEz7/KhahGrUMkVq9g==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.2.tgz", + "integrity": "sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==", + "license": "MIT", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.1" + "@babel/helper-define-polyfill-provider": "^0.6.2" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" @@ -7809,13 +7276,15 @@ "version": "7.0.0-beta.0", "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-7.0.0-beta.0.tgz", "integrity": "sha512-Xj9XuRuz3nTSbaTXWv3itLOcxyF4oPD8douBBmj7U9BBC6nEBYfyOJYQMf/8PJAFotC62UY5dFfIGEPr7WswzQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/babel-preset-fbjs": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/babel-preset-fbjs/-/babel-preset-fbjs-3.4.0.tgz", "integrity": "sha512-9ywCsCvo1ojrw0b+XYk7aFvTH6D9064t0RIL1rtMf3nsa02Xw41MS7sZw216Im35xj/UY0PDBQsa1brUDDF1Ow==", "dev": true, + "license": "MIT", "dependencies": { "@babel/plugin-proposal-class-properties": "^7.0.0", "@babel/plugin-proposal-object-rest-spread": "^7.0.0", @@ -7852,12 +7321,14 @@ "node_modules/backo2": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", - "integrity": "sha512-zj6Z6M7Eq+PBZ7PQxl5NT665MvJdAkzp0f60nAJ+sLaSCBPMwVak5ZegFbgVCzFcCJTKFoMizvM5Ld7+JrRJHA==" + "integrity": "sha512-zj6Z6M7Eq+PBZ7PQxl5NT665MvJdAkzp0f60nAJ+sLaSCBPMwVak5ZegFbgVCzFcCJTKFoMizvM5Ld7+JrRJHA==", + "license": "MIT" }, "node_modules/bail": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" @@ -7866,7 +7337,8 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" }, "node_modules/base64-js": { "version": "1.5.1", @@ -7886,23 +7358,29 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/bcrypt-pbkdf": { "version": "1.0.2", "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" } }, "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "license": "MIT", "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/bl": { @@ -7910,6 +7388,7 @@ "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", "dev": true, + "license": "MIT", "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", @@ -7920,24 +7399,28 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/blob-util/-/blob-util-2.0.2.tgz", "integrity": "sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==", - "dev": true + "dev": true, + "license": "Apache-2.0" }, "node_modules/bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "license": "ISC" }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -7947,6 +7430,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", "dependencies": { "fill-range": "^7.1.1" }, @@ -7955,9 +7439,9 @@ } }, "node_modules/browserslist": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", - "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.0.tgz", + "integrity": "sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==", "funding": [ { "type": "opencollective", @@ -7972,11 +7456,12 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001587", - "electron-to-chromium": "^1.4.668", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" + "caniuse-lite": "^1.0.30001663", + "electron-to-chromium": "^1.5.28", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.0" }, "bin": { "browserslist": "cli.js" @@ -7990,6 +7475,7 @@ "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { "node-int64": "^0.4.0" } @@ -8013,6 +7499,7 @@ "url": "https://feross.org/support" } ], + "license": "MIT", "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" @@ -8023,64 +7510,11 @@ "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", "dev": true, + "license": "MIT", "engines": { "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 +7532,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" } @@ -8107,6 +7542,7 @@ "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.4.0.tgz", "integrity": "sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -8116,6 +7552,7 @@ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "dev": true, + "license": "MIT", "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -8134,6 +7571,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "license": "MIT", "engines": { "node": ">=6" } @@ -8143,6 +7581,7 @@ "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", "dev": true, + "license": "MIT", "dependencies": { "pascal-case": "^3.1.2", "tslib": "^2.0.3" @@ -8152,6 +7591,7 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "license": "MIT", "engines": { "node": ">=10" }, @@ -8163,14 +7603,15 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "license": "MIT", "engines": { "node": ">= 6" } }, "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.30001669", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001669.tgz", + "integrity": "sha512-DlWzFDJqstqtIVx1zeSpIMLjunf5SmwOw0N2Ck/QSQdS8PLS4+9HrLaYei4w8BIAL7IB/UEDu889d8vhCTPA0w==", "funding": [ { "type": "opencollective", @@ -8184,13 +7625,15 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ] + ], + "license": "CC-BY-4.0" }, "node_modules/capital-case": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/capital-case/-/capital-case-1.0.4.tgz", "integrity": "sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==", "dev": true, + "license": "MIT", "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3", @@ -8201,22 +7644,25 @@ "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", "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" } }, "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 +7670,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" @@ -8235,6 +7681,7 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -8251,6 +7698,7 @@ "resolved": "https://registry.npmjs.org/change-case/-/change-case-4.1.2.tgz", "integrity": "sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A==", "dev": true, + "license": "MIT", "dependencies": { "camel-case": "^4.1.2", "capital-case": "^1.0.4", @@ -8271,6 +7719,7 @@ "resolved": "https://registry.npmjs.org/change-case-all/-/change-case-all-1.0.15.tgz", "integrity": "sha512-3+GIFhk3sNuvFAJKU46o26OdzudQlPNBCu1ZQi3cMeMHhty1bhDxu2WrEilVNYaGvqUtR1VSigFcJOiS13dRhQ==", "dev": true, + "license": "MIT", "dependencies": { "change-case": "^4.1.2", "is-lower-case": "^2.0.2", @@ -8288,6 +7737,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" @@ -8297,6 +7747,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" @@ -8306,6 +7757,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" @@ -8315,6 +7767,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" @@ -8324,12 +7777,14 @@ "version": "0.7.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/charenc": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", + "license": "BSD-3-Clause", "engines": { "node": "*" } @@ -8339,6 +7794,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" }, @@ -8351,20 +7807,16 @@ "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", "integrity": "sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8.0" } }, "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "license": "MIT", "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -8377,6 +7829,9 @@ "engines": { "node": ">= 8.10.0" }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, "optionalDependencies": { "fsevents": "~2.3.2" } @@ -8392,6 +7847,7 @@ "url": "https://github.com/sponsors/sibiraj-s" } ], + "license": "MIT", "engines": { "node": ">=8" } @@ -8400,6 +7856,7 @@ "version": "0.7.0", "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.0.tgz", "integrity": "sha512-jFI8IQw4hczaL4ALINxqLEXQbWcNjoSkloa4IaufXCJr6QawJyw7tuRysRsrE8w2p/4gGaxKIt/hX3qz/IbD1A==", + "license": "Apache-2.0", "dependencies": { "clsx": "2.0.0" }, @@ -8411,6 +7868,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz", "integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==", + "license": "MIT", "engines": { "node": ">=6" } @@ -8418,13 +7876,15 @@ "node_modules/classnames": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", - "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==" + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", + "license": "MIT" }, "node_modules/clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -8434,6 +7894,7 @@ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", "dev": true, + "license": "MIT", "dependencies": { "restore-cursor": "^3.1.0" }, @@ -8446,6 +7907,7 @@ "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" }, @@ -8454,10 +7916,11 @@ } }, "node_modules/cli-table3": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz", - "integrity": "sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==", + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", + "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", "dev": true, + "license": "MIT", "dependencies": { "string-width": "^4.2.0" }, @@ -8473,6 +7936,7 @@ "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", "dev": true, + "license": "MIT", "dependencies": { "slice-ansi": "^3.0.0", "string-width": "^4.2.0" @@ -8489,6 +7953,7 @@ "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", "dev": true, + "license": "ISC", "engines": { "node": ">= 10" } @@ -8496,13 +7961,15 @@ "node_modules/client-only": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", - "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "license": "MIT" }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, + "license": "ISC", "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", @@ -8517,6 +7984,7 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -8534,14 +8002,16 @@ "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8" } }, "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" } @@ -8550,6 +8020,7 @@ "version": "0.0.14", "resolved": "https://registry.npmjs.org/cm6-graphql/-/cm6-graphql-0.0.14.tgz", "integrity": "sha512-iiRnTVcALKSbxp7ExenZm5qC56Tjgc7wahMPLtj+XFTOIldrrwMDPTv2NGF/WB3x+PUi568w5pB73lpH9cSQGw==", + "license": "MIT", "dependencies": { "graphql-language-service": "^5.2.0" }, @@ -8567,6 +8038,7 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/cm6-theme-basic-light/-/cm6-theme-basic-light-0.2.0.tgz", "integrity": "sha512-1prg2gv44sYfpHscP26uLT/ePrh0mlmVwMSoSd3zYKQ92Ab3jPRLzyCnpyOCQLJbK+YdNs4HvMRqMNYdy4pMhA==", + "license": "MIT", "peerDependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.0.0", @@ -8574,690 +8046,1050 @@ "@lezer/highlight": "^1.0.0" } }, - "node_modules/codemirror": { - "version": "5.65.16", - "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.16.tgz", - "integrity": "sha512-br21LjYmSlVL0vFCPWPfhzUCT34FM/pAdK7rRIZwa0rrtrIdotvP4Oh4GUHsu2E3IrQMCfRkL/fN3ytMNxVQvg==" - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "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": { - "color-name": "~1.1.4" + "@radix-ui/react-dialog": "1.0.5", + "@radix-ui/react-primitive": "1.0.3" }, - "engines": { - "node": ">=7.0.0" + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" } }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "dev": true - }, - "node_modules/colors-named": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/colors-named/-/colors-named-1.0.2.tgz", - "integrity": "sha512-2ANq2r393PV9njYUD66UdfBcxR1slMqRA3QRTWgCx49JoCJ+kOhyfbQYxKJbPZQIhZUcNjVOs5AlyY1WwXec3w==", - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://jaywcjlove.github.io/#/sponsor" + "node_modules/cmdk/node_modules/@radix-ui/primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.1.tgz", + "integrity": "sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" } }, - "node_modules/colors-named-hex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/colors-named-hex/-/colors-named-hex-1.0.2.tgz", - "integrity": "sha512-k6kq1e1pUCQvSVwIaGFq2l0LrkAPQZWyeuZn1Z8nOiYSEZiKoFj4qx690h2Kd34DFl9Me0gKS6MUwAMBJj8nuA==", - "engines": { - "node": ">=14.16" + "node_modules/cmdk/node_modules/@radix-ui/react-compose-refs": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz", + "integrity": "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10" }, - "funding": { - "url": "https://jaywcjlove.github.io/#/sponsor" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, + "node_modules/cmdk/node_modules/@radix-ui/react-context": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.0.1.tgz", + "integrity": "sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==", + "license": "MIT", "dependencies": { - "delayed-stream": "~1.0.0" + "@babel/runtime": "^7.13.10" }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/comma-separated-tokens": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", - "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/commander": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", - "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/common-tags": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", - "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", - "dev": true, - "engines": { - "node": ">=4.0.0" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "node_modules/constant-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/constant-case/-/constant-case-3.0.4.tgz", - "integrity": "sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==", - "dev": true, + "node_modules/cmdk/node_modules/@radix-ui/react-dialog": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.0.5.tgz", + "integrity": "sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q==", + "license": "MIT", "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3", - "upper-case": "^2.0.2" + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-dismissable-layer": "1.0.5", + "@radix-ui/react-focus-guards": "1.0.1", + "@radix-ui/react-focus-scope": "1.0.4", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-portal": "1.0.4", + "@radix-ui/react-presence": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-slot": "1.0.2", + "@radix-ui/react-use-controllable-state": "1.0.1", + "aria-hidden": "^1.1.1", + "react-remove-scroll": "2.5.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" - }, - "node_modules/copy-to-clipboard": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz", - "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==", + "node_modules/cmdk/node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.5.tgz", + "integrity": "sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==", + "license": "MIT", "dependencies": { - "toggle-selection": "^1.0.6" + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-escape-keydown": "1.0.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/core-js-compat": { - "version": "3.36.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.36.1.tgz", - "integrity": "sha512-Dk997v9ZCt3X/npqzyGdTlq6t7lDBhZwGvV94PKzDArjp7BTRm7WlDAXYd/OWdeFHO8OChQYRJNJvUCqCbrtKA==", + "node_modules/cmdk/node_modules/@radix-ui/react-focus-guards": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.1.tgz", + "integrity": "sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==", + "license": "MIT", "dependencies": { - "browserslist": "^4.23.0" + "@babel/runtime": "^7.13.10" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/core-util-is": { - "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 - }, - "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==", + "node_modules/cmdk/node_modules/@radix-ui/react-focus-scope": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.4.tgz", + "integrity": "sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA==", + "license": "MIT", "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" + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1" }, "peerDependencies": { - "typescript": ">=4.9.5" + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" }, "peerDependenciesMeta": { - "typescript": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { "optional": true } } }, - "node_modules/create-require": { - "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 - }, - "node_modules/crelt": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", - "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==" - }, - "node_modules/cross-fetch": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", - "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", + "node_modules/cmdk/node_modules/@radix-ui/react-id": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.1.tgz", + "integrity": "sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==", + "license": "MIT", "dependencies": { - "node-fetch": "^2.6.12" + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-layout-effect": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/cross-inspect": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/cross-inspect/-/cross-inspect-1.0.0.tgz", - "integrity": "sha512-4PFfn4b5ZN6FMNGSZlyb7wUhuN8wvj8t/VQHZdM4JsDcruGJ8L2kf9zao98QIrBPFCpdk27qst/AGTl7pL3ypQ==", - "dev": true, + "node_modules/cmdk/node_modules/@radix-ui/react-portal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.4.tgz", + "integrity": "sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==", + "license": "MIT", "dependencies": { - "tslib": "^2.4.0" + "@babel/runtime": "^7.13.10", + "@radix-ui/react-primitive": "1.0.3" }, - "engines": { - "node": ">=16.0.0" + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "node_modules/cmdk/node_modules/@radix-ui/react-presence": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.0.1.tgz", + "integrity": "sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==", + "license": "MIT", "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-use-layout-effect": "1.0.1" }, - "engines": { - "node": ">= 8" + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/crypt": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", - "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", - "engines": { - "node": "*" + "node_modules/cmdk/node_modules/@radix-ui/react-primitive": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz", + "integrity": "sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-slot": "1.0.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, - "node_modules/css-select": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", - "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "node_modules/cmdk/node_modules/@radix-ui/react-slot": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz", + "integrity": "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==", + "license": "MIT", "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.1.0", - "domhandler": "^5.0.2", - "domutils": "^3.0.1", - "nth-check": "^2.0.1" + "@babel/runtime": "^7.13.10", + "@radix-ui/react-compose-refs": "1.0.1" }, - "funding": { - "url": "https://github.com/sponsors/fb55" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/css-tree": { - "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==", + "node_modules/cmdk/node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz", + "integrity": "sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==", + "license": "MIT", "dependencies": { - "mdn-data": "2.0.30", - "source-map-js": "^1.0.1" + "@babel/runtime": "^7.13.10" }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", - "engines": { - "node": ">= 6" + "node_modules/cmdk/node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.1.tgz", + "integrity": "sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-callback-ref": "1.0.1" }, - "funding": { - "url": "https://github.com/sponsors/fb55" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "bin": { - "cssesc": "bin/cssesc" + "node_modules/cmdk/node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.3.tgz", + "integrity": "sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-use-callback-ref": "1.0.1" }, - "engines": { - "node": ">=4" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "node_modules/csso": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", - "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", + "node_modules/cmdk/node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz", + "integrity": "sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==", + "license": "MIT", "dependencies": { - "css-tree": "~2.2.0" + "@babel/runtime": "^7.13.10" }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", - "npm": ">=7.0.0" + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "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==", + "node_modules/cmdk/node_modules/react-remove-scroll": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz", + "integrity": "sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==", + "license": "MIT", "dependencies": { - "mdn-data": "2.0.28", - "source-map-js": "^1.0.1" + "react-remove-scroll-bar": "^2.3.3", + "react-style-singleton": "^2.2.1", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.0", + "use-sidecar": "^1.1.2" }, "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", - "npm": ">=7.0.0" + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "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==" + "node_modules/codemirror": { + "version": "5.65.18", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.18.tgz", + "integrity": "sha512-Gaz4gHnkbHMGgahNt3CA5HBk5lLQBqmD/pBgeB4kQU6OedZmqMBjlRF0LSrp2tJ4wlLNPm2FfaUd1pDy0mdlpA==", + "license": "MIT" }, - "node_modules/cssstyle": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.0.1.tgz", - "integrity": "sha512-8ZYiJ3A/3OkDd093CBT/0UKDWry7ak4BdPTFP2+QEP7cmhouyq/Up709ASSj2cK02BbZiMgk7kYjZNS4QP5qrQ==", - "dev": true, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", "dependencies": { - "rrweb-cssom": "^0.6.0" + "color-name": "~1.1.4" }, "engines": { - "node": ">=18" + "node": ">=7.0.0" } }, - "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" }, - "node_modules/cypress": { - "version": "13.7.3", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.7.3.tgz", - "integrity": "sha512-uoecY6FTCAuIEqLUYkTrxamDBjMHTYak/1O7jtgwboHiTnS1NaMOoR08KcTrbRZFCBvYOiS4tEkQRmsV+xcrag==", + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", "dev": true, - "hasInstallScript": true, - "dependencies": { - "@cypress/request": "^3.0.0", - "@cypress/xvfb": "^1.2.4", - "@types/sinonjs__fake-timers": "8.1.1", - "@types/sizzle": "^2.3.2", - "arch": "^2.2.0", - "blob-util": "^2.0.2", - "bluebird": "^3.7.2", - "buffer": "^5.7.1", - "cachedir": "^2.3.0", - "chalk": "^4.1.0", - "check-more-types": "^2.24.0", - "cli-cursor": "^3.1.0", - "cli-table3": "~0.6.1", - "commander": "^6.2.1", - "common-tags": "^1.8.0", - "dayjs": "^1.10.4", - "debug": "^4.3.4", - "enquirer": "^2.3.6", - "eventemitter2": "6.4.7", - "execa": "4.1.0", - "executable": "^4.1.1", - "extract-zip": "2.0.1", - "figures": "^3.2.0", - "fs-extra": "^9.1.0", - "getos": "^3.2.1", - "is-ci": "^3.0.1", - "is-installed-globally": "~0.4.0", - "lazy-ass": "^1.6.0", - "listr2": "^3.8.3", - "lodash": "^4.17.21", - "log-symbols": "^4.0.0", - "minimist": "^1.2.8", - "ospath": "^1.2.2", - "pretty-bytes": "^5.6.0", - "process": "^0.11.10", - "proxy-from-env": "1.0.0", - "request-progress": "^3.0.0", - "semver": "^7.5.3", - "supports-color": "^8.1.1", - "tmp": "~0.2.1", - "untildify": "^4.0.0", - "yauzl": "^2.10.0" - }, - "bin": { - "cypress": "bin/cypress" - }, + "license": "MIT" + }, + "node_modules/colors-named": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/colors-named/-/colors-named-1.0.2.tgz", + "integrity": "sha512-2ANq2r393PV9njYUD66UdfBcxR1slMqRA3QRTWgCx49JoCJ+kOhyfbQYxKJbPZQIhZUcNjVOs5AlyY1WwXec3w==", + "license": "MIT", "engines": { - "node": "^16.0.0 || ^18.0.0 || >=20.0.0" + "node": ">=14.16" + }, + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" } }, - "node_modules/cypress/node_modules/listr2": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.14.0.tgz", - "integrity": "sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g==", - "dev": true, - "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.1", - "through": "^2.3.8", - "wrap-ansi": "^7.0.0" - }, + "node_modules/colors-named-hex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/colors-named-hex/-/colors-named-hex-1.0.2.tgz", + "integrity": "sha512-k6kq1e1pUCQvSVwIaGFq2l0LrkAPQZWyeuZn1Z8nOiYSEZiKoFj4qx690h2Kd34DFl9Me0gKS6MUwAMBJj8nuA==", + "license": "MIT", "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "enquirer": ">= 2.3.0 < 3" + "node": ">=14.16" }, - "peerDependenciesMeta": { - "enquirer": { - "optional": true - } + "funding": { + "url": "https://jaywcjlove.github.io/#/sponsor" } }, - "node_modules/cypress/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==", + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dev": true, + "license": "MIT", "dependencies": { - "yallist": "^4.0.0" + "delayed-stream": "~1.0.0" }, "engines": { - "node": ">=10" + "node": ">= 0.8" } }, - "node_modules/cypress/node_modules/proxy-from-env": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", - "integrity": "sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==", - "dev": true + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } }, - "node_modules/cypress/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "node_modules/commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, + "license": "MIT", "engines": { - "node": ">=10" + "node": ">= 6" } }, - "node_modules/cypress/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "node_modules/common-tags": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", + "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=4.0.0" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "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", + "integrity": "sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3", + "upper-case": "^2.0.2" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "license": "MIT" + }, + "node_modules/copy-to-clipboard": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz", + "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==", + "license": "MIT", + "dependencies": { + "toggle-selection": "^1.0.6" + } + }, + "node_modules/core-js-compat": { + "version": "3.38.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.38.1.tgz", + "integrity": "sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw==", + "license": "MIT", + "dependencies": { + "browserslist": "^4.23.3" }, "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/core-js" } }, - "node_modules/cypress/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==", + "node_modules/core-util-is": { + "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, + "license": "MIT" + }, + "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==", + "license": "MIT", "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.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/chalk/wrap-ansi?sponsor=1" + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/cypress/node_modules/yallist": { + "node_modules/crelt": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==", + "license": "MIT" + }, + "node_modules/cross-fetch": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", + "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", + "license": "MIT", + "dependencies": { + "node-fetch": "^2.6.12" + } }, - "node_modules/d3-array": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", - "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "node_modules/cross-inspect": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cross-inspect/-/cross-inspect-1.0.1.tgz", + "integrity": "sha512-Pcw1JTvZLSJH83iiGWt6fRcT+BjZlCDRVwYLbUcHzv/CRpB7r0MlSrGbIyQvVSNyGnbt7G4AXuyCiDR3POvZ1A==", + "dev": true, + "license": "MIT", "dependencies": { - "internmap": "1 - 2" + "tslib": "^2.4.0" }, "engines": { - "node": ">=12" + "node": ">=16.0.0" } }, - "node_modules/d3-color": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", - "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", - "engines": { - "node": ">=12" + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" } }, - "node_modules/d3-ease": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", - "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "node_modules/crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", + "license": "BSD-3-Clause", "engines": { - "node": ">=12" + "node": "*" } }, - "node_modules/d3-format": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", - "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", - "engines": { - "node": ">=12" + "node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" } }, - "node_modules/d3-interpolate": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", - "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "node_modules/css-tree": { + "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": { - "d3-color": "1 - 3" + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" }, "engines": { - "node": ">=12" + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" } }, - "node_modules/d3-path": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", - "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "license": "BSD-2-Clause", "engines": { - "node": ">=12" + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" } }, - "node_modules/d3-scale": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", - "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", - "dependencies": { - "d3-array": "2.10.0 - 3", - "d3-format": "1 - 3", - "d3-interpolate": "1.2.0 - 3", - "d3-time": "2.1.1 - 3", - "d3-time-format": "2 - 4" + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" }, "engines": { - "node": ">=12" + "node": ">=4" } }, - "node_modules/d3-shape": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", - "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "node_modules/csso": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", + "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", + "license": "MIT", "dependencies": { - "d3-path": "^3.1.0" + "css-tree": "~2.2.0" }, "engines": { - "node": ">=12" + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" } }, - "node_modules/d3-time": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", - "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "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": { - "d3-array": "2 - 3" + "mdn-data": "2.0.28", + "source-map-js": "^1.0.1" }, "engines": { - "node": ">=12" + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", + "npm": ">=7.0.0" } }, - "node_modules/d3-time-format": { + "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/cssstyle": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", - "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.1.0.tgz", + "integrity": "sha512-h66W1URKpBS5YMI/V8PyXvTMFT8SupJ1IzoIV8IeBC/ji8WVmrO8dGlTi+2dh6whmdk6BiKJLD/ZBkhWbcg6nA==", + "dev": true, + "license": "MIT", "dependencies": { - "d3-time": "1 - 3" + "rrweb-cssom": "^0.7.1" }, "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/d3-timer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", - "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", - "engines": { - "node": ">=12" - } + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" }, - "node_modules/dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "node_modules/cypress": { + "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": { - "assert-plus": "^1.0.0" + "@cypress/request": "^3.0.4", + "@cypress/xvfb": "^1.2.4", + "@types/sinonjs__fake-timers": "8.1.1", + "@types/sizzle": "^2.3.2", + "arch": "^2.2.0", + "blob-util": "^2.0.2", + "bluebird": "^3.7.2", + "buffer": "^5.7.1", + "cachedir": "^2.3.0", + "chalk": "^4.1.0", + "check-more-types": "^2.24.0", + "cli-cursor": "^3.1.0", + "cli-table3": "~0.6.1", + "commander": "^6.2.1", + "common-tags": "^1.8.0", + "dayjs": "^1.10.4", + "debug": "^4.3.4", + "enquirer": "^2.3.6", + "eventemitter2": "6.4.7", + "execa": "4.1.0", + "executable": "^4.1.1", + "extract-zip": "2.0.1", + "figures": "^3.2.0", + "fs-extra": "^9.1.0", + "getos": "^3.2.1", + "is-ci": "^3.0.1", + "is-installed-globally": "~0.4.0", + "lazy-ass": "^1.6.0", + "listr2": "^3.8.3", + "lodash": "^4.17.21", + "log-symbols": "^4.0.0", + "minimist": "^1.2.8", + "ospath": "^1.2.2", + "pretty-bytes": "^5.6.0", + "process": "^0.11.10", + "proxy-from-env": "1.0.0", + "request-progress": "^3.0.0", + "semver": "^7.5.3", + "supports-color": "^8.1.1", + "tmp": "~0.2.3", + "untildify": "^4.0.0", + "yauzl": "^2.10.0" + }, + "bin": { + "cypress": "bin/cypress" }, "engines": { - "node": ">=0.10" + "node": "^16.0.0 || ^18.0.0 || >=20.0.0" } }, - "node_modules/data-urls": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", - "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", + "node_modules/cypress/node_modules/listr2": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.14.0.tgz", + "integrity": "sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g==", "dev": true, + "license": "MIT", "dependencies": { - "whatwg-mimetype": "^4.0.0", - "whatwg-url": "^14.0.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.1", + "through": "^2.3.8", + "wrap-ansi": "^7.0.0" }, "engines": { - "node": ">=18" + "node": ">=10.0.0" + }, + "peerDependencies": { + "enquirer": ">= 2.3.0 < 3" + }, + "peerDependenciesMeta": { + "enquirer": { + "optional": true + } } }, - "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==", + "node_modules/cypress/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, - "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" + "license": "ISC", + "bin": { + "semver": "bin/semver.js" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=10" } }, - "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==", + "node_modules/cypress/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" + "has-flag": "^4.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "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==", + "node_modules/cypress/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, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.6", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "license": "ISC", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/dashdash": { + "version": "1.14.1", + "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" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/data-urls": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", + "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0" + }, + "engines": { + "node": ">=18" } }, "node_modules/dataloader": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/dataloader/-/dataloader-2.2.2.tgz", "integrity": "sha512-8YnDaaf7N3k/q5HnTJVuzSyLETjoZjVmHc4AeKAzOvKHEFQKcn64OKBfzHYtE9zGjctNM7V9I0MfnUVLpi7M5g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/date-fns": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/kossnocorp" } }, "node_modules/dayjs": { - "version": "1.11.10", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz", - "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==", - "dev": true + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", + "dev": true, + "license": "MIT" }, "node_modules/debounce": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==", - "dev": true + "dev": true, + "license": "MIT" + }, + "node_modules/debounce-promise": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/debounce-promise/-/debounce-promise-3.1.2.tgz", + "integrity": "sha512-rZHcgBkbYavBeD9ej6sP56XfG53d51CD4dnaw989YX/nZ/ZJfgRx/9ePKmTNiUiyQvh4mtrMoS3OAWW+yoYtpg==", + "license": "MIT" }, "node_modules/debug": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", - "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -9273,6 +9105,7 @@ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -9281,17 +9114,20 @@ "version": "10.4.3", "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/decimal.js-light": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", - "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==" + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==", + "license": "MIT" }, "node_modules/decode-named-character-reference": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", + "license": "MIT", "dependencies": { "character-entities": "^2.0.0" }, @@ -9304,15 +9140,17 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.4.1.tgz", "integrity": "sha512-+8VxcR21HhTy8nOt6jf20w0c9CADrw1O8d+VZ/YzzCt4bJ3uBjw+D1q2osAB8RnpwwaeYBxy0HyKQxD5JBMuuQ==", + "license": "MIT", "engines": { "node": ">=14.16" } }, "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 +9163,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,16 +9191,11 @@ "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", "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -9371,6 +9205,7 @@ "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", "dev": true, + "license": "MIT", "dependencies": { "clone": "^1.0.2" }, @@ -9383,6 +9218,7 @@ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dev": true, + "license": "MIT", "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -9400,6 +9236,7 @@ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dev": true, + "license": "MIT", "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", @@ -9417,6 +9254,7 @@ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.4.0" } @@ -9426,6 +9264,7 @@ "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz", "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6.0" } @@ -9434,6 +9273,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", "engines": { "node": ">=6" } @@ -9443,33 +9283,22 @@ "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/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, - "bin": { - "detect-libc": "bin/detect-libc.js" - }, - "engines": { - "node": ">=0.10" - } - }, "node_modules/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==" + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", + "license": "MIT" }, "node_modules/devlop": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "license": "MIT", "dependencies": { "dequal": "^2.0.0" }, @@ -9481,13 +9310,14 @@ "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "license": "Apache-2.0" }, "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "devOptional": true, + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" } @@ -9495,13 +9325,15 @@ "node_modules/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==" + "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==", + "license": "Apache-2.0" }, "node_modules/diff-sequences": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", "dev": true, + "license": "MIT", "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } @@ -9511,6 +9343,7 @@ "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "dev": true, + "license": "MIT", "dependencies": { "path-type": "^4.0.0" }, @@ -9521,30 +9354,21 @@ "node_modules/dlv": { "version": "1.1.3", "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" - } + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "license": "MIT" }, "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", "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.8.7", "csstype": "^3.0.2" @@ -9554,6 +9378,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "license": "MIT", "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", @@ -9572,12 +9397,14 @@ "type": "github", "url": "https://github.com/sponsors/fb55" } - ] + ], + "license": "BSD-2-Clause" }, "node_modules/domhandler": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "license": "BSD-2-Clause", "dependencies": { "domelementtype": "^2.3.0" }, @@ -9592,6 +9419,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "license": "BSD-2-Clause", "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", @@ -9605,6 +9433,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "license": "MIT", "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3" @@ -9615,6 +9444,7 @@ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=12" }, @@ -9635,33 +9465,38 @@ "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" }, "node_modules/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, + "license": "MIT", "dependencies": { "jsbn": "~0.1.0", "safer-buffer": "^2.1.0" } }, "node_modules/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==" + "version": "1.5.40", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.40.tgz", + "integrity": "sha512-LYm78o6if4zTasnYclgQzxEcgMoIcybWOhkATWepN95uwVVWV0/IW10v+2sIeHE+bIYWipLneTftVyQm45UY7g==", + "license": "ISC" }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" }, "node_modules/end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", "dev": true, + "license": "MIT", "dependencies": { "once": "^1.4.0" } @@ -9671,6 +9506,7 @@ "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", "dev": true, + "license": "MIT", "dependencies": { "ansi-colors": "^4.1.1", "strip-ansi": "^6.0.1" @@ -9683,6 +9519,7 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", "engines": { "node": ">=0.12" }, @@ -9707,75 +9544,17 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "license": "MIT", "dependencies": { "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", "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", "dev": true, + "license": "MIT", "dependencies": { "get-intrinsic": "^1.2.4" }, @@ -9788,6 +9567,7 @@ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -9797,6 +9577,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 +9593,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", @@ -9928,1154 +9632,981 @@ } }, "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", "engines": { "node": ">=6" } }, "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, + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "license": "MIT", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.8.0" } }, - "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" - }, + "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==", + "license": "MIT", "funding": { - "url": "https://opencollective.com/eslint" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "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/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==", + "license": "MIT" + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "license": "BSD-2-Clause", "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==", + "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, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } + "license": "MIT" + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "license": "MIT" }, - "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/execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", "dev": true, + "license": "MIT", "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" + "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" - } - }, - "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/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, - "bin": { - "eslint-config-prettier": "bin/cli.js" }, - "peerDependencies": { - "eslint": ">=7.0.0" - } - }, - "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==", - "dev": true, - "dependencies": { - "debug": "^3.2.7", - "is-core-module": "^2.13.0", - "resolve": "^1.22.4" - } - }, - "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==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "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/executable": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz", + "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", "dev": true, + "license": "MIT", "dependencies": { - "debug": "^3.2.7" + "pify": "^2.2.0" }, "engines": { "node": ">=4" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } } }, - "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==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" }, - "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==", + "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, + "license": "MIT", "dependencies": { - "globals": "^13.20.0" + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" }, - "peerDependencies": { - "eslint": ">= 3.2.1" + "engines": { + "node": ">=4" } }, - "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/external-editor/node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", "dev": true, + "license": "MIT", "dependencies": { - "type-fest": "^0.20.2" + "os-tmpdir": "~1.0.2" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.6.0" } }, - "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/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, + "license": "MIT", "engines": { - "node": ">=10" + "node": "^12.20 || >= 14.13" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/jaydenseric" } }, - "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==", + "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, + "license": "BSD-2-Clause", "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" + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" }, - "peerDependencies": { - "eslint": ">=8" - } - }, - "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==", - "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" + "bin": { + "extract-zip": "cli.js" }, "engines": { - "node": ">=4" + "node": ">= 10.17.0" }, - "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + "optionalDependencies": { + "@types/yauzl": "^2.9.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/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/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/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, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } + "license": "MIT" }, - "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/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, - "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" - }, + "license": "MIT" + }, + "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==", + "license": "MIT", "engines": { - "node": ">=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=7.0.0" + "node": ">=6.0.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==", - "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==", + "license": "MIT", "dependencies": { - "type-fest": "^0.20.2" + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=8.6.0" } }, - "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==", + "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, + "license": "MIT", "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" + "fast-decode-uri-component": "^1.0.1" + } + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" } }, - "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==", + "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, + "license": "Apache-2.0", "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" + "bser": "2.1.1" } }, - "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==", + "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, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "license": "MIT", + "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" } }, - "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/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, + "license": "MIT" }, - "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/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, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" + "license": "MIT", + "dependencies": { + "node-fetch": "^2.6.12" } }, - "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/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, + "license": "MIT", "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" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + "pend": "~1.2.0" } }, - "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/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", "dev": true, + "license": "MIT", "dependencies": { - "esutils": "^2.0.2" + "escape-string-regexp": "^1.0.5" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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, + "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==", + "license": "MIT", "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" + "to-regex-range": "^5.0.1" }, - "bin": { - "resolve": "bin/resolve" + "engines": { + "node": ">=8" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=14.16" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "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, + "license": "MIT", "dependencies": { - "eslint-rule-composer": "^0.3.0" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "@typescript-eslint/eslint-plugin": "6 - 7", - "eslint": "8" - }, - "peerDependenciesMeta": { - "@typescript-eslint/eslint-plugin": { - "optional": true - } + "node": ">=8" } }, - "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==", + "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": ">=4.0.0" + "license": "MIT", + "dependencies": { + "is-callable": "^1.1.3" } }, - "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==", - "dev": true, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "license": "ISC", "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=14" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/isaacs" } }, - "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==", - "dev": true, + "node_modules/foreground-child/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==", + "license": "ISC", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=14" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/isaacs" } }, - "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/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": { - "is-glob": "^4.0.3" - }, + "license": "Apache-2.0", "engines": { - "node": ">=10.13.0" + "node": "*" } }, - "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/form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", "dev": true, + "license": "MIT", "dependencies": { - "type-fest": "^0.20.2" + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 6" } }, - "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==", - "dev": true, + "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==", + "license": "MIT", "engines": { - "node": ">=10" + "node": "*" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "patreon", + "url": "https://github.com/sponsors/rawify" } }, - "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==", - "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==", + "license": "MIT", "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" + "@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" }, - "funding": { - "url": "https://opencollective.com/eslint" + "peerDependencies": { + "react": ">=16.8 || ^17.0.0 || ^18.0.0", + "react-dom": ">=16.8 || ^17.0.0 || ^18.0.0" } }, - "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, + "node_modules/framesync": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/framesync/-/framesync-6.0.1.tgz", + "integrity": "sha512-fUY88kXvGiIItgNC7wcTOl0SNRCVXMKSWW2Yzfmn7EKNc+MpCzcz9DhdHcdjbrtN3c6R4H5dTY2jiCpPdysEjA==", + "license": "MIT", "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" + "tslib": "^2.1.0" } }, - "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/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, + "license": "MIT", "dependencies": { - "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": ">=4.0" + "node": ">=10" } }, - "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/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, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=4.0" + "node": "^8.16.0 || ^10.6.0 || >=11.0.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==", + "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==", + "license": "MIT", "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://github.com/sponsors/ljharb" } }, - "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/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, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "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==", + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=6.9.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==", + "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, - "license": "MIT" + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } }, - "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/get-east-asian-width": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", + "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", "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" - }, + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/executable": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz", - "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", + "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, - "dependencies": { - "pify": "^2.2.0" - }, + "license": "MIT", "engines": { - "node": ">=4" + "node": "*" } }, - "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/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, + "license": "MIT", "dependencies": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" + "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": ">=4" - } - }, - "node_modules/external-editor/node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "dependencies": { - "os-tmpdir": "~1.0.2" + "node": ">= 0.4" }, - "engines": { - "node": ">=0.6.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "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, + "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==", + "license": "MIT", "engines": { - "node": "^12.20 || >= 14.13" - }, - "funding": { - "url": "https://github.com/sponsors/jaydenseric" + "node": ">=6" } }, - "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/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, + "license": "MIT", "dependencies": { - "debug": "^4.1.1", - "get-stream": "^5.1.0", - "yauzl": "^2.10.0" - }, - "bin": { - "extract-zip": "cli.js" + "pump": "^3.0.0" }, "engines": { - "node": ">= 10.17.0" + "node": ">=8" }, - "optionalDependencies": { - "@types/yauzl": "^2.9.1" - } - }, - "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==", - "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" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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/get-value": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-3.0.1.tgz", + "integrity": "sha512-mKZj9JLQrwMBtj5wxi6MH8Z5eSKaERpAwjg43dPtlGI1ZVEgH/qC7T8/6R2OBSUA+zzHBZgICsVJaEIV2tKTDA==", + "license": "MIT", "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" + "isobject": "^3.0.1" }, "engines": { - "node": ">=8.6.0" + "node": ">=6.0" } }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "node_modules/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/getos": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/getos/-/getos-3.2.1.tgz", + "integrity": "sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==", "dev": true, + "license": "MIT", "dependencies": { - "fast-decode-uri-component": "^1.0.1" + "async": "^3.2.0" } }, - "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==", + "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": { - "punycode": "^1.3.2" + "assert-plus": "^1.0.0" } }, - "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/gitdiff-parser": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/gitdiff-parser/-/gitdiff-parser-0.3.1.tgz", + "integrity": "sha512-YQJnY8aew65id8okGxKCksH3efDCJ9HzV7M9rsvd65habf39Pkh4cgYJ27AaoDMqo1X98pgNJhNMrm/kpV7UVQ==", + "license": "MIT" }, - "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==", + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, + "license": "ISC", "dependencies": { - "bser": "2.1.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/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, + "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==", + "license": "ISC", "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" + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" } }, - "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==", + "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, + "license": "MIT", "dependencies": { - "node-fetch": "^2.6.12" + "ini": "2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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": { - "pend": "~1.2.0" + "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==", + "license": "MIT", + "engines": { + "node": ">=4" } }, - "node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, + "license": "MIT", "dependencies": { - "escape-string-regexp": "^1.0.5" + "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": ">=8" + "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "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": ">=0.8.0" - } + "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==", + "license": "MIT" }, - "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/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", "dev": true, + "license": "MIT", "dependencies": { - "flat-cache": "^3.0.4" + "get-intrinsic": "^1.1.3" }, - "engines": { - "node": "^10.12.0 || >=12.0.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "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/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, + "license": "ISC" + }, + "node_modules/graphiql": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/graphiql/-/graphiql-3.7.1.tgz", + "integrity": "sha512-kmummedOrFYs0BI5evrVY0AerOYlaMt/Sc/e+Sta1x8X6vEMYWNeUUz/kKF2NQT5BcsR3FnNdFt1Gk2QMgueGQ==", + "license": "MIT", "dependencies": { - "to-regex-range": "^5.0.1" + "@graphiql/react": "^0.26.2" }, - "engines": { - "node": ">=8" + "peerDependencies": { + "graphql": "^15.5.0 || ^16.0.0 || ^17.0.0-alpha.2", + "react": "^16.8.0 || ^17 || ^18", + "react-dom": "^16.8.0 || ^17 || ^18" } }, - "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": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node_modules/graphiql-explorer": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/graphiql-explorer/-/graphiql-explorer-0.9.0.tgz", + "integrity": "sha512-fZC/wsuatqiQDO2otchxriFO0LaWIo/ovF/CQJ1yOudmY0P7pzDiP+l9CEHUiWbizk3e99x6DQG4XG1VxA+d6A==", + "license": "MIT", + "peerDependencies": { + "graphql": "^0.6.0 || ^0.7.0 || ^0.8.0-b || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0", + "react": "^15.6.0 || ^16.0.0", + "react-dom": "^15.6.0 || ^16.0.0" } }, - "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==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, + "node_modules/graphql": { + "version": "16.9.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.9.0.tgz", + "integrity": "sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw==", + "license": "MIT", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" } }, - "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/graphql-config": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/graphql-config/-/graphql-config-5.1.3.tgz", + "integrity": "sha512-RBhejsPjrNSuwtckRlilWzLVt2j8itl74W9Gke1KejDTz7oaA5kVd6wRn9zK9TS5mcmIYGxf7zN7a1ORMdxp1Q==", "dev": true, + "license": "MIT", "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" + "@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": "^2.0.0", + "minimatch": "^9.0.5", + "string-env-interpolation": "^1.0.1", + "tslib": "^2.4.0" }, "engines": { - "node": "^10.12.0 || >=12.0.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/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==", + "node_modules/graphql-config/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, + "license": "MIT", "dependencies": { - "is-callable": "^1.1.3" + "balanced-match": "^1.0.0" } }, - "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==", + "node_modules/graphql-config/node_modules/jiti": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.3.3.tgz", + "integrity": "sha512-EX4oNDwcXSivPrw2qKH2LB5PoFxEvgtv2JgwW0bU858HoLQ+kutSvjLMUqBd0PeJYEinLWhoI9Ol0eYMqj/wNQ==", "dev": true, - "engines": { - "node": "*" + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" } }, - "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==", + "node_modules/graphql-config/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, + "license": "ISC", "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">= 6" - } - }, - "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": ">=16 || 14 >=14.17" }, "funding": { - "type": "patreon", - "url": "https://github.com/sponsors/rawify" + "url": "https://github.com/sponsors/isaacs" } }, - "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==", + "node_modules/graphql-language-service": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/graphql-language-service/-/graphql-language-service-5.3.0.tgz", + "integrity": "sha512-gCQIIy7lM9CB1KPLEb+DNZLczA9zuTLEOJE2hEQZTFYInogdmMDRa6RAkvM4LL0LcgcS+3uPs6KtHlcjCqRbUg==", + "license": "MIT", "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" + "debounce-promise": "^3.1.2", + "nullthrows": "^1.0.0", + "vscode-languageserver-types": "^3.17.1" }, - "optionalDependencies": { - "@emotion/is-prop-valid": "^0.8.2" + "bin": { + "graphql": "dist/temp-bin.js" }, "peerDependencies": { - "react": ">=16.8 || ^17.0.0 || ^18.0.0", - "react-dom": ">=16.8 || ^17.0.0 || ^18.0.0" + "graphql": "^15.5.0 || ^16.0.0 || ^17.0.0-alpha.2" } }, - "node_modules/framesync": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/framesync/-/framesync-6.0.1.tgz", - "integrity": "sha512-fUY88kXvGiIItgNC7wcTOl0SNRCVXMKSWW2Yzfmn7EKNc+MpCzcz9DhdHcdjbrtN3c6R4H5dTY2jiCpPdysEjA==", + "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, + "license": "MIT", "dependencies": { - "tslib": "^2.1.0" + "@graphql-typed-document-node/core": "^3.2.0", + "cross-fetch": "^3.1.5" + }, + "peerDependencies": { + "graphql": "14 - 16" } }, - "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/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==", "dev": true, + "license": "MIT", "dependencies": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" + "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==", + "license": "MIT", + "dependencies": { + "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/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" + "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, + "license": "MIT", + "workspaces": [ + "website" ], "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://github.com/sponsors/ljharb" + "node": ">=10" + }, + "peerDependencies": { + "graphql": ">=0.11 <=16" } }, - "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==", - "dev": true, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" }, "engines": { - "node": ">= 0.4" + "node": ">=0.4.7" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "optionalDependencies": { + "uglify-js": "^3.1.4" } }, - "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/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, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "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==", + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, + "license": "MIT", "engines": { - "node": "6.* || 8.* || >= 10.*" + "node": ">=8" } }, - "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/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, "license": "MIT", - "engines": { - "node": ">=18" + "dependencies": { + "es-define-property": "^1.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "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==", + "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, + "license": "MIT", "engines": { - "node": "*" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "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/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": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - }, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -11083,405 +10614,453 @@ "url": "https://github.com/sponsors/ljharb" } }, - "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": ">=6" - } - }, - "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/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, + "license": "MIT", "dependencies": { - "pump": "^3.0.0" + "has-symbols": "^1.0.3" }, "engines": { - "node": ">=8" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "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, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", "dependencies": { - "call-bind": "^1.0.5", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4" + "function-bind": "^1.1.2" }, "engines": { "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" } }, - "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, + "node_modules/hast-util-to-jsx-runtime": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.2.tgz", + "integrity": "sha512-1ngXYb+V9UT5h+PxNRa1O1FYguZK/XL+gkeqvp7EdHlB9oHUG0eYRo/vY5inBdcqo3RkPMC58/H94HvkbfGdyg==", + "license": "MIT", "dependencies": { - "resolve-pkg-maps": "^1.0.0" + "@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": { - "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "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, + "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==", + "license": "MIT", "dependencies": { - "async": "^3.2.0" + "@types/hast": "^3.0.0" + }, + "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==", + "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, + "license": "MIT", "dependencies": { - "assert-plus": "^1.0.0" + "capital-case": "^1.0.4", + "tslib": "^2.0.3" } }, - "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/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "node_modules/hey-listen": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/hey-listen/-/hey-listen-1.0.8.tgz", + "integrity": "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==", + "license": "MIT" + }, + "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==", + "license": "BSD-3-Clause", + "dependencies": { + "react-is": "^16.7.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==", "dev": true, + "license": "MIT", "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" + "whatwg-encoding": "^3.1.1" }, "engines": { - "node": "*" - }, + "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, + "license": "MIT" + }, + "node_modules/html-url-attributes": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz", + "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==", + "license": "MIT", "funding": { - "url": "https://github.com/sponsors/isaacs" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "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/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, + "license": "MIT", "dependencies": { - "is-glob": "^4.0.1" + "agent-base": "^7.1.0", + "debug": "^4.3.4" }, "engines": { - "node": ">= 6" + "node": ">= 14" } }, - "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/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, + "license": "MIT", "dependencies": { - "ini": "2.0.0" + "assert-plus": "^1.0.0", + "jsprim": "^2.0.2", + "sshpk": "^1.18.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10" } }, - "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==", + "node_modules/https-proxy-agent": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", + "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, "engines": { - "node": ">=4" + "node": ">= 14" } }, - "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/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, - "dependencies": { - "define-properties": "^1.1.3" - }, + "license": "Apache-2.0", "engines": { - "node": ">= 0.4" + "node": ">=8.12.0" + } + }, + "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/ljharb" + "url": "https://github.com/sponsors/cyberalien" } }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "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, + "license": "MIT", "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" + "safer-buffer": ">= 2.1.2 < 3" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.10.0" } }, - "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/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" + } + ], + "license": "BSD-3-Clause" }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "license": "MIT", + "engines": { + "node": ">= 4" } }, - "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/immutable": { + "version": "3.7.6", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.7.6.tgz", + "integrity": "sha512-AizQPcaofEtO11RZhPPHBOJRdo/20MKQF9mBLnVkBoyHi1/zXK8fzVdnEpSV9gxqtnh6Qomfp3F0xT5qP/vThw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.8.0" + } }, - "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/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "license": "MIT", "dependencies": { - "@graphiql/react": "^0.20.4", - "@graphiql/toolkit": "^0.9.1", - "graphql-language-service": "^5.2.0", - "markdown-it": "^12.2.0" + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" }, - "peerDependencies": { - "graphql": "^15.5.0 || ^16.0.0", - "react": "^16.8.0 || ^17 || ^18", - "react-dom": "^16.8.0 || ^17 || ^18" + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/graphql": { - "version": "16.8.1", - "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.8.1.tgz", - "integrity": "sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==", + "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==", + "license": "MIT", "engines": { - "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" + "node": ">=4" } }, - "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/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, - "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" - }, + "license": "MIT", "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": ">=12.2" }, - "peerDependenciesMeta": { - "cosmiconfig-toml-loader": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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/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": { - "brace-expansion": "^1.1.7" - }, + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=8" } }, - "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": { - "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/graphql-request": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/graphql-request/-/graphql-request-6.1.0.tgz", - "integrity": "sha512-p+XPfS4q7aIpKVcgmnZKhMNqhltk20hfXtkaIkTfjjmiKMJ5xrt5c743cL03y/K7y1rg3WrIC49xGiEQ4mxdNw==", + "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, - "dependencies": { - "@graphql-typed-document-node/core": "^3.2.0", - "cross-fetch": "^3.1.5" + "license": "MIT", + "engines": { + "node": ">=18" }, - "peerDependencies": { - "graphql": "14 - 16" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, + "license": "ISC", "dependencies": { - "node-fetch": "^2.6.12" + "once": "^1.3.0", + "wrappy": "1" } }, - "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" - }, - "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/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" }, - "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/ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "dev": true, + "license": "ISC", "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==", + "node_modules/inline-style-parser": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz", + "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==", + "license": "MIT" + }, + "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, + "license": "MIT", "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.2", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" + "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": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" + "node": ">=12.0.0" } }, - "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==", + "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, + "license": "MIT", "dependencies": { - "ansi-regex": "^2.0.0" + "es-errors": "^1.3.0", + "hasown": "^2.0.0", + "side-channel": "^1.0.4" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.4" } }, - "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, + "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==", + "license": "ISC", "engines": { - "node": ">=0.10.0" + "node": ">=12" } }, - "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, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.0.0" } }, - "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-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, + "license": "MIT", + "dependencies": { + "is-relative": "^1.0.0", + "is-windows": "^1.0.1" + }, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "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, + "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==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "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==", + "license": "MIT", "dependencies": { - "es-define-property": "^1.0.0" + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "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-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": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -11489,11 +11068,16 @@ "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==", + "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, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1" + }, "engines": { "node": ">= 0.4" }, @@ -11501,438 +11085,365 @@ "url": "https://github.com/sponsors/ljharb" } }, - "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-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "license": "MIT" + }, + "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, + "license": "MIT", "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" + "has-bigints": "^1.0.1" }, "funding": { "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==", + "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==", + "license": "MIT", "dependencies": { - "function-bind": "^1.1.2" + "binary-extensions": "^2.0.0" }, "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==", + "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, + "license": "MIT", "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" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://github.com/sponsors/ljharb" } }, - "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-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, + "license": "MIT", + "engines": { + "node": ">= 0.4" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://github.com/sponsors/ljharb" } }, - "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==", + "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, + "license": "MIT", "dependencies": { - "capital-case": "^1.0.4", - "tslib": "^2.0.3" + "ci-info": "^3.2.0" + }, + "bin": { + "is-ci": "bin.js" } }, - "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==", + "node_modules/is-core-module": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "license": "MIT", "dependencies": { - "react-is": "^16.7.0" + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "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-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, + "license": "MIT", "dependencies": { - "whatwg-encoding": "^3.1.1" + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">=18" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "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_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==", + "license": "MIT", "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "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": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", "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==", - "dev": true, - "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^2.0.2", - "sshpk": "^1.14.1" - }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", "engines": { - "node": ">=0.10" + "node": ">=8" } }, - "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, + "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==", + "license": "MIT", "dependencies": { - "agent-base": "^7.0.2", - "debug": "4" + "is-extglob": "^2.1.1" }, "engines": { - "node": ">= 14" + "node": ">=0.10.0" } }, - "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": ">=8.12.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==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "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-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, - "bin": { - "husky": "lib/bin.js" + "license": "MIT", + "dependencies": { + "global-dirs": "^3.0.0", + "is-path-inside": "^3.0.2" }, "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/typicode" - } - }, - "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==", - "dependencies": { - "@iconify/types": "^2.0.0" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/cyberalien" + "url": "https://github.com/sponsors/sindresorhus" } }, - "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-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, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "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-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, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] + "license": "MIT", + "dependencies": { + "tslib": "^2.0.3" + } }, - "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-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", "dev": true, + "license": "MIT", "engines": { - "node": ">= 4" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "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, + "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==", + "license": "MIT", "engines": { - "node": ">=0.8.0" + "node": ">=0.12.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==", + "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, + "license": "MIT", "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">=6" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "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" - } - }, - "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/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "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, + "license": "MIT", "engines": { "node": ">=8" } }, - "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, + "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==", + "license": "MIT", "engines": { - "node": ">=18" + "node": ">=12" }, "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, + "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==", + "license": "MIT", "dependencies": { - "once": "^1.3.0", - "wrappy": "1" + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "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/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, + "license": "MIT" + }, + "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==", + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=0.10.0" } }, - "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/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, + "license": "MIT", "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" + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" }, "engines": { - "node": ">=12.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "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/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, + "license": "MIT", "dependencies": { - "es-errors": "^1.3.0", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" + "is-unc-path": "^1.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=0.10.0" } }, - "node_modules/internmap": { + "node_modules/is-set": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", - "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "license": "MIT", "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": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "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==", + "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, + "license": "MIT", "dependencies": { - "is-relative": "^1.0.0", - "is-windows": "^1.0.1" + "call-bind": "^1.0.7" }, "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": ">= 0.4" + }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-alphanumerical": { + "node_modules/is-stream": { "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" + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "url": "https://github.com/sponsors/sindresorhus" } }, - "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/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" }, "engines": { @@ -11942,14 +11453,14 @@ "url": "https://github.com/sponsors/ljharb" } }, - "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==", + "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, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1" + "has-symbols": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -11958,58 +11469,55 @@ "url": "https://github.com/sponsors/ljharb" } }, - "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-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-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/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, + "license": "MIT", "dependencies": { - "has-tostringtag": "^1.0.0" + "unc-path-regex": "^0.1.2" }, "engines": { - "node": ">= 0.4" + "node": ">=0.10.0" + } + }, + "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, + "license": "MIT", + "engines": { + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "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/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, + "license": "MIT", "dependencies": { - "has-bigints": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "tslib": "^2.0.3" } }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-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/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -12017,938 +11525,767 @@ "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/is-weakset": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz", + "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==", "dev": true, + "license": "MIT", "dependencies": { - "builtin-modules": "^3.3.0" + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4" }, "engines": { - "node": ">=6" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "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==", + "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, + "license": "MIT", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.10.0" } }, - "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/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", "dev": true, - "dependencies": { - "ci-info": "^3.2.0" - }, - "bin": { - "is-ci": "bin.js" - } + "license": "MIT" }, - "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" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" }, - "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==", - "dev": true, - "dependencies": { - "is-typed-array": "^1.1.13" - }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "license": "MIT", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.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/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, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "license": "MIT", + "peerDependencies": { + "ws": "*" } }, - "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/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", + "dev": true, + "license": "MIT" }, - "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/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, + "license": "BSD-3-Clause", "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "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==", + "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, + "license": "BSD-3-Clause", "dependencies": { - "call-bind": "^1.0.2" + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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": ">=10" } }, - "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/istanbul-lib-source-maps": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", + "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "has-tostringtag": "^1.0.0" + "@jridgewell/trace-mapping": "^0.3.23", + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=10" } }, - "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/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, + "license": "BSD-3-Clause", "dependencies": { - "is-extglob": "^2.1.1" + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "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/iterall": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.3.0.tgz", + "integrity": "sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg==", + "license": "MIT" }, - "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, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", "dependencies": { - "global-dirs": "^3.0.0", - "is-path-inside": "^3.0.2" - }, - "engines": { - "node": ">=10" + "@isaacs/cliui": "^8.0.2" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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" + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" } }, - "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": { - "tslib": "^2.0.3" + "node_modules/jiti": { + "version": "1.21.6", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz", + "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==", + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" } }, - "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/jose": { + "version": "5.9.4", + "resolved": "https://registry.npmjs.org/jose/-/jose-5.9.4.tgz", + "integrity": "sha512-WBBl6au1qg6OHj67yCffCgFR3BADJBXN8MdRvCgJDuMv3driV2nHr7jdGvaKX9IolosAsn+M0XRArqLXUhyJHQ==", "dev": true, + "license": "MIT", "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/panva" } }, - "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==", - "dev": true, + "node_modules/jotai": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/jotai/-/jotai-2.10.1.tgz", + "integrity": "sha512-4FycO+BOTl2auLyF2Chvi6KTDqdsdDDtpaL/WHQMs8f3KS1E3loiUShQzAzFA/sMU5cJ0hz/RT1xum9YbG/zaA==", + "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-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-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/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" - }, + "license": "MIT", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.10.0" } }, - "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": ">=8" - } + "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==", + "license": "MIT" }, - "node_modules/is-plain-obj": { + "node_modules/js-yaml": { "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": { - "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==", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "license": "MIT", "dependencies": { - "isobject": "^3.0.1" + "argparse": "^2.0.1" }, - "engines": { - "node": ">=0.10.0" + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "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==", - "engines": { - "node": ">=0.10.0" - } + "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/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/jsdom": { + "version": "24.1.3", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-24.1.3.tgz", + "integrity": "sha512-MyL55p3Ut3cXbeBEG7Hcv0mVM8pp8PBNWxRqchZnSfAiES1v1mRnMeFfaHWIPULpwsYfvO+ZmMZz5tGCnjzDUQ==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" + "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.2", + "https-proxy-agent": "^7.0.5", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.12", + "parse5": "^7.1.2", + "rrweb-cssom": "^0.7.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.1.4", + "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.18.0", + "xml-name-validator": "^5.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=18" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "canvas": "^2.11.2" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } } }, - "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": { - "is-unc-path": "^1.0.0" + "node_modules/jsesc": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", + "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" }, "engines": { - "node": ">=0.10.0" + "node": ">=6" } }, - "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/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==", + "license": "MIT" + }, + "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, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "license": "(AFL-2.1 OR BSD-3-Clause)" }, - "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/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true, - "dependencies": { - "call-bind": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "license": "MIT" }, - "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/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, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "license": "ISC" }, - "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/json-to-graphql-query": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/json-to-graphql-query/-/json-to-graphql-query-2.3.0.tgz", + "integrity": "sha512-khZtaLLQ0HllFec+t89ZWduUZ0rmne/OpRm/39hyZUWDHNx9Yk4DgQzDtMeqd8zj2g5opBD4GHrdtH0JzKnN2g==", + "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, + "license": "Apache-2.0", "dependencies": { - "has-tostringtag": "^1.0.0" + "remedial": "^1.0.7", + "remove-trailing-spaces": "^1.0.6" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 0.2.0" } }, - "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": { - "has-symbols": "^1.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==", + "license": "MIT", + "bin": { + "json5": "lib/cli.js" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=6" } }, - "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/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "dev": true, + "license": "MIT", "dependencies": { - "which-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" + "universalify": "^2.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "optionalDependencies": { + "graceful-fs": "^4.1.6" } }, - "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/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/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": { - "unc-path-regex": "^0.1.2" - }, - "engines": { - "node": ">=0.10.0" + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" } }, - "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/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, + "license": "MIT", "engines": { - "node": ">=10" + "node": "> 0.8" + } + }, + "node_modules/lilconfig": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", + "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", + "license": "MIT", + "engines": { + "node": ">=14" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/antonk52" } }, - "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, - "dependencies": { - "tslib": "^2.0.3" - } + "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==", + "license": "MIT" }, - "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, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "license": "MIT", + "dependencies": { + "uc.micro": "^2.0.0" } }, - "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": { + "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": { - "call-bind": "^1.0.2" + "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" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://opencollective.com/lint-staged" } }, - "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/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": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "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/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" - } - }, - "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": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "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==", + "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, - "peerDependencies": { - "ws": "*" + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "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/chalk": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", + "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", "dev": true, + "license": "MIT", "engines": { - "node": ">=8" + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?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/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": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" + "restore-cursor": "^5.0.0" }, "engines": { - "node": ">=10" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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/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": { - "@jridgewell/trace-mapping": "^0.3.23", - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0" + "slice-ansi": "^5.0.0", + "string-width": "^7.0.0" }, "engines": { - "node": ">=10" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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/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, - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=18" } }, - "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/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/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==", + "node_modules/lint-staged/node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", "dev": true, - "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" - } + "license": "MIT" }, - "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/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": { - "@isaacs/cliui": "^8.0.2" + "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": ">=14" + "node": ">=16.17" }, "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/sindresorhus/execa?sponsor=1" } }, - "node_modules/jose": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/jose/-/jose-5.2.3.tgz", - "integrity": "sha512-KUXdbctm1uHVL8BYhnyHkgp3zDX5KW8ZhAKVFEfUbU2P8Alpzjb+48hHvjOdQIyPshoblhzsuqOwEEAbtHVirA==", + "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" + }, "funding": { - "url": "https://github.com/sponsors/panva" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jotai": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/jotai/-/jotai-2.7.2.tgz", - "integrity": "sha512-6Ft5kpNu8p93Ssf1Faoza3hYQZRIYp7rioK8MwTTFnbQKwUyZElwquPwl1h6U0uo9hC0jr+ghO3gcSjc6P35/Q==", + "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": ">=12.20.0" - }, - "peerDependencies": { - "@types/react": ">=17.0.0", - "react": ">=17.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "react": { - "optional": true - } + "node": ">=16.17.0" } }, - "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/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_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" + "node": ">=12" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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/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": { - "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" - }, + "license": "MIT", "engines": { - "node": ">=18" - }, - "peerDependencies": { - "canvas": "^2.11.2" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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" + "node_modules/lint-staged/node_modules/listr2": { + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.5.tgz", + "integrity": "sha512-iyAZCeyD+c1gPyE9qpFu8af0Y+MRtmKOncdGoA2S5EY8iFq99dmmvkNnHiWo+pj0s7yH7l3KPIgee77tKpXPWQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "cli-truncate": "^4.0.0", + "colorette": "^2.0.20", + "eventemitter3": "^5.0.1", + "log-update": "^6.1.0", + "rfdc": "^1.4.1", + "wrap-ansi": "^9.0.0" }, "engines": { - "node": ">=4" + "node": ">=18.0.0" } }, - "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/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.5", - "isarray": "^2.0.5", - "jsonify": "^0.0.1", - "object-keys": "^1.1.1" + "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/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==", + "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": { - "remedial": "^1.0.7", - "remove-trailing-spaces": "^1.0.6" + "get-east-asian-width": "^1.0.0" }, "engines": { - "node": ">= 0.2.0" - } - }, - "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" + "node": ">=18" }, - "engines": { - "node": ">=6" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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/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": { - "universalify": "^2.0.0" + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/jsonify": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz", - "integrity": "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==", - "dev": true, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "node_modules/jsprim": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz", - "integrity": "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==", + "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, - "engines": [ - "node >=0.6.0" - ], - "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" + "license": "MIT", + "engines": { + "node": ">=12" + }, + "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/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": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "object.assign": "^4.1.4", - "object.values": "^1.1.6" + "path-key": "^4.0.0" }, "engines": { - "node": ">=4.0" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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/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": { - "json-buffer": "3.0.1" + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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/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": "> 0.8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "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": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" }, "engines": { - "node": ">= 0.8.0" - } - }, - "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": ">=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==" - }, - "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/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": { - "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" + "node": ">=18" }, "funding": { - "url": "https://opencollective.com/lint-staged" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lint-staged/node_modules/ansi-escapes": { + "node_modules/lint-staged/node_modules/restore-cursor/node_modules/onetime": { "version": "7.0.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz", - "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", "dev": true, "license": "MIT", "dependencies": { - "environment": "^1.0.0" + "mimic-function": "^5.0.0" }, "engines": { "node": ">=18" @@ -12957,69 +12294,46 @@ "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" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/lint-staged/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/lint-staged/node_modules/chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", + "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": "^12.17.0 || ^14.13 || >=16.0.0" + "node": ">=14" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/lint-staged/node_modules/cli-cursor": { + "node_modules/lint-staged/node_modules/slice-ansi": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", - "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "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": { - "restore-cursor": "^5.0.0" + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" }, "engines": { - "node": ">=18" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, - "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==", + "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": { - "slice-ansi": "^5.0.0", - "string-width": "^7.0.0" + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" }, "engines": { "node": ">=18" @@ -13028,343 +12342,30 @@ "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" - } - }, - "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==", + "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": { - "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" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "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" + "ansi-regex": "^6.0.1" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/lint-staged/node_modules/is-stream": { + "node_modules/lint-staged/node_modules/strip-final-newline": { "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" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, - "node_modules/lint-staged/node_modules/listr2": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.4.tgz", - "integrity": "sha512-opevsywziHd3zHCVQGAj8zu+Z3yHNkkoYhWIGnq54RrCVwLz0MozotJEDnKsIBLvkfLGN6BLOyAeRrYI0pKA4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "cli-truncate": "^4.0.0", - "colorette": "^2.0.20", - "eventemitter3": "^5.0.1", - "log-update": "^6.1.0", - "rfdc": "^1.4.1", - "wrap-ansi": "^9.0.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/lint-staged/node_modules/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==", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", "dev": true, "license": "MIT", - "dependencies": { - "ansi-escapes": "^7.0.0", - "cli-cursor": "^5.0.0", - "slice-ansi": "^7.1.0", - "strip-ansi": "^7.1.0", - "wrap-ansi": "^9.0.0" - }, "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/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": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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": { - "ansi-styles": "^6.2.1", - "is-fullwidth-code-point": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/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": { - "path-key": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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/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": { - "onetime": "^7.0.0", - "signal-exit": "^4.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "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/chalk/slice-ansi?sponsor=1" - } - }, - "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": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lint-staged/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/lint-staged/node_modules/strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -13374,15723 +12375,40 @@ "version": "9.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/listr2": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-4.0.5.tgz", - "integrity": "sha512-juGHV1doQdpNT3GSTs9IUN43QJb7KHdF9uqg7Vufs/tG9VTzpFphqF4pm/ICdAABGQxsyNn9CiYA3StkI6jpwA==", - "dev": true, - "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" - }, - "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" - } - }, - "node_modules/micromark-factory-destination": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.0.tgz", - "integrity": "sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-label": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.0.tgz", - "integrity": "sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-space": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", - "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-title": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.0.tgz", - "integrity": "sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-whitespace": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.0.tgz", - "integrity": "sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-character": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", - "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-chunked": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.0.tgz", - "integrity": "sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-classify-character": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.0.tgz", - "integrity": "sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-combine-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.0.tgz", - "integrity": "sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-chunked": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-numeric-character-reference": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.1.tgz", - "integrity": "sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.0.tgz", - "integrity": "sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA==", - "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", - "micromark-util-character": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-encode": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz", - "integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-util-html-tag-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.0.tgz", - "integrity": "sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-util-normalize-identifier": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.0.tgz", - "integrity": "sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-resolve-all": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.0.tgz", - "integrity": "sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-sanitize-uri": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz", - "integrity": "sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-subtokenize": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.0.tgz", - "integrity": "sha512-vc93L1t+gpR3p8jxeVdaYlbV2jTYteDje19rNSS/H5dlhxUYll5Fy6vJ2cDwP8RnsXi818yGty1ayP55y3W6fg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-symbol": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromark-util-types": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.0.tgz", - "integrity": "sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ] - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/mimic-function": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", - "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mini-svg-data-uri": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz", - "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==", - "bin": { - "mini-svg-data-uri": "cli.js" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minipass": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", - "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "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" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "dev": true - }, - "node_modules/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } - }, - "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/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", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" - }, - "node_modules/no-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", - "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", - "dependencies": { - "lower-case": "^2.0.2", - "tslib": "^2.0.3" - } - }, - "node_modules/node-addon-api": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.0.tgz", - "integrity": "sha512-mNcltoe1R8o7STTegSOHdnJNN7s5EUvhoS7ShnTHDyOSd+8H+UdWODq6qSv67PjC8Zc5JRT8+oLAMCr0SIXw7g==", - "dev": true, - "optional": true, - "peer": true, - "engines": { - "node": "^16 || ^18 || >= 20" - } - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-fetch/node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "node_modules/node-fetch/node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "node_modules/node-fetch/node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true - }, - "node_modules/node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==" - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, - "node_modules/nullthrows": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz", - "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==" - }, - "node_modules/nwsapi": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz", - "integrity": "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==", - "dev": true - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", - "engines": { - "node": ">= 6" - } - }, - "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==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", - "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "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", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/openapi-typescript": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/openapi-typescript/-/openapi-typescript-7.0.2.tgz", - "integrity": "sha512-BBrYEf0YdW31Ernd07cD/qHoalSuiiUQvy+rHvU/1Iz9WbcFpRsIXrnfEnrEuiGTRuKCG6cDQCrxNK/rbwQRLg==", - "dev": true, - "dependencies": { - "@redocly/openapi-core": "^1.16.0", - "ansi-colors": "^4.1.3", - "parse-json": "^8.1.0", - "supports-color": "^9.4.0", - "yargs-parser": "^21.1.1" - }, - "bin": { - "openapi-typescript": "bin/cli.js" - }, - "peerDependencies": { - "typescript": "^5.x" - } - }, - "node_modules/openapi-typescript/node_modules/parse-json": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.1.0.tgz", - "integrity": "sha512-rum1bPifK5SSar35Z6EKZuYPJx85pkNaFrxBK3mwdfSJ1/WKbYrjoW/zTPSjRRamfmVX1ACBIdFAO0VRErW/EA==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.22.13", - "index-to-position": "^0.1.2", - "type-fest": "^4.7.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/openapi-typescript/node_modules/supports-color": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.4.0.tgz", - "integrity": "sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/openapi-typescript/node_modules/type-fest": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.21.0.tgz", - "integrity": "sha512-ADn2w7hVPcK6w1I0uWnM//y1rLXZhzB9mr0a3OirzclKF1Wp6VzevUmzz/NRAWunOT6E8HrnpGY7xOfc6K57fA==", - "dev": true, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/openapi-typescript/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/optimism": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/optimism/-/optimism-0.18.0.tgz", - "integrity": "sha512-tGn8+REwLRNFnb9WmcY5IfpOqeX2kpaYJ1s6Ae3mn12AeydLkR3j+jSCmVQFoXqU8D41PAJ1RG1rCRNWmNZVmQ==", - "dependencies": { - "@wry/caches": "^1.0.0", - "@wry/context": "^0.7.0", - "@wry/trie": "^0.4.3", - "tslib": "^2.3.0" - } - }, - "node_modules/optimism/node_modules/@wry/trie": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@wry/trie/-/trie-0.4.3.tgz", - "integrity": "sha512-I6bHwH0fSf6RqQcnnXLJKhkSXG45MFral3GxPaY4uAl0LYDZM+YDVDAiU9bYwjTuysy1S0IeecWtmq1SZA3M1w==", - "dependencies": { - "tslib": "^2.3.0" - }, - "engines": { - "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", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "dev": true, - "dependencies": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ospath": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz", - "integrity": "sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==", - "dev": true - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "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", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "dev": true, - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/param-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", - "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", - "dev": true, - "dependencies": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-entities": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.1.tgz", - "integrity": "sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w==", - "dependencies": { - "@types/unist": "^2.0.0", - "character-entities": "^2.0.0", - "character-entities-legacy": "^3.0.0", - "character-reference-invalid": "^2.0.0", - "decode-named-character-reference": "^1.0.0", - "is-alphanumerical": "^2.0.0", - "is-decimal": "^2.0.0", - "is-hexadecimal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/parse-entities/node_modules/@types/unist": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz", - "integrity": "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==" - }, - "node_modules/parse-filepath": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", - "integrity": "sha512-FwdRXKCohSVeXqwtYonZTXtbGJKrn+HNyWDYVcp5yuJlesTwNH4rsmRZ+GrKAPJ5bLpRxESMeS+Rl0VCHRvB2Q==", - "dev": true, - "dependencies": { - "is-absolute": "^1.0.0", - "map-cache": "^0.2.0", - "path-root": "^0.1.1" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parse5": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", - "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", - "dev": true, - "dependencies": { - "entities": "^4.4.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/pascal-case": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", - "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", - "dev": true, - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/path-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/path-case/-/path-case-3.0.4.tgz", - "integrity": "sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg==", - "dev": true, - "dependencies": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" - }, - "node_modules/path-root": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", - "integrity": "sha512-QLcPegTHF11axjfojBIoDygmS2E3Lf+8+jI6wOVmNVenrKSo3mFdSGiIgdSHenczw3wPtlVMQaFVwGmM7BJdtg==", - "dev": true, - "dependencies": { - "path-root-regex": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-root-regex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", - "integrity": "sha512-4GlJ6rZDhQZFE0DPVKh0e9jmZ5egZfxTkp7bcRDuPlJXbAwhxcl2dINPUAsjLdejqaLsCeg8axcLjIbvBjN4pQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-scurry": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", - "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", - "dependencies": { - "lru-cache": "^9.1.1 || ^10.0.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", - "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", - "engines": { - "node": "14 || >=16.14" - } - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/pathe": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", - "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", - "dev": true - }, - "node_modules/pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", - "dev": true - }, - "node_modules/performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", - "dev": true - }, - "node_modules/picocolors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", - "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pidtree": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", - "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", - "dev": true, - "bin": { - "pidtree": "bin/pidtree.js" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", - "engines": { - "node": ">= 6" - } - }, - "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==", - "dev": true, - "dependencies": { - "jsonc-parser": "^3.2.0", - "mlly": "^1.2.0", - "pathe": "^1.1.0" - } - }, - "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==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "playwright-core": "1.47.0" - }, - "bin": { - "playwright": "cli.js" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "fsevents": "2.3.2" - } - }, - "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==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "playwright-core": "cli.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/playwright/node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/pluralize": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", - "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/popmotion": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/popmotion/-/popmotion-11.0.3.tgz", - "integrity": "sha512-Y55FLdj3UxkR7Vl3s7Qr4e9m0onSnP8W7d/xQLsoJM40vs6UKHFdygs6SWryasTZYqugMjm3BepCF4CWXDiHgA==", - "dependencies": { - "framesync": "6.0.1", - "hey-listen": "^1.0.8", - "style-value-types": "5.0.0", - "tslib": "^2.1.0" - } - }, - "node_modules/possible-typed-array-names": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", - "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/postcss": { - "version": "8.4.47", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", - "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.1.0", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-import": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", - "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", - "dependencies": { - "postcss-value-parser": "^4.0.0", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "postcss": "^8.0.0" - } - }, - "node_modules/postcss-js": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", - "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", - "dependencies": { - "camelcase-css": "^2.0.1" - }, - "engines": { - "node": "^12 || ^14 || >= 16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.4.21" - } - }, - "node_modules/postcss-load-config": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", - "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "lilconfig": "^3.0.0", - "yaml": "^2.3.4" - }, - "engines": { - "node": ">= 14" - }, - "peerDependencies": { - "postcss": ">=8.0.9", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "postcss": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/postcss-load-config/node_modules/lilconfig": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.0.0.tgz", - "integrity": "sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g==", - "engines": { - "node": ">=14" - } - }, - "node_modules/postcss-nested": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", - "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", - "dependencies": { - "postcss-selector-parser": "^6.0.11" - }, - "engines": { - "node": ">=12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.2.14" - } - }, - "node_modules/postcss-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==", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" - }, - "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" - } - }, - "node_modules/pretty-bytes": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", - "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", - "dev": true, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pretty-format": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", - "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^17.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/pretty-format/node_modules/react-is": { - "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" - } - }, - "node_modules/prismjs": { - "version": "1.29.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", - "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==", - "engines": { - "node": ">=6" - } - }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", - "dev": true, - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/promise": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", - "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", - "dev": true, - "dependencies": { - "asap": "~2.0.3" - } - }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "node_modules/property-information": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.4.1.tgz", - "integrity": "sha512-OHYtXfu5aI2sS2LWFSN5rgJjrQ4pCy8i1jubJLe2QvMF8JJ++HXTUIVWFLfXJoaOfvYYjk2SN8J2wFUWIGXT4w==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", - "dev": true - }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", - "dev": true - }, - "node_modules/pvtsutils": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.5.tgz", - "integrity": "sha512-ARvb14YB9Nm2Xi6nBq1ZX6dAM0FsJnuk+31aUp4TrcZEdKUlSqOqsxJHUPJDNE3qiIp+iUPEIeR6Je/tgV7zsA==", - "dev": true, - "dependencies": { - "tslib": "^2.6.1" - } - }, - "node_modules/pvutils": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.1.3.tgz", - "integrity": "sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "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==", - "dev": true, - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/query-string": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-9.0.0.tgz", - "integrity": "sha512-4EWwcRGsO2H+yzq6ddHcVqkCQ2EFUSfDMEjF8ryp8ReymyZhIuaFRGLomeOQLkrzacMHoyky2HW0Qe30UbzkKw==", - "dependencies": { - "decode-uri-component": "^0.4.1", - "filter-obj": "^5.1.0", - "split-on-first": "^3.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "dev": true - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "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/ramda": { - "version": "0.29.1", - "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.29.1.tgz", - "integrity": "sha512-OfxIeWzd4xdUNxlWhgFazxsA/nl3mS4/jGZI5n00uWOoSSFRhC1b6gl6xvmzUamgmqELraWp0J/qqVlXYPDPyA==", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/ramda" - } - }, - "node_modules/react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", - "dependencies": { - "loose-envify": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-accessible-treeview": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/react-accessible-treeview/-/react-accessible-treeview-2.8.3.tgz", - "integrity": "sha512-taDTIYZ6p96/zIhJBUKvyGTXcInudatP/9fwKG0BW+VRf1PmU5hOT2FkDovDKzSwj2VSOj1PRx+E6ojhOA+2xA==", - "peerDependencies": { - "classnames": "^2.2.6", - "prop-types": "^15.7.2", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/react-datepicker": { - "version": "4.25.0", - "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-4.25.0.tgz", - "integrity": "sha512-zB7CSi44SJ0sqo8hUQ3BF1saE/knn7u25qEMTO1CQGofY1VAKahO8k9drZtp0cfW1DMfoYLR3uSY1/uMvbEzbg==", - "dependencies": { - "@popperjs/core": "^2.11.8", - "classnames": "^2.2.6", - "date-fns": "^2.30.0", - "prop-types": "^15.7.2", - "react-onclickoutside": "^6.13.0", - "react-popper": "^2.3.0" - }, - "peerDependencies": { - "react": "^16.9.0 || ^17 || ^18", - "react-dom": "^16.9.0 || ^17 || ^18" - } - }, - "node_modules/react-datepicker/node_modules/date-fns": { - "version": "2.30.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", - "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", - "dependencies": { - "@babel/runtime": "^7.21.0" - }, - "engines": { - "node": ">=0.11" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/date-fns" - } - }, - "node_modules/react-diff-view": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/react-diff-view/-/react-diff-view-3.2.0.tgz", - "integrity": "sha512-p58XoqMxgt71ujpiDQTs9Za3nqTawt1E4bTzKsYSqr8I8br6cjQj1b66HxGnV8Yrc6MD6iQPqS1aZiFoGqEw+g==", - "dependencies": { - "classnames": "^2.3.2", - "diff-match-patch": "^1.0.5", - "gitdiff-parser": "^0.3.1", - "lodash": "^4.17.21", - "shallow-equal": "^3.1.0", - "warning": "^4.0.3" - }, - "peerDependencies": { - "react": ">=16.14.0" - } - }, - "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==", - "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" - }, - "peerDependencies": { - "react": "^18.2.0" - } - }, - "node_modules/react-error-boundary": { - "version": "4.0.13", - "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-4.0.13.tgz", - "integrity": "sha512-b6PwbdSv8XeOSYvjt8LpgpKrZ0yGdtZokYwkwV2wlcZbxgopHX/hgPl5VgpnoVOWd868n1hktM8Qm4b+02MiLQ==", - "dependencies": { - "@babel/runtime": "^7.12.5" - }, - "peerDependencies": { - "react": ">=16.13.1" - } - }, - "node_modules/react-fast-compare": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", - "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==" - }, - "node_modules/react-hook-form": { - "version": "7.51.5", - "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.51.5.tgz", - "integrity": "sha512-J2ILT5gWx1XUIJRETiA7M19iXHlG74+6O3KApzvqB/w8S5NQR7AbU8HVZrMALdmDgWpRPYiZJl0zx8Z4L2mP6Q==", - "engines": { - "node": ">=12.22.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/react-hook-form" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17 || ^18" - } - }, - "node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - }, - "node_modules/react-loading": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/react-loading/-/react-loading-2.0.3.tgz", - "integrity": "sha512-Vdqy79zq+bpeWJqC+xjltUjuGApyoItPgL0vgVfcJHhqwU7bAMKzysfGW/ADu6i0z0JiOCRJjo+IkFNkRNbA3A==", - "peerDependencies": { - "prop-types": "^15.6.0", - "react": ">=0.14.0" - } - }, - "node_modules/react-markdown": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-9.0.1.tgz", - "integrity": "sha512-186Gw/vF1uRkydbsOIkcGXw7aHq0sZOCRFFjGrr7b9+nVZg4UfA4enXCaxm4fUzecU38sWfrNDitGhshuU7rdg==", - "dependencies": { - "@types/hast": "^3.0.0", - "devlop": "^1.0.0", - "hast-util-to-jsx-runtime": "^2.0.0", - "html-url-attributes": "^3.0.0", - "mdast-util-to-hast": "^13.0.0", - "remark-parse": "^11.0.0", - "remark-rehype": "^11.0.0", - "unified": "^11.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - }, - "peerDependencies": { - "@types/react": ">=18", - "react": ">=18" - } - }, - "node_modules/react-onclickoutside": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/react-onclickoutside/-/react-onclickoutside-6.13.0.tgz", - "integrity": "sha512-ty8So6tcUpIb+ZE+1HAhbLROvAIJYyJe/1vRrrcmW+jLsaM+/powDRqxzo6hSh9CuRZGSL1Q8mvcF5WRD93a0A==", - "funding": { - "type": "individual", - "url": "https://github.com/Pomax/react-onclickoutside/blob/master/FUNDING.md" - }, - "peerDependencies": { - "react": "^15.5.x || ^16.x || ^17.x || ^18.x", - "react-dom": "^15.5.x || ^16.x || ^17.x || ^18.x" - } - }, - "node_modules/react-paginate": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/react-paginate/-/react-paginate-8.2.0.tgz", - "integrity": "sha512-sJCz1PW+9PNIjUSn919nlcRVuleN2YPoFBOvL+6TPgrH/3lwphqiSOgdrLafLdyLDxsgK+oSgviqacF4hxsDIw==", - "dependencies": { - "prop-types": "^15" - }, - "peerDependencies": { - "react": "^16 || ^17 || ^18" - } - }, - "node_modules/react-popper": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.3.0.tgz", - "integrity": "sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==", - "dependencies": { - "react-fast-compare": "^3.0.1", - "warning": "^4.0.2" - }, - "peerDependencies": { - "@popperjs/core": "^2.0.0", - "react": "^16.8.0 || ^17 || ^18", - "react-dom": "^16.8.0 || ^17 || ^18" - } - }, - "node_modules/react-refresh": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", - "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-remove-scroll": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz", - "integrity": "sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==", - "dependencies": { - "react-remove-scroll-bar": "^2.3.3", - "react-style-singleton": "^2.2.1", - "tslib": "^2.1.0", - "use-callback-ref": "^1.3.0", - "use-sidecar": "^1.1.2" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/react-remove-scroll-bar": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.6.tgz", - "integrity": "sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==", - "dependencies": { - "react-style-singleton": "^2.2.1", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/react-router": { - "version": "6.22.3", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.3.tgz", - "integrity": "sha512-dr2eb3Mj5zK2YISHK++foM9w4eBnO23eKnZEDs7c880P6oKbrjz/Svg9+nxqtHQK+oMW4OtjZca0RqPglXxguQ==", - "dependencies": { - "@remix-run/router": "1.15.3" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "react": ">=16.8" - } - }, - "node_modules/react-router-dom": { - "version": "6.22.3", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.3.tgz", - "integrity": "sha512-7ZILI7HjcE+p31oQvwbokjk6OA/bnFxrhJ19n82Ex9Ph8fNAq+Hm/7KchpMGlTgWhUxRHMMCut+vEtNpWpowKw==", - "dependencies": { - "@remix-run/router": "1.15.3", - "react-router": "6.22.3" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "react": ">=16.8", - "react-dom": ">=16.8" - } - }, - "node_modules/react-shallow-renderer": { - "version": "16.15.0", - "resolved": "https://registry.npmjs.org/react-shallow-renderer/-/react-shallow-renderer-16.15.0.tgz", - "integrity": "sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA==", - "dev": true, - "dependencies": { - "object-assign": "^4.1.1", - "react-is": "^16.12.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependencies": { - "react": "^16.0.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/react-simple-code-editor": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/react-simple-code-editor/-/react-simple-code-editor-0.13.1.tgz", - "integrity": "sha512-XYeVwRZwgyKtjNIYcAEgg2FaQcCZwhbarnkJIV20U2wkCU9q/CPFBo8nRXrK4GXUz3AvbqZFsZRrpUTkqqEYyQ==", - "peerDependencies": { - "react": "*", - "react-dom": "*" - } - }, - "node_modules/react-smooth": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.0.tgz", - "integrity": "sha512-2NMXOBY1uVUQx1jBeENGA497HK20y6CPGYL1ZnJLeoQ8rrc3UfmOM82sRxtzpcoCkUMy4CS0RGylfuVhuFjBgg==", - "dependencies": { - "fast-equals": "^5.0.1", - "prop-types": "^15.8.1", - "react-transition-group": "^4.4.5" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/react-style-singleton": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz", - "integrity": "sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==", - "dependencies": { - "get-nonce": "^1.0.0", - "invariant": "^2.2.4", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/react-test-renderer": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-18.2.0.tgz", - "integrity": "sha512-JWD+aQ0lh2gvh4NM3bBM42Kx+XybOxCpgYK7F8ugAlpaTSnWsX+39Z4XkOykGZAHrjwwTZT3x3KxswVWxHPUqA==", - "dev": true, - "dependencies": { - "react-is": "^18.2.0", - "react-shallow-renderer": "^16.15.0", - "scheduler": "^0.23.0" - }, - "peerDependencies": { - "react": "^18.2.0" - } - }, - "node_modules/react-test-renderer/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 - }, - "node_modules/react-toastify": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-9.1.3.tgz", - "integrity": "sha512-fPfb8ghtn/XMxw3LkxQBk3IyagNpF/LIKjOBflbexr2AWxAH1MJgvnESwEwBn9liLFXgTKWgBSdZpw9m4OTHTg==", - "dependencies": { - "clsx": "^1.1.1" - }, - "peerDependencies": { - "react": ">=16", - "react-dom": ">=16" - } - }, - "node_modules/react-toastify/node_modules/clsx": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", - "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/react-transition-group": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", - "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", - "dependencies": { - "@babel/runtime": "^7.5.5", - "dom-helpers": "^5.0.1", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2" - }, - "peerDependencies": { - "react": ">=16.6.0", - "react-dom": ">=16.6.0" - } - }, - "node_modules/read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", - "dependencies": { - "pify": "^2.3.0" - } - }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/recharts": { - "version": "2.12.3", - "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.12.3.tgz", - "integrity": "sha512-vE/F7wTlokf5mtCqVDJlVKelCjliLSJ+DJxj79XlMREm7gpV7ljwbrwE3CfeaoDlOaLX+6iwHaVRn9587YkwIg==", - "dependencies": { - "clsx": "^2.0.0", - "eventemitter3": "^4.0.1", - "lodash": "^4.17.21", - "react-is": "^16.10.2", - "react-smooth": "^4.0.0", - "recharts-scale": "^0.4.4", - "tiny-invariant": "^1.3.1", - "victory-vendor": "^36.6.8" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "react": "^16.0.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" - } - }, - "node_modules/recharts-scale": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz", - "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", - "dependencies": { - "decimal.js-light": "^2.4.1" - } - }, - "node_modules/recharts/node_modules/eventemitter3": { - "version": "4.0.7", - "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", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" - }, - "node_modules/regenerate-unicode-properties": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz", - "integrity": "sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==", - "dependencies": { - "regenerate": "^1.4.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" - }, - "node_modules/regenerator-transform": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", - "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", - "dependencies": { - "@babel/runtime": "^7.8.4" - } - }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", - "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.6", - "define-properties": "^1.2.1", - "es-errors": "^1.3.0", - "set-function-name": "^2.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/regexpu-core": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", - "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", - "dependencies": { - "@babel/regjsgen": "^0.8.0", - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.1.0", - "regjsparser": "^0.9.1", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regjsparser": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", - "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", - "dependencies": { - "jsesc": "~0.5.0" - }, - "bin": { - "regjsparser": "bin/parser" - } - }, - "node_modules/regjsparser/node_modules/jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", - "bin": { - "jsesc": "bin/jsesc" - } - }, - "node_modules/rehackt": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/rehackt/-/rehackt-0.0.6.tgz", - "integrity": "sha512-l3WEzkt4ntlEc/IB3/mF6SRgNHA6zfQR7BlGOgBTOmx7IJJXojDASav+NsgXHFjHn+6RmwqsGPFgZpabWpeOdw==", - "peerDependencies": { - "@types/react": "*", - "react": "*" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "react": { - "optional": true - } - } - }, - "node_modules/relay-runtime": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/relay-runtime/-/relay-runtime-12.0.0.tgz", - "integrity": "sha512-QU6JKr1tMsry22DXNy9Whsq5rmvwr3LSZiiWV/9+DFpuTWvp+WFhobWMc8TC4OjKFfNhEZy7mOiqUAn5atQtug==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.0.0", - "fbjs": "^3.0.0", - "invariant": "^2.2.4" - } - }, - "node_modules/remark-gfm": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.0.tgz", - "integrity": "sha512-U92vJgBPkbw4Zfu/IiW2oTZLSL3Zpv+uI7My2eq8JxKgqraFdU8YUGicEJCEgSbeaG+QDFqIcwwfMTOEelPxuA==", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-gfm": "^3.0.0", - "micromark-extension-gfm": "^3.0.0", - "remark-parse": "^11.0.0", - "remark-stringify": "^11.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-parse": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", - "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-from-markdown": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-rehype": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.0.tgz", - "integrity": "sha512-z3tJrAs2kIs1AqIIy6pzHmAHlF1hWQ+OdY4/hv+Wxe35EhyLKcajL33iUEn3ScxtFox9nUvRufR/Zre8Q08H/g==", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "mdast-util-to-hast": "^13.0.0", - "unified": "^11.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-stringify": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", - "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-to-markdown": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remeda": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/remeda/-/remeda-2.6.0.tgz", - "integrity": "sha512-uwq9c2s91Mry5YQiPGWtW9vF2DgTIVLzCIZZYz6Rcv8hsgWFQjavOaP24m9ZNF2mur+eq0x2EqRQlPBaLZuYrA==", - "dependencies": { - "type-fest": "^4.21.0" - } - }, - "node_modules/remeda/node_modules/type-fest": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.22.1.tgz", - "integrity": "sha512-9tHNEa0Ov81YOopiVkcCJVz5TM6AEQ+CHHjFIktqPnE3NV0AHIkx+gh9tiCl58m/66wWxkOC9eltpa75J4lQPA==", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/remedial": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/remedial/-/remedial-1.0.8.tgz", - "integrity": "sha512-/62tYiOe6DzS5BqVsNpH/nkGlX45C/Sp6V+NtiN6JQNS1Viay7cWkazmRkrQrdFj2eshDe96SIQNIoMxqhzBOg==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", - "dev": true - }, - "node_modules/remove-trailing-spaces": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/remove-trailing-spaces/-/remove-trailing-spaces-1.0.8.tgz", - "integrity": "sha512-O3vsMYfWighyFbTd8hk8VaSj9UAGENxAtX+//ugIst2RMk5e03h6RoIS+0ylsFxY1gvmPuAY/PO4It+gPEeySA==", - "dev": true - }, - "node_modules/request-progress": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz", - "integrity": "sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg==", - "dev": true, - "dependencies": { - "throttleit": "^1.0.0" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "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", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true - }, - "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-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", - "integrity": "sha512-pVzEEzrsg23Sh053rmDUvLSkGXluZio0qu8VT6ukrYuvtjVfCbDZH9d6PGXb8HZfzdNZt8feXv/jvUzlhRgLnw==", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rfdc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", - "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", - "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", - "integrity": "sha512-vD8HJ5raRcWOyymsR6Z3o6+RzfEPCnVLMFJ6vRslO1jt4LO6dUo5Qnpg7y4RkZFM2DMe3WUirkI5c16onjrc6A==", - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.5" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.22.4", - "@rollup/rollup-android-arm64": "4.22.4", - "@rollup/rollup-darwin-arm64": "4.22.4", - "@rollup/rollup-darwin-x64": "4.22.4", - "@rollup/rollup-linux-arm-gnueabihf": "4.22.4", - "@rollup/rollup-linux-arm-musleabihf": "4.22.4", - "@rollup/rollup-linux-arm64-gnu": "4.22.4", - "@rollup/rollup-linux-arm64-musl": "4.22.4", - "@rollup/rollup-linux-powerpc64le-gnu": "4.22.4", - "@rollup/rollup-linux-riscv64-gnu": "4.22.4", - "@rollup/rollup-linux-s390x-gnu": "4.22.4", - "@rollup/rollup-linux-x64-gnu": "4.22.4", - "@rollup/rollup-linux-x64-musl": "4.22.4", - "@rollup/rollup-win32-arm64-msvc": "4.22.4", - "@rollup/rollup-win32-ia32-msvc": "4.22.4", - "@rollup/rollup-win32-x64-msvc": "4.22.4", - "fsevents": "~2.3.2" - } - }, - "node_modules/rrweb-cssom": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz", - "integrity": "sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==", - "dev": true - }, - "node_modules/run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", - "dev": true, - "dependencies": { - "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", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/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", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "node_modules/saxes": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", - "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", - "dev": true, - "dependencies": { - "xmlchars": "^2.2.0" - }, - "engines": { - "node": ">=v12.22.7" - } - }, - "node_modules/scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", - "dependencies": { - "loose-envify": "^1.1.0" - } - }, - "node_modules/scuid": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/scuid/-/scuid-1.1.0.tgz", - "integrity": "sha512-MuCAyrGZcTLfQoH2XoBlQ8C6bzwN88XT/0slOGz0pn8+gIP85BOAfYa44ZXQUTOwRwPU0QvgU+V+OSajl/59Xg==", - "dev": true - }, - "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/sentence-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-3.0.4.tgz", - "integrity": "sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==", - "dev": true, - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3", - "upper-case-first": "^2.0.2" - } - }, - "node_modules/serialize-query-params": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/serialize-query-params/-/serialize-query-params-2.0.2.tgz", - "integrity": "sha512-1chMo1dST4pFA9RDXAtF0Rbjaut4is7bzFbI1Z26IuMub68pNCILku85aYmeFhvnY//BXUPUhoRMjYcsT93J/Q==" - }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "dev": true - }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dev": true, - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-function-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", - "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", - "dev": true, - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-value": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-4.1.0.tgz", - "integrity": "sha512-zTEg4HL0RwVrqcWs3ztF+x1vkxfm0lP+MQQFPiMJTKVceBwEV0A569Ou8l9IYQG8jOZdMVI1hGsc0tmeD2o/Lw==", - "funding": [ - "https://github.com/sponsors/jonschlinkert", - "https://paypal.me/jonathanschlinkert", - "https://jonschlinkert.dev/sponsor" - ], - "dependencies": { - "is-plain-object": "^2.0.4", - "is-primitive": "^3.0.1" - }, - "engines": { - "node": ">=11.0" - } - }, - "node_modules/setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", - "dev": true - }, - "node_modules/sha1": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/sha1/-/sha1-1.1.1.tgz", - "integrity": "sha512-dZBS6OrMjtgVkopB1Gmo4RQCDKiZsqcpAQpkV/aaj+FCrCg8r4I4qMkDPQjBgLIxlmu9k4nUbWq6ohXahOneYA==", - "dependencies": { - "charenc": ">= 0.0.1", - "crypt": ">= 0.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/shallow-equal": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-3.1.0.tgz", - "integrity": "sha512-pfVOw8QZIXpMbhBWvzBISicvToTiM5WBF1EeAUZDDSb5Dt29yl4AYbyywbJFSEsRUMr7gJaxqCdr4L3tQf9wVg==" - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "engines": { - "node": ">=8" - } - }, - "node_modules/shell-quote": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", - "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel": { - "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==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/siginfo": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", - "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", - "dev": true - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "node_modules/signedsource": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/signedsource/-/signedsource-1.0.0.tgz", - "integrity": "sha512-6+eerH9fEnNmi/hyM1DXcRK3pWdoMQtlkQ+ns0ntzunjKqp5i3sKCc80ym8Fib3iaYhdJUOPdhlJWj1tvge2Ww==", - "dev": true - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/slice-ansi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", - "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "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==", - "dependencies": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "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/source-map-js": { - "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" - } - }, - "node_modules/space-separated-tokens": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", - "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/split-on-first": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-3.0.0.tgz", - "integrity": "sha512-qxQJTx2ryR0Dw0ITYyekNQWpz6f8dGd7vffGNflQQ3Iqj9NJ6qiZ7ELpZsJ/QBhIVAiDfXdag3+Gp8RvWa62AA==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/sponge-case": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sponge-case/-/sponge-case-1.0.1.tgz", - "integrity": "sha512-dblb9Et4DAtiZ5YSUZHLl4XhH4uK80GhAZrVXdN4O2P4gQ40Wa5UIOPUHlA/nFd2PLblBZWUioLMMAVrgpoYcA==", - "dev": true, - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/sshpk": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", - "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", - "dev": true, - "dependencies": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - }, - "bin": { - "sshpk-conv": "bin/sshpk-conv", - "sshpk-sign": "bin/sshpk-sign", - "sshpk-verify": "bin/sshpk-verify" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/stackback": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", - "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", - "dev": true - }, - "node_modules/std-env": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz", - "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==", - "dev": true - }, - "node_modules/stop-iteration-iterator": { - "version": "1.0.0", - "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, - "dependencies": { - "internal-slot": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/streamsearch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", - "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", - "dev": true, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string-argv": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", - "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", - "dev": true, - "engines": { - "node": ">=0.6.19" - } - }, - "node_modules/string-env-interpolation": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string-env-interpolation/-/string-env-interpolation-1.0.1.tgz", - "integrity": "sha512-78lwMoCcn0nNu8LszbP1UA7g55OeE4v7rCeWnM5B453rnNr4aq+5it3FEYtZrSEiMvHZOZ9Jlqb0OD0M2VInqg==", - "dev": true - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "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", - "integrity": "sha512-BP9nNHMhhfcMbiuQKCqMjhDP5yBCAxsPu4pHFFzJ6Alo9dZgY4VLDPutXqIjpRiMoKdp7Av85Gr73Q5uH9k7+g==", - "dependencies": { - "character-entities-html4": "^2.0.0", - "character-entities-legacy": "^3.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "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", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "engines": { - "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", - "integrity": "sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw==", - "dev": true, - "dependencies": { - "js-tokens": "^9.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/strip-literal/node_modules/js-tokens": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.0.tgz", - "integrity": "sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==", - "dev": true - }, - "node_modules/style-mod": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.0.tgz", - "integrity": "sha512-Ca5ib8HrFn+f+0n4N4ScTIA9iTOQ7MaGS1ylHcoVqW9J7w2w8PzN6g9gKmTYgGEBH8e120+RCmhpje6jC5uGWA==" - }, - "node_modules/style-to-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.5.tgz", - "integrity": "sha512-rDRwHtoDD3UMMrmZ6BzOW0naTjMsVZLIjsGleSKS/0Oz+cgCfAPRspaqJuE8rDzpKha/nEvnM0IF4seEAZUTKQ==", - "dependencies": { - "inline-style-parser": "0.2.2" - } - }, - "node_modules/style-value-types": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/style-value-types/-/style-value-types-5.0.0.tgz", - "integrity": "sha512-08yq36Ikn4kx4YU6RD7jWEv27v4V+PUsOGa4n/as8Et3CuODMJQ00ENeAVXAeydX4Z2j1XHZF1K2sX4mGl18fA==", - "dependencies": { - "hey-listen": "^1.0.8", - "tslib": "^2.1.0" - } - }, - "node_modules/subscriptions-transport-ws": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/subscriptions-transport-ws/-/subscriptions-transport-ws-0.11.0.tgz", - "integrity": "sha512-8D4C6DIH5tGiAIpp5I0wD/xRlNiZAPGHygzCe7VzyzUoxHtawzjNAY9SUTXU05/EY2NMY9/9GF0ycizkXr1CWQ==", - "deprecated": "The `subscriptions-transport-ws` package is no longer maintained. We recommend you use `graphql-ws` instead. For help migrating Apollo software to `graphql-ws`, see https://www.apollographql.com/docs/apollo-server/data/subscriptions/#switching-from-subscriptions-transport-ws For general help using `graphql-ws`, see https://github.com/enisdenjo/graphql-ws/blob/master/README.md", - "dependencies": { - "backo2": "^1.0.2", - "eventemitter3": "^3.1.0", - "iterall": "^1.2.1", - "symbol-observable": "^1.0.4", - "ws": "^5.2.0 || ^6.0.0 || ^7.0.0" - }, - "peerDependencies": { - "graphql": "^15.7.2 || ^16.0.0" - } - }, - "node_modules/subscriptions-transport-ws/node_modules/eventemitter3": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", - "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==" - }, - "node_modules/subscriptions-transport-ws/node_modules/symbol-observable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", - "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/subscriptions-transport-ws/node_modules/ws": { - "version": "7.5.10", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", - "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/sucrase": { - "version": "3.35.0", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", - "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.2", - "commander": "^4.0.0", - "glob": "^10.3.10", - "lines-and-columns": "^1.1.6", - "mz": "^2.7.0", - "pirates": "^4.0.1", - "ts-interface-checker": "^0.1.9" - }, - "bin": { - "sucrase": "bin/sucrase", - "sucrase-node": "bin/sucrase-node" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/sucrase/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/sucrase/node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "engines": { - "node": ">= 6" - } - }, - "node_modules/sucrase/node_modules/foreground-child": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", - "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", - "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/sucrase/node_modules/glob": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", - "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.3.5", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", - "path-scurry": "^1.10.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/sucrase/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/sucrase/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==", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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==" - }, - "node_modules/svgo": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.2.0.tgz", - "integrity": "sha512-4PP6CMW/V7l/GmKRKzsLR8xxjdHTV4IMvhTnpuHwwBazSIlw5W/5SmPjN8Dwyt7lKbSJrRDgp4t9ph0HgChFBQ==", - "dependencies": { - "@trysound/sax": "0.2.0", - "commander": "^7.2.0", - "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": ">=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==", - "engines": { - "node": ">= 10" - } - }, - "node_modules/swap-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/swap-case/-/swap-case-2.0.2.tgz", - "integrity": "sha512-kc6S2YS/2yXbtkSMunBtKdah4VFETZ8Oh6ONSmSd9bRxhqTrtARUCBUiWXH3xVPpvR7tz2CSnkuXVE42EcGnMw==", - "dev": true, - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/symbol-observable": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", - "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", - "dev": true - }, - "node_modules/tailwind-merge": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.2.2.tgz", - "integrity": "sha512-tWANXsnmJzgw6mQ07nE3aCDkCK4QdT3ThPMCzawoYA2Pws7vSTCvz3Vrjg61jVUGfFZPJzxEP+NimbcW+EdaDw==", - "dependencies": { - "@babel/runtime": "^7.24.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/dcastil" - } - }, - "node_modules/tailwindcss": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.3.tgz", - "integrity": "sha512-U7sxQk/n397Bmx4JHbJx/iSOOv5G+II3f1kpLpY2QeUv5DcPdcTsYLlusZfq1NthHS1c1cZoyFmmkex1rzke0A==", - "dependencies": { - "@alloc/quick-lru": "^5.2.0", - "arg": "^5.0.2", - "chokidar": "^3.5.3", - "didyoumean": "^1.2.2", - "dlv": "^1.1.3", - "fast-glob": "^3.3.0", - "glob-parent": "^6.0.2", - "is-glob": "^4.0.3", - "jiti": "^1.21.0", - "lilconfig": "^2.1.0", - "micromatch": "^4.0.5", - "normalize-path": "^3.0.0", - "object-hash": "^3.0.0", - "picocolors": "^1.0.0", - "postcss": "^8.4.23", - "postcss-import": "^15.1.0", - "postcss-js": "^4.0.1", - "postcss-load-config": "^4.0.1", - "postcss-nested": "^6.0.1", - "postcss-selector-parser": "^6.0.11", - "resolve": "^1.22.2", - "sucrase": "^3.32.0" - }, - "bin": { - "tailwind": "lib/cli.js", - "tailwindcss": "lib/cli.js" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tailwindcss/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, - "node_modules/thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "dependencies": { - "any-promise": "^1.0.0" - } - }, - "node_modules/thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "dependencies": { - "thenify": ">= 3.1.0 < 4" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/throttleit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.1.tgz", - "integrity": "sha512-vDZpf9Chs9mAdfY046mcPt8fg5QSZr37hEH4TXYBnDF+izxgrbRGUAAaBvIk/fJm9aOFCGFd1EsNg5AZCbnQCQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true - }, - "node_modules/tiny-invariant": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", - "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 - }, - "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==", - "dev": true, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tinyspy": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.1.tgz", - "integrity": "sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==", - "dev": true, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/title-case": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/title-case/-/title-case-3.0.3.tgz", - "integrity": "sha512-e1zGYRvbffpcHIrnuqT0Dh+gEJtDaxDSoG4JAIpq4oDFyooziLBIiYQv0GBT4FUAnUop5uZ1hiIAj7oAF6sOCA==", - "dev": true, - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "dev": true, - "dependencies": { - "rimraf": "^3.0.0" - }, - "engines": { - "node": ">=8.17.0" - } - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "engines": { - "node": ">=4" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toggle-selection": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", - "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==" - }, - "node_modules/tough-cookie": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", - "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", - "dev": true, - "dependencies": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.2.0", - "url-parse": "^1.5.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tough-cookie/node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/tough-cookie/node_modules/universalify": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", - "dev": true, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/tr46": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", - "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", - "dev": true, - "dependencies": { - "punycode": "^2.3.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/tr46/node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/trim-lines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", - "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/trough": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/trough/-/trough-2.1.0.tgz", - "integrity": "sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==", - "funding": { - "type": "github", - "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", - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==" - }, - "node_modules/ts-invariant": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/ts-invariant/-/ts-invariant-0.10.3.tgz", - "integrity": "sha512-uivwYcQaxAucv1CzRp2n/QdYPo4ILf9VXgH19zEIjFx2EJufV16P0JtJVpYHy89DItG6Kwj2oIUjrcK5au+4tQ==", - "dependencies": { - "tslib": "^2.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ts-log": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/ts-log/-/ts-log-2.2.5.tgz", - "integrity": "sha512-PGcnJoTBnVGy6yYNFxWVNkdcAuAMstvutN9MgDJIV6L0oG8fB+ZNNy1T+wJzah8RPGor1mZuPQkVfXNDpy9eHA==", - "dev": true - }, - "node_modules/ts-node": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", - "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "devOptional": true, - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, - "node_modules/ts-node/node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "devOptional": true - }, - "node_modules/ts-toolbelt": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/ts-toolbelt/-/ts-toolbelt-9.6.0.tgz", - "integrity": "sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==", - "dev": true - }, - "node_modules/tsconfck": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.0.tgz", - "integrity": "sha512-CMjc5zMnyAjcS9sPLytrbFmj89st2g+JYtY/c02ug4Q+CZaAtCgbyviI0n1YvjZE/pzoc6FbNsINS13DOL1B9w==", - "bin": { - "tsconfck": "bin/tsconfck.js" - }, - "engines": { - "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" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" - }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "dev": true, - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/tweetnacl": { - "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" - } - }, - "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==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/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", - "integrity": "sha512-5PJiW/eiTPyXXBYGZOYGezMl6qj7keBiZheRwfjJZY26QPHsNrjfJnz0mru6oeqqoTHOni893Jfd6zyUXfQRWg==", - "dev": true, - "dependencies": { - "ts-toolbelt": "^9.6.0" - } - }, - "node_modules/typescript": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", - "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", - "devOptional": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "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", - "integrity": "sha512-bhTyI94tZofjo+Dn8SN6Zv8nBDvyXTymAdM3LDI/0IboIUwTu1rEhW7v2TfiVsoYWgkQ4kOVqnI8APUFbIQIFQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/ua-parser-js" - }, - { - "type": "paypal", - "url": "https://paypal.me/faisalman" - }, - { - "type": "github", - "url": "https://github.com/sponsors/faisalman" - } - ], - "engines": { - "node": "*" - } - }, - "node_modules/uc.micro": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", - "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 - }, - "node_modules/uglify-js": { - "version": "3.17.4", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", - "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", - "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "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", - "integrity": "sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "devOptional": true - }, - "node_modules/unicode-canonical-property-names-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", - "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", - "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", - "dependencies": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", - "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-property-aliases-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", - "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", - "engines": { - "node": ">=4" - } - }, - "node_modules/unidiff": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/unidiff/-/unidiff-1.0.4.tgz", - "integrity": "sha512-ynU0vsAXw0ir8roa+xPCUHmnJ5goc5BTM2Kuc3IJd8UwgaeRs7VSD5+eeaQL+xp1JtB92hu/Zy/Lgy7RZcr1pQ==", - "dependencies": { - "diff": "^5.1.0" - } - }, - "node_modules/unidiff/node_modules/diff": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", - "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/unified": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.4.tgz", - "integrity": "sha512-apMPnyLjAX+ty4OrNap7yumyVAMlKx5IWU2wlzzUdYJO9A8f1p9m/gywF/GM2ZDFcjQPrx59Mc90KwmxsoklxQ==", - "dependencies": { - "@types/unist": "^3.0.0", - "bail": "^2.0.0", - "devlop": "^1.0.0", - "extend": "^3.0.0", - "is-plain-obj": "^4.0.0", - "trough": "^2.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-is": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", - "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-position": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", - "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-remove-position": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz", - "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-visit": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-stringify-position": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", - "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", - "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", - "dependencies": { - "@types/unist": "^3.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/unist-util-visit-parents": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", - "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/unixify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unixify/-/unixify-1.0.0.tgz", - "integrity": "sha512-6bc58dPYhCMHHuwxldQxO3RRNZ4eCogZ/st++0+fcC1nr0jiGUtAdBJ2qzmLQWSxbtz42pWt4QQMiZ9HvZf5cg==", - "dev": true, - "dependencies": { - "normalize-path": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/unixify/node_modules/normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", - "dev": true, - "dependencies": { - "remove-trailing-separator": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/untildify": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", - "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/upper-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-2.0.2.tgz", - "integrity": "sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg==", - "dev": true, - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/upper-case-first": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-2.0.2.tgz", - "integrity": "sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==", - "dev": true, - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/uri-js/node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/url-parse": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "dev": true, - "dependencies": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, - "node_modules/urlpattern-polyfill": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-8.0.2.tgz", - "integrity": "sha512-Qp95D4TPJl1kC9SKigDcqgyM2VDVO4RiJc2d4qe5GrYm+zbIQCWWKAFaJNQ4BhdFeDGwBmAxqJBwWSJDb9T3BQ==", - "dev": true - }, - "node_modules/use-callback-ref": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.2.tgz", - "integrity": "sha512-elOQwe6Q8gqZgDA8mrh44qRTQqpIHDcZ3hXTLjBe1i4ph8XpNJnO+aQf3NaG+lriLopI4HMx9VjQLfPQ6vhnoA==", - "dependencies": { - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/use-query-params": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/use-query-params/-/use-query-params-2.2.1.tgz", - "integrity": "sha512-i6alcyLB8w9i3ZK3caNftdb+UnbfBRNPDnc89CNQWkGRmDrm/gfydHvMBfVsQJRq3NoHOM2dt/ceBWG2397v1Q==", - "dependencies": { - "serialize-query-params": "^2.0.2" - }, - "peerDependencies": { - "@reach/router": "^1.2.1", - "react": ">=16.8.0", - "react-dom": ">=16.8.0", - "react-router-dom": ">=5" - }, - "peerDependenciesMeta": { - "@reach/router": { - "optional": true - }, - "react-router-dom": { - "optional": true - } - } - }, - "node_modules/use-sidecar": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz", - "integrity": "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==", - "dependencies": { - "detect-node-es": "^1.1.0", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "^16.9.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "devOptional": true - }, - "node_modules/v8-to-istanbul": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz", - "integrity": "sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/value-or-promise": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/value-or-promise/-/value-or-promise-1.0.12.tgz", - "integrity": "sha512-Z6Uz+TYwEqE7ZN50gwn+1LCVo9ZVrpxRPOhOLnncYkY1ZzOYtrX8Fwf/rFktZ8R5mJms6EZf5TqNOMeZmnPq9Q==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "node_modules/vfile": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.1.tgz", - "integrity": "sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-stringify-position": "^4.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-message": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", - "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/victory-vendor": { - "version": "36.9.1", - "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.1.tgz", - "integrity": "sha512-+pZIP+U3pEJdDCeFmsXwHzV7vNHQC/eIbHklfe2ZCZqayYRH7lQbHcVgsJ0XOOv27hWs4jH4MONgXxHMObTMSA==", - "dependencies": { - "@types/d3-array": "^3.0.3", - "@types/d3-ease": "^3.0.0", - "@types/d3-interpolate": "^3.0.1", - "@types/d3-scale": "^4.0.2", - "@types/d3-shape": "^3.1.0", - "@types/d3-time": "^3.0.0", - "@types/d3-timer": "^3.0.0", - "d3-array": "^3.1.6", - "d3-ease": "^3.0.1", - "d3-interpolate": "^3.0.1", - "d3-scale": "^4.0.2", - "d3-shape": "^3.1.0", - "d3-time": "^3.0.0", - "d3-timer": "^3.0.1" - } - }, - "node_modules/vite": { - "version": "5.4.7", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.7.tgz", - "integrity": "sha512-5l2zxqMEPVENgvzTuBpHer2awaetimj2BGkhBPdnwKbPNOlHsODU+oiazEZzLK7KhAnOrO+XGYJYn4ZlUhDtDQ==", - "license": "MIT", - "dependencies": { - "esbuild": "^0.21.3", - "postcss": "^8.4.43", - "rollup": "^4.20.0" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - } - } - }, - "node_modules/vite-node": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.4.0.tgz", - "integrity": "sha512-VZDAseqjrHgNd4Kh8icYHWzTKSCZMhia7GyHfhtzLW33fZlG9SwsB6CEhgyVOWkJfJ2pFLrp/Gj1FSfAiqH9Lw==", - "dev": true, - "dependencies": { - "cac": "^6.7.14", - "debug": "^4.3.4", - "pathe": "^1.1.1", - "picocolors": "^1.0.0", - "vite": "^5.0.0" - }, - "bin": { - "vite-node": "vite-node.mjs" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/vite-tsconfig-paths": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-4.3.2.tgz", - "integrity": "sha512-0Vd/a6po6Q+86rPlntHye7F31zA2URZMbH8M3saAZ/xR9QoGN/L21bxEGfXdWmFdNkqPpRdxFT7nmNe12e9/uA==", - "dependencies": { - "debug": "^4.1.1", - "globrex": "^0.1.2", - "tsconfck": "^3.0.3" - }, - "peerDependencies": { - "vite": "*" - }, - "peerDependenciesMeta": { - "vite": { - "optional": true - } - } - }, - "node_modules/vitest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.4.0.tgz", - "integrity": "sha512-gujzn0g7fmwf83/WzrDTnncZt2UiXP41mHuFYFrdwaLRVQ6JYQEiME2IfEjU3vcFL3VKa75XhI3lFgn+hfVsQw==", - "dev": true, - "dependencies": { - "@vitest/expect": "1.4.0", - "@vitest/runner": "1.4.0", - "@vitest/snapshot": "1.4.0", - "@vitest/spy": "1.4.0", - "@vitest/utils": "1.4.0", - "acorn-walk": "^8.3.2", - "chai": "^4.3.10", - "debug": "^4.3.4", - "execa": "^8.0.1", - "local-pkg": "^0.5.0", - "magic-string": "^0.30.5", - "pathe": "^1.1.1", - "picocolors": "^1.0.0", - "std-env": "^3.5.0", - "strip-literal": "^2.0.0", - "tinybench": "^2.5.1", - "tinypool": "^0.8.2", - "vite": "^5.0.0", - "vite-node": "1.4.0", - "why-is-node-running": "^2.2.2" - }, - "bin": { - "vitest": "vitest.mjs" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "@edge-runtime/vm": "*", - "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "1.4.0", - "@vitest/ui": "1.4.0", - "happy-dom": "*", - "jsdom": "*" - }, - "peerDependenciesMeta": { - "@edge-runtime/vm": { - "optional": true - }, - "@types/node": { - "optional": true - }, - "@vitest/browser": { - "optional": true - }, - "@vitest/ui": { - "optional": true - }, - "happy-dom": { - "optional": true - }, - "jsdom": { - "optional": true - } - } - }, - "node_modules/vitest/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, - "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" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/vitest/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, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/vitest/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, - "engines": { - "node": ">=16.17.0" - } - }, - "node_modules/vitest/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, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/vitest/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, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/vitest/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, - "dependencies": { - "path-key": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/vitest/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, - "dependencies": { - "mimic-fn": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/vitest/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, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/vitest/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, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/vitest/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, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/vscode-languageserver-types": { - "version": "3.17.5", - "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", - "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==" - }, - "node_modules/w3c-xmlserializer": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", - "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", - "dev": true, - "dependencies": { - "xml-name-validator": "^5.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/warning": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", - "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", - "dependencies": { - "loose-envify": "^1.0.0" - } - }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "dev": true, - "dependencies": { - "defaults": "^1.0.3" - } - }, - "node_modules/web-streams-polyfill": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", - "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", - "dev": true, - "engines": { - "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", - "integrity": "sha512-FE+a4PPkOmBbgNDIyRmcHhgXn+2ClRl3JzJdDu/P4+B8y81LqKe6RAsI9b3lAOHe1T1BMkSjsRHTYRikImZnVA==", - "dev": true, - "dependencies": { - "@peculiar/asn1-schema": "^2.3.8", - "@peculiar/json-schema": "^1.1.12", - "asn1js": "^3.0.1", - "pvtsutils": "^1.3.5", - "tslib": "^2.6.2" - } - }, - "node_modules/webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "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==", - "dev": true, - "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==", - "dev": true, - "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==", - "dev": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/whatwg-url": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.0.0.tgz", - "integrity": "sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==", - "dev": true, - "dependencies": { - "tr46": "^5.0.0", - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dev": true, - "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - }, - "funding": { - "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", - "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", - "dev": true, - "dependencies": { - "is-map": "^2.0.1", - "is-set": "^2.0.1", - "is-weakmap": "^2.0.1", - "is-weakset": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-module": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", - "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", - "dev": true - }, - "node_modules/which-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", - "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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==", - "dev": true, - "dependencies": { - "siginfo": "^2.0.0", - "stackback": "0.0.2" - }, - "bin": { - "why-is-node-running": "cli.js" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==" - }, - "node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "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/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", - "dev": true, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/xml-name-validator": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", - "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", - "dev": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", - "dev": true - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - }, - "node_modules/yaml": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.0.tgz", - "integrity": "sha512-2wWLbGbYDiSqqIKoPjar3MPgB94ErzCtrNE1FdqGuaO0pi2JGjmE8aW8TDZwzU7vuxcGRdL/4gPQwQ7hD5AMSw==", - "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/yaml-ast-parser": { - "version": "0.0.43", - "resolved": "https://registry.npmjs.org/yaml-ast-parser/-/yaml-ast-parser-0.0.43.tgz", - "integrity": "sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==", - "dev": true - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", - "dev": true, - "dependencies": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" - } - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "devOptional": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/zen-observable": { - "version": "0.8.15", - "resolved": "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz", - "integrity": "sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==" - }, - "node_modules/zen-observable-ts": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-1.2.5.tgz", - "integrity": "sha512-QZWQekv6iB72Naeake9hS1KxHlotfRpe+WGNbNx5/ta+R3DNjVO2bswf63gXlWDcs+EMd7XY8HfVQyP1X6T4Zg==", - "dependencies": { - "zen-observable": "0.8.15" - } - }, - "node_modules/zwitch": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", - "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - } - }, - "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", - "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==" - }, - "@ampproject/remapping": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", - "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", - "requires": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@apollo/client": { - "version": "3.9.10", - "resolved": "https://registry.npmjs.org/@apollo/client/-/client-3.9.10.tgz", - "integrity": "sha512-w8i/Lk1P0vvWZF0Xb00XPonn79/0rgRJ1vopBlVudVuy9QP29/NZXK0rI2xJIN6VrKuEqJZaVGJC+7k23I2sfA==", - "requires": { - "@graphql-typed-document-node/core": "^3.1.1", - "@wry/caches": "^1.0.0", - "@wry/equality": "^0.5.6", - "@wry/trie": "^0.5.0", - "graphql-tag": "^2.12.6", - "hoist-non-react-statics": "^3.3.2", - "optimism": "^0.18.0", - "prop-types": "^15.7.2", - "rehackt": "0.0.6", - "response-iterator": "^0.2.6", - "symbol-observable": "^4.0.0", - "ts-invariant": "^0.10.3", - "tslib": "^2.3.0", - "zen-observable-ts": "^1.2.5" - } - }, - "@ardatan/relay-compiler": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@ardatan/relay-compiler/-/relay-compiler-12.0.0.tgz", - "integrity": "sha512-9anThAaj1dQr6IGmzBMcfzOQKTa5artjuPmw8NYK/fiGEMjADbSguBY2FMDykt+QhilR3wc9VA/3yVju7JHg7Q==", - "dev": true, - "requires": { - "@babel/core": "^7.14.0", - "@babel/generator": "^7.14.0", - "@babel/parser": "^7.14.0", - "@babel/runtime": "^7.0.0", - "@babel/traverse": "^7.14.0", - "@babel/types": "^7.0.0", - "babel-preset-fbjs": "^3.4.0", - "chalk": "^4.0.0", - "fb-watchman": "^2.0.0", - "fbjs": "^3.0.0", - "glob": "^7.1.1", - "immutable": "~3.7.6", - "invariant": "^2.2.4", - "nullthrows": "^1.1.1", - "relay-runtime": "12.0.0", - "signedsource": "^1.0.0", - "yargs": "^15.3.1" - }, - "dependencies": { - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "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" - } - }, - "y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true - }, - "yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "dev": true, - "requires": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - } - }, - "yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } - } - }, - "@ardatan/sync-fetch": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/@ardatan/sync-fetch/-/sync-fetch-0.0.1.tgz", - "integrity": "sha512-xhlTqH0m31mnsG0tIP4ETgfSB6gXDaYYsUWTrlUV93fFQPI9dd8hE0Ot6MHLCtqgB32hwJAC3YZMWlXZw7AleA==", - "dev": true, - "requires": { - "node-fetch": "^2.6.1" - } - }, - "@babel/code-frame": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", - "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", - "requires": { - "@babel/highlight": "^7.23.4", - "chalk": "^2.4.2" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, - "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==" - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@babel/compat-data": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.1.tgz", - "integrity": "sha512-Pc65opHDliVpRHuKfzI+gSA4zcgr65O4cl64fFJIWEEh8JoHIHh0Oez1Eo8Arz8zq/JhgKodQaxEwUPRtZylVA==" - }, - "@babel/core": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.9.tgz", - "integrity": "sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw==", - "requires": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.6", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helpers": "^7.23.9", - "@babel/parser": "^7.23.9", - "@babel/template": "^7.23.9", - "@babel/traverse": "^7.23.9", - "@babel/types": "^7.23.9", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - } - }, - "@babel/generator": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", - "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", - "requires": { - "@babel/types": "^7.23.6", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" - } - }, - "@babel/helper-annotate-as-pure": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", - "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz", - "integrity": "sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==", - "requires": { - "@babel/types": "^7.22.15" - } - }, - "@babel/helper-compilation-targets": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", - "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", - "requires": { - "@babel/compat-data": "^7.23.5", - "@babel/helper-validator-option": "^7.23.5", - "browserslist": "^4.22.2", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - } - }, - "@babel/helper-create-class-features-plugin": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.1.tgz", - "integrity": "sha512-1yJa9dX9g//V6fDebXoEfEsxkZHk3Hcbm+zLhyu6qVgYFLvmTALTeV+jNU9e5RnYtioBrGEOdoI2joMSNQ/+aA==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-member-expression-to-functions": "^7.23.0", - "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-replace-supers": "^7.24.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "semver": "^6.3.1" - } - }, - "@babel/helper-create-regexp-features-plugin": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz", - "integrity": "sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "regexpu-core": "^5.3.1", - "semver": "^6.3.1" - } - }, - "@babel/helper-define-polyfill-provider": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.1.tgz", - "integrity": "sha512-o7SDgTJuvx5vLKD6SFvkydkSMBvahDKGiNJzG22IZYXhiqoe9efY7zocICBgzHV4IRg5wdgl2nEL/tulKIEIbA==", - "requires": { - "@babel/helper-compilation-targets": "^7.22.6", - "@babel/helper-plugin-utils": "^7.22.5", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2" - } - }, - "@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==" - }, - "@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", - "requires": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz", - "integrity": "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==", - "requires": { - "@babel/types": "^7.23.0" - } - }, - "@babel/helper-module-imports": { - "version": "7.24.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz", - "integrity": "sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==", - "requires": { - "@babel/types": "^7.24.0" - } - }, - "@babel/helper-module-transforms": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", - "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", - "requires": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.20" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", - "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.0.tgz", - "integrity": "sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w==" - }, - "@babel/helper-remap-async-to-generator": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz", - "integrity": "sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-wrap-function": "^7.22.20" - } - }, - "@babel/helper-replace-supers": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.24.1.tgz", - "integrity": "sha512-QCR1UqC9BzG5vZl8BMicmZ28RuUBnHhAMddD8yHFHDRH9lLTZ9uUPehX8ctVPT8l0TKblJidqcgUUKGVrePleQ==", - "requires": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-member-expression-to-functions": "^7.23.0", - "@babel/helper-optimise-call-expression": "^7.22.5" - } - }, - "@babel/helper-simple-access": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", - "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", - "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-string-parser": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", - "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==" - }, - "@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==" - }, - "@babel/helper-validator-option": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", - "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==" - }, - "@babel/helper-wrap-function": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.20.tgz", - "integrity": "sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw==", - "requires": { - "@babel/helper-function-name": "^7.22.5", - "@babel/template": "^7.22.15", - "@babel/types": "^7.22.19" - } - }, - "@babel/helpers": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.9.tgz", - "integrity": "sha512-87ICKgU5t5SzOT7sBMfCOZQ2rHjRU+Pcb9BoILMYz600W6DkVRLFBPwQ18gwUVvggqXivaUakpnxWQGbpywbBQ==", - "requires": { - "@babel/template": "^7.23.9", - "@babel/traverse": "^7.23.9", - "@babel/types": "^7.23.9" - } - }, - "@babel/highlight": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", - "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", - "requires": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" - }, - "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==" - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@babel/parser": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.1.tgz", - "integrity": "sha512-Zo9c7N3xdOIQrNip7Lc9wvRPzlRtovHVE4lkz8WEDr7uYh/GMQhSiIgFxGIArRHYdJE5kxtZjAf8rT0xhdLCzg==" - }, - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.24.1.tgz", - "integrity": "sha512-y4HqEnkelJIOQGd+3g1bTeKsA5c6qM7eOn7VggGVbBc0y8MLSKHacwcIE2PplNlQSj0PqS9rrXL/nkPVK+kUNg==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.24.1.tgz", - "integrity": "sha512-Hj791Ii4ci8HqnaKHAlLNs+zaLXb0EzSDhiAWp5VNlyvCNymYfacs64pxTxbH1znW/NcArSmwpmG9IKE/TUVVQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/plugin-transform-optional-chaining": "^7.24.1" - } - }, - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.24.1.tgz", - "integrity": "sha512-m9m/fXsXLiHfwdgydIFnpk+7jlVbnvlK5B2EKiPdLUb6WX654ZaaEWJUjk8TftRbZpK0XibovlLWX4KIZhV6jw==", - "requires": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-proposal-class-properties": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", - "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-proposal-object-rest-spread": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz", - "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.20.5", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.20.7" - } - }, - "@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.0-placeholder-for-preset-env.2", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", - "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", - "requires": {} - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-syntax-flow": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.24.1.tgz", - "integrity": "sha512-sxi2kLTI5DeW5vDtMUsk4mTPwvlUDbjOnoWayhynCwrw4QXRld4QEYwqzY8JmQXaJUtgUuCIurtSRH5sn4c7mA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-syntax-import-assertions": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.24.1.tgz", - "integrity": "sha512-IuwnI5XnuF189t91XbxmXeCDz3qs6iDRO7GJ++wcfgeXNs/8FmIlKcpDSXNVyuLQxlwvskmI3Ct73wUODkJBlQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-syntax-import-attributes": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.24.1.tgz", - "integrity": "sha512-zhQTMH0X2nVLnb04tz+s7AMuasX8U0FnpE+nHTOhSOINjWMnopoZTxtIKsd45n4GQ/HIZLyfIpoul8e2m0DnRA==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-jsx": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.1.tgz", - "integrity": "sha512-2eCtxZXf+kbkMIsXS4poTvT4Yu5rXiRa+9xGVT56raghjmBTKMpFNc9R4IDiB4emao9eO22Ox7CxuJG7BgExqA==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-typescript": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.1.tgz", - "integrity": "sha512-Yhnmvy5HZEnHUty6i++gcfH1/l68AHnItFHnaCv6hn9dNh0hQvvQJsxpi4BMBFN5DLeHBuucT/0DgzXif/OyRw==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-syntax-unicode-sets-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", - "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - } - }, - "@babel/plugin-transform-arrow-functions": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.24.1.tgz", - "integrity": "sha512-ngT/3NkRhsaep9ck9uj2Xhv9+xB1zShY3tM3g6om4xxCELwCDN4g4Aq5dRn48+0hasAql7s2hdBOysCfNpr4fw==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-async-generator-functions": { - "version": "7.24.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.24.3.tgz", - "integrity": "sha512-Qe26CMYVjpQxJ8zxM1340JFNjZaF+ISWpr1Kt/jGo+ZTUzKkfw/pphEWbRCb+lmSM6k/TOgfYLvmbHkUQ0asIg==", - "requires": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-remap-async-to-generator": "^7.22.20", - "@babel/plugin-syntax-async-generators": "^7.8.4" - } - }, - "@babel/plugin-transform-async-to-generator": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.24.1.tgz", - "integrity": "sha512-AawPptitRXp1y0n4ilKcGbRYWfbbzFWz2NqNu7dacYDtFtz0CMjG64b3LQsb3KIgnf4/obcUL78hfaOS7iCUfw==", - "requires": { - "@babel/helper-module-imports": "^7.24.1", - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-remap-async-to-generator": "^7.22.20" - } - }, - "@babel/plugin-transform-block-scoped-functions": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.24.1.tgz", - "integrity": "sha512-TWWC18OShZutrv9C6mye1xwtam+uNi2bnTOCBUd5sZxyHOiWbU6ztSROofIMrK84uweEZC219POICK/sTYwfgg==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-block-scoping": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.1.tgz", - "integrity": "sha512-h71T2QQvDgM2SmT29UYU6ozjMlAt7s7CSs5Hvy8f8cf/GM/Z4a2zMfN+fjVGaieeCrXR3EdQl6C4gQG+OgmbKw==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-class-properties": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.24.1.tgz", - "integrity": "sha512-OMLCXi0NqvJfORTaPQBwqLXHhb93wkBKZ4aNwMl6WtehO7ar+cmp+89iPEQPqxAnxsOKTaMcs3POz3rKayJ72g==", - "requires": { - "@babel/helper-create-class-features-plugin": "^7.24.1", - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-class-static-block": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.24.1.tgz", - "integrity": "sha512-FUHlKCn6J3ERiu8Dv+4eoz7w8+kFLSyeVG4vDAikwADGjUCoHw/JHokyGtr8OR4UjpwPVivyF+h8Q5iv/JmrtA==", - "requires": { - "@babel/helper-create-class-features-plugin": "^7.24.1", - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/plugin-syntax-class-static-block": "^7.14.5" - } - }, - "@babel/plugin-transform-classes": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.24.1.tgz", - "integrity": "sha512-ZTIe3W7UejJd3/3R4p7ScyyOoafetUShSf4kCqV0O7F/RiHxVj/wRaRnQlrGwflvcehNA8M42HkAiEDYZu2F1Q==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-replace-supers": "^7.24.1", - "@babel/helper-split-export-declaration": "^7.22.6", - "globals": "^11.1.0" - } - }, - "@babel/plugin-transform-computed-properties": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.24.1.tgz", - "integrity": "sha512-5pJGVIUfJpOS+pAqBQd+QMaTD2vCL/HcePooON6pDpHgRp4gNRmzyHTPIkXntwKsq3ayUFVfJaIKPw2pOkOcTw==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/template": "^7.24.0" - } - }, - "@babel/plugin-transform-destructuring": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.24.1.tgz", - "integrity": "sha512-ow8jciWqNxR3RYbSNVuF4U2Jx130nwnBnhRw6N6h1bOejNkABmcI5X5oz29K4alWX7vf1C+o6gtKXikzRKkVdw==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-dotall-regex": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.24.1.tgz", - "integrity": "sha512-p7uUxgSoZwZ2lPNMzUkqCts3xlp8n+o05ikjy7gbtFJSt9gdU88jAmtfmOxHM14noQXBxfgzf2yRWECiNVhTCw==", - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-duplicate-keys": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.24.1.tgz", - "integrity": "sha512-msyzuUnvsjsaSaocV6L7ErfNsa5nDWL1XKNnDePLgmz+WdU4w/J8+AxBMrWfi9m4IxfL5sZQKUPQKDQeeAT6lA==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-dynamic-import": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.24.1.tgz", - "integrity": "sha512-av2gdSTyXcJVdI+8aFZsCAtR29xJt0S5tas+Ef8NvBNmD1a+N/3ecMLeMBgfcK+xzsjdLDT6oHt+DFPyeqUbDA==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" - } - }, - "@babel/plugin-transform-exponentiation-operator": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.24.1.tgz", - "integrity": "sha512-U1yX13dVBSwS23DEAqU+Z/PkwE9/m7QQy8Y9/+Tdb8UWYaGNDYwTLi19wqIAiROr8sXVum9A/rtiH5H0boUcTw==", - "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.15", - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-export-namespace-from": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.24.1.tgz", - "integrity": "sha512-Ft38m/KFOyzKw2UaJFkWG9QnHPG/Q/2SkOrRk4pNBPg5IPZ+dOxcmkK5IyuBcxiNPyyYowPGUReyBvrvZs7IlQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" - } - }, - "@babel/plugin-transform-flow-strip-types": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.24.1.tgz", - "integrity": "sha512-iIYPIWt3dUmUKKE10s3W+jsQ3icFkw0JyRVyY1B7G4yK/nngAOHLVx8xlhA6b/Jzl/Y0nis8gjqhqKtRDQqHWQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/plugin-syntax-flow": "^7.24.1" - } - }, - "@babel/plugin-transform-for-of": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.24.1.tgz", - "integrity": "sha512-OxBdcnF04bpdQdR3i4giHZNZQn7cm8RQKcSwA17wAAqEELo1ZOwp5FFgeptWUQXFyT9kwHo10aqqauYkRZPCAg==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" - } - }, - "@babel/plugin-transform-function-name": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.24.1.tgz", - "integrity": "sha512-BXmDZpPlh7jwicKArQASrj8n22/w6iymRnvHYYd2zO30DbE277JO20/7yXJT3QxDPtiQiOxQBbZH4TpivNXIxA==", - "requires": { - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-json-strings": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.24.1.tgz", - "integrity": "sha512-U7RMFmRvoasscrIFy5xA4gIp8iWnWubnKkKuUGJjsuOH7GfbMkB+XZzeslx2kLdEGdOJDamEmCqOks6e8nv8DQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/plugin-syntax-json-strings": "^7.8.3" - } - }, - "@babel/plugin-transform-literals": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.24.1.tgz", - "integrity": "sha512-zn9pwz8U7nCqOYIiBaOxoQOtYmMODXTJnkxG4AtX8fPmnCRYWBOHD0qcpwS9e2VDSp1zNJYpdnFMIKb8jmwu6g==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-logical-assignment-operators": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.24.1.tgz", - "integrity": "sha512-OhN6J4Bpz+hIBqItTeWJujDOfNP+unqv/NJgyhlpSqgBTPm37KkMmZV6SYcOj+pnDbdcl1qRGV/ZiIjX9Iy34w==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" - } - }, - "@babel/plugin-transform-member-expression-literals": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.24.1.tgz", - "integrity": "sha512-4ojai0KysTWXzHseJKa1XPNXKRbuUrhkOPY4rEGeR+7ChlJVKxFa3H3Bz+7tWaGKgJAXUWKOGmltN+u9B3+CVg==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-modules-amd": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.24.1.tgz", - "integrity": "sha512-lAxNHi4HVtjnHd5Rxg3D5t99Xm6H7b04hUS7EHIXcUl2EV4yl1gWdqZrNzXnSrHveL9qMdbODlLF55mvgjAfaQ==", - "requires": { - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-modules-commonjs": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.24.1.tgz", - "integrity": "sha512-szog8fFTUxBfw0b98gEWPaEqF42ZUD/T3bkynW/wtgx2p/XCP55WEsb+VosKceRSd6njipdZvNogqdtI4Q0chw==", - "requires": { - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-simple-access": "^7.22.5" - } - }, - "@babel/plugin-transform-modules-systemjs": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.24.1.tgz", - "integrity": "sha512-mqQ3Zh9vFO1Tpmlt8QPnbwGHzNz3lpNEMxQb1kAemn/erstyqw1r9KeOlOfo3y6xAnFEcOv2tSyrXfmMk+/YZA==", - "requires": { - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-validator-identifier": "^7.22.20" - } - }, - "@babel/plugin-transform-modules-umd": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.24.1.tgz", - "integrity": "sha512-tuA3lpPj+5ITfcCluy6nWonSL7RvaG0AOTeAuvXqEKS34lnLzXpDb0dcP6K8jD0zWZFNDVly90AGFJPnm4fOYg==", - "requires": { - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz", - "integrity": "sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==", - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-transform-new-target": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.24.1.tgz", - "integrity": "sha512-/rurytBM34hYy0HKZQyA0nHbQgQNFm4Q/BOc9Hflxi2X3twRof7NaE5W46j4kQitm7SvACVRXsa6N/tSZxvPug==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.24.1.tgz", - "integrity": "sha512-iQ+caew8wRrhCikO5DrUYx0mrmdhkaELgFa+7baMcVuhxIkN7oxt06CZ51D65ugIb1UWRQ8oQe+HXAVM6qHFjw==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" - } - }, - "@babel/plugin-transform-numeric-separator": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.24.1.tgz", - "integrity": "sha512-7GAsGlK4cNL2OExJH1DzmDeKnRv/LXq0eLUSvudrehVA5Rgg4bIrqEUW29FbKMBRT0ztSqisv7kjP+XIC4ZMNw==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" - } - }, - "@babel/plugin-transform-object-rest-spread": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.24.1.tgz", - "integrity": "sha512-XjD5f0YqOtebto4HGISLNfiNMTTs6tbkFf2TOqJlYKYmbo+mN9Dnpl4SRoofiziuOWMIyq3sZEUqLo3hLITFEA==", - "requires": { - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.24.1" - } - }, - "@babel/plugin-transform-object-super": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.24.1.tgz", - "integrity": "sha512-oKJqR3TeI5hSLRxudMjFQ9re9fBVUU0GICqM3J1mi8MqlhVr6hC/ZN4ttAyMuQR6EZZIY6h/exe5swqGNNIkWQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-replace-supers": "^7.24.1" - } - }, - "@babel/plugin-transform-optional-catch-binding": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.24.1.tgz", - "integrity": "sha512-oBTH7oURV4Y+3EUrf6cWn1OHio3qG/PVwO5J03iSJmBg6m2EhKjkAu/xuaXaYwWW9miYtvbWv4LNf0AmR43LUA==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" - } - }, - "@babel/plugin-transform-optional-chaining": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.24.1.tgz", - "integrity": "sha512-n03wmDt+987qXwAgcBlnUUivrZBPZ8z1plL0YvgQalLm+ZE5BMhGm94jhxXtA1wzv1Cu2aaOv1BM9vbVttrzSg==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - } - }, - "@babel/plugin-transform-parameters": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.24.1.tgz", - "integrity": "sha512-8Jl6V24g+Uw5OGPeWNKrKqXPDw2YDjLc53ojwfMcKwlEoETKU9rU0mHUtcg9JntWI/QYzGAXNWEcVHZ+fR+XXg==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-private-methods": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.24.1.tgz", - "integrity": "sha512-tGvisebwBO5em4PaYNqt4fkw56K2VALsAbAakY0FjTYqJp7gfdrgr7YX76Or8/cpik0W6+tj3rZ0uHU9Oil4tw==", - "requires": { - "@babel/helper-create-class-features-plugin": "^7.24.1", - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-private-property-in-object": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.24.1.tgz", - "integrity": "sha512-pTHxDVa0BpUbvAgX3Gat+7cSciXqUcY9j2VZKTbSB6+VQGpNgNO9ailxTGHSXlqOnX1Hcx1Enme2+yv7VqP9bg==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.24.1", - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" - } - }, - "@babel/plugin-transform-property-literals": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.24.1.tgz", - "integrity": "sha512-LetvD7CrHmEx0G442gOomRr66d7q8HzzGGr4PMHGr+5YIm6++Yke+jxj246rpvsbyhJwCLxcTn6zW1P1BSenqA==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-react-constant-elements": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.24.1.tgz", - "integrity": "sha512-QXp1U9x0R7tkiGB0FOk8o74jhnap0FlZ5gNkRIWdG3eP+SvMFg118e1zaWewDzgABb106QSKpVsD3Wgd8t6ifA==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-react-display-name": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.24.1.tgz", - "integrity": "sha512-mvoQg2f9p2qlpDQRBC7M3c3XTr0k7cp/0+kFKKO/7Gtu0LSw16eKB+Fabe2bDT/UpsyasTBBkAnbdsLrkD5XMw==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-react-jsx": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.23.4.tgz", - "integrity": "sha512-5xOpoPguCZCRbo/JeHlloSkTA8Bld1J/E1/kLfD1nsuiW1m8tduTA1ERCgIZokDflX/IBzKcqR3l7VlRgiIfHA==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/plugin-syntax-jsx": "^7.23.3", - "@babel/types": "^7.23.4" - } - }, - "@babel/plugin-transform-react-jsx-development": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.22.5.tgz", - "integrity": "sha512-bDhuzwWMuInwCYeDeMzyi7TaBgRQei6DqxhbyniL7/VG4RSS7HtSL2QbY4eESy1KJqlWt8g3xeEBGPuo+XqC8A==", - "requires": { - "@babel/plugin-transform-react-jsx": "^7.22.5" - } - }, - "@babel/plugin-transform-react-jsx-self": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.23.3.tgz", - "integrity": "sha512-qXRvbeKDSfwnlJnanVRp0SfuWE5DQhwQr5xtLBzp56Wabyo+4CMosF6Kfp+eOD/4FYpql64XVJ2W0pVLlJZxOQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-transform-react-jsx-source": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.23.3.tgz", - "integrity": "sha512-91RS0MDnAWDNvGC6Wio5XYkyWI39FMFO+JK9+4AlgaTH+yWwVTsw7/sn6LK0lH7c5F+TFkpv/3LfCJ1Ydwof/g==", - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-transform-react-pure-annotations": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.24.1.tgz", - "integrity": "sha512-+pWEAaDJvSm9aFvJNpLiM2+ktl2Sn2U5DdyiWdZBxmLc6+xGt88dvFqsHiAiDS+8WqUwbDfkKz9jRxK3M0k+kA==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-regenerator": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.24.1.tgz", - "integrity": "sha512-sJwZBCzIBE4t+5Q4IGLaaun5ExVMRY0lYwos/jNecjMrVCygCdph3IKv0tkP5Fc87e/1+bebAmEAGBfnRD+cnw==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.0", - "regenerator-transform": "^0.15.2" - } - }, - "@babel/plugin-transform-reserved-words": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.24.1.tgz", - "integrity": "sha512-JAclqStUfIwKN15HrsQADFgeZt+wexNQ0uLhuqvqAUFoqPMjEcFCYZBhq0LUdz6dZK/mD+rErhW71fbx8RYElg==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-shorthand-properties": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.24.1.tgz", - "integrity": "sha512-LyjVB1nsJ6gTTUKRjRWx9C1s9hE7dLfP/knKdrfeH9UPtAGjYGgxIbFfx7xyLIEWs7Xe1Gnf8EWiUqfjLhInZA==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-spread": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.24.1.tgz", - "integrity": "sha512-KjmcIM+fxgY+KxPVbjelJC6hrH1CgtPmTvdXAfn3/a9CnWGSTY7nH4zm5+cjmWJybdcPSsD0++QssDsjcpe47g==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" - } - }, - "@babel/plugin-transform-sticky-regex": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.24.1.tgz", - "integrity": "sha512-9v0f1bRXgPVcPrngOQvLXeGNNVLc8UjMVfebo9ka0WF3/7+aVUHmaJVT3sa0XCzEFioPfPHZiOcYG9qOsH63cw==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-template-literals": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.24.1.tgz", - "integrity": "sha512-WRkhROsNzriarqECASCNu/nojeXCDTE/F2HmRgOzi7NGvyfYGq1NEjKBK3ckLfRgGc6/lPAqP0vDOSw3YtG34g==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-typeof-symbol": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.24.1.tgz", - "integrity": "sha512-CBfU4l/A+KruSUoW+vTQthwcAdwuqbpRNB8HQKlZABwHRhsdHZ9fezp4Sn18PeAlYxTNiLMlx4xUBV3AWfg1BA==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-typescript": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.24.1.tgz", - "integrity": "sha512-liYSESjX2fZ7JyBFkYG78nfvHlMKE6IpNdTVnxmlYUR+j5ZLsitFbaAE+eJSK2zPPkNWNw4mXL51rQ8WrvdK0w==", - "requires": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-create-class-features-plugin": "^7.24.1", - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/plugin-syntax-typescript": "^7.24.1" - } - }, - "@babel/plugin-transform-unicode-escapes": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.24.1.tgz", - "integrity": "sha512-RlkVIcWT4TLI96zM660S877E7beKlQw7Ig+wqkKBiWfj0zH5Q4h50q6er4wzZKRNSYpfo6ILJ+hrJAGSX2qcNw==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-unicode-property-regex": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.24.1.tgz", - "integrity": "sha512-Ss4VvlfYV5huWApFsF8/Sq0oXnGO+jB+rijFEFugTd3cwSObUSnUi88djgR5528Csl0uKlrI331kRqe56Ov2Ng==", - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-unicode-regex": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.24.1.tgz", - "integrity": "sha512-2A/94wgZgxfTsiLaQ2E36XAOdcZmGAaEEgVmxQWwZXWkGhvoHbaqXcKnU8zny4ycpu3vNqg0L/PcCiYtHtA13g==", - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/plugin-transform-unicode-sets-regex": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.24.1.tgz", - "integrity": "sha512-fqj4WuzzS+ukpgerpAoOnMfQXwUHFxXUZUE84oL2Kao2N8uSlvcpnAidKASgsNgzZHBsHWvcm8s9FPWUhAb8fA==", - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.24.0" - } - }, - "@babel/preset-env": { - "version": "7.24.3", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.3.tgz", - "integrity": "sha512-fSk430k5c2ff8536JcPvPWK4tZDwehWLGlBp0wrsBUjZVdeQV6lePbwKWZaZfK2vnh/1kQX1PzAJWsnBmVgGJA==", - "requires": { - "@babel/compat-data": "^7.24.1", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-validator-option": "^7.23.5", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.24.1", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.1", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.24.1", - "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.24.1", - "@babel/plugin-syntax-import-attributes": "^7.24.1", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5", - "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.24.1", - "@babel/plugin-transform-async-generator-functions": "^7.24.3", - "@babel/plugin-transform-async-to-generator": "^7.24.1", - "@babel/plugin-transform-block-scoped-functions": "^7.24.1", - "@babel/plugin-transform-block-scoping": "^7.24.1", - "@babel/plugin-transform-class-properties": "^7.24.1", - "@babel/plugin-transform-class-static-block": "^7.24.1", - "@babel/plugin-transform-classes": "^7.24.1", - "@babel/plugin-transform-computed-properties": "^7.24.1", - "@babel/plugin-transform-destructuring": "^7.24.1", - "@babel/plugin-transform-dotall-regex": "^7.24.1", - "@babel/plugin-transform-duplicate-keys": "^7.24.1", - "@babel/plugin-transform-dynamic-import": "^7.24.1", - "@babel/plugin-transform-exponentiation-operator": "^7.24.1", - "@babel/plugin-transform-export-namespace-from": "^7.24.1", - "@babel/plugin-transform-for-of": "^7.24.1", - "@babel/plugin-transform-function-name": "^7.24.1", - "@babel/plugin-transform-json-strings": "^7.24.1", - "@babel/plugin-transform-literals": "^7.24.1", - "@babel/plugin-transform-logical-assignment-operators": "^7.24.1", - "@babel/plugin-transform-member-expression-literals": "^7.24.1", - "@babel/plugin-transform-modules-amd": "^7.24.1", - "@babel/plugin-transform-modules-commonjs": "^7.24.1", - "@babel/plugin-transform-modules-systemjs": "^7.24.1", - "@babel/plugin-transform-modules-umd": "^7.24.1", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", - "@babel/plugin-transform-new-target": "^7.24.1", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.1", - "@babel/plugin-transform-numeric-separator": "^7.24.1", - "@babel/plugin-transform-object-rest-spread": "^7.24.1", - "@babel/plugin-transform-object-super": "^7.24.1", - "@babel/plugin-transform-optional-catch-binding": "^7.24.1", - "@babel/plugin-transform-optional-chaining": "^7.24.1", - "@babel/plugin-transform-parameters": "^7.24.1", - "@babel/plugin-transform-private-methods": "^7.24.1", - "@babel/plugin-transform-private-property-in-object": "^7.24.1", - "@babel/plugin-transform-property-literals": "^7.24.1", - "@babel/plugin-transform-regenerator": "^7.24.1", - "@babel/plugin-transform-reserved-words": "^7.24.1", - "@babel/plugin-transform-shorthand-properties": "^7.24.1", - "@babel/plugin-transform-spread": "^7.24.1", - "@babel/plugin-transform-sticky-regex": "^7.24.1", - "@babel/plugin-transform-template-literals": "^7.24.1", - "@babel/plugin-transform-typeof-symbol": "^7.24.1", - "@babel/plugin-transform-unicode-escapes": "^7.24.1", - "@babel/plugin-transform-unicode-property-regex": "^7.24.1", - "@babel/plugin-transform-unicode-regex": "^7.24.1", - "@babel/plugin-transform-unicode-sets-regex": "^7.24.1", - "@babel/preset-modules": "0.1.6-no-external-plugins", - "babel-plugin-polyfill-corejs2": "^0.4.10", - "babel-plugin-polyfill-corejs3": "^0.10.4", - "babel-plugin-polyfill-regenerator": "^0.6.1", - "core-js-compat": "^3.31.0", - "semver": "^6.3.1" - } - }, - "@babel/preset-modules": { - "version": "0.1.6-no-external-plugins", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", - "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - } - }, - "@babel/preset-react": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.24.1.tgz", - "integrity": "sha512-eFa8up2/8cZXLIpkafhaADTXSnl7IsUFCYenRWrARBz0/qZwcT0RBXpys0LJU4+WfPoF2ZG6ew6s2V6izMCwRA==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-validator-option": "^7.23.5", - "@babel/plugin-transform-react-display-name": "^7.24.1", - "@babel/plugin-transform-react-jsx": "^7.23.4", - "@babel/plugin-transform-react-jsx-development": "^7.22.5", - "@babel/plugin-transform-react-pure-annotations": "^7.24.1" - } - }, - "@babel/preset-typescript": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.24.1.tgz", - "integrity": "sha512-1DBaMmRDpuYQBPWD8Pf/WEwCrtgRHxsZnP4mIy9G/X+hFfbI47Q2G4t1Paakld84+qsk2fSsUPMKg71jkoOOaQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.24.0", - "@babel/helper-validator-option": "^7.23.5", - "@babel/plugin-syntax-jsx": "^7.24.1", - "@babel/plugin-transform-modules-commonjs": "^7.24.1", - "@babel/plugin-transform-typescript": "^7.24.1" - } - }, - "@babel/regjsgen": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" - }, - "@babel/runtime": { - "version": "7.24.1", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.1.tgz", - "integrity": "sha512-+BIznRzyqBf+2wCTxcKE3wDjfGeCoVE61KSHGpkzqrLi8qxqFwBeUFyId2cxkTmm55fzDGnm0+yCxaxygrLUnQ==", - "requires": { - "regenerator-runtime": "^0.14.0" - } - }, - "@babel/template": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz", - "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==", - "requires": { - "@babel/code-frame": "^7.23.5", - "@babel/parser": "^7.24.0", - "@babel/types": "^7.24.0" - } - }, - "@babel/traverse": { - "version": "7.23.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.9.tgz", - "integrity": "sha512-I/4UJ9vs90OkBtY6iiiTORVMyIhJ4kAVmsKo9KFc8UOxMeUfi2hvtIBsET5u9GizXE6/GFSuKCTNfgCswuEjRg==", - "requires": { - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.6", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.9", - "@babel/types": "^7.23.9", - "debug": "^4.3.1", - "globals": "^11.1.0" - } - }, - "@babel/types": { - "version": "7.24.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", - "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", - "requires": { - "@babel/helper-string-parser": "^7.23.4", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" - } - }, - "@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "@codemirror/autocomplete": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.2.0.tgz", - "integrity": "sha512-yNCm2CEE4kE4L2Sf7WeyCej1Q3951ccaCWfomrlBkoERKCss+TzuEeqGe5VnAJTEybLy1yzf1BdMUY/988bfpg==", - "requires": { - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", - "@lezer/common": "^1.0.0" - } - }, - "@codemirror/commands": { - "version": "6.3.3", - "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.3.3.tgz", - "integrity": "sha512-dO4hcF0fGT9tu1Pj1D2PvGvxjeGkbC6RGcZw6Qs74TH+Ed1gw98jmUgd2axWvIZEqTeTuFrg1lEB1KV6cK9h1A==", - "requires": { - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.4.0", - "@codemirror/view": "^6.0.0", - "@lezer/common": "^1.1.0" - } - }, - "@codemirror/lang-css": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-6.2.1.tgz", - "integrity": "sha512-/UNWDNV5Viwi/1lpr/dIXJNWiwDxpw13I4pTUAsNxZdg6E0mI2kTQb0P2iHczg1Tu+H4EBgJR+hYhKiHKko7qg==", - "requires": { - "@codemirror/autocomplete": "^6.0.0", - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@lezer/common": "^1.0.2", - "@lezer/css": "^1.0.0" - } - }, - "@codemirror/lang-html": { - "version": "6.4.8", - "resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.8.tgz", - "integrity": "sha512-tE2YK7wDlb9ZpAH6mpTPiYm6rhfdQKVDa5r9IwIFlwwgvVaKsCfuKKZoJGWsmMZIf3FQAuJ5CHMPLymOtg1hXw==", - "requires": { - "@codemirror/autocomplete": "^6.0.0", - "@codemirror/lang-css": "^6.0.0", - "@codemirror/lang-javascript": "^6.0.0", - "@codemirror/language": "^6.4.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.17.0", - "@lezer/common": "^1.0.0", - "@lezer/css": "^1.1.0", - "@lezer/html": "^1.3.0" - }, - "dependencies": { - "@codemirror/language": { - "version": "6.10.1", - "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.1.tgz", - "integrity": "sha512-5GrXzrhq6k+gL5fjkAwt90nYDmjlzTIJV8THnxNFtNKWotMIlzzN+CpqxqwXOECnUdOndmSeWntVrVcv5axWRQ==", - "requires": { - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.23.0", - "@lezer/common": "^1.1.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0", - "style-mod": "^4.0.0" - } - } - } - }, - "@codemirror/lang-javascript": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.1.tgz", - "integrity": "sha512-jlFOXTejVyiQCW3EQwvKH0m99bUYIw40oPmFjSX2VS78yzfe0HELZ+NEo9Yfo1MkGRpGlj3Gnu4rdxV1EnAs5A==", - "requires": { - "@codemirror/autocomplete": "^6.0.0", - "@codemirror/language": "^6.6.0", - "@codemirror/lint": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.17.0", - "@lezer/common": "^1.0.0", - "@lezer/javascript": "^1.0.0" - }, - "dependencies": { - "@codemirror/language": { - "version": "6.10.1", - "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.1.tgz", - "integrity": "sha512-5GrXzrhq6k+gL5fjkAwt90nYDmjlzTIJV8THnxNFtNKWotMIlzzN+CpqxqwXOECnUdOndmSeWntVrVcv5axWRQ==", - "requires": { - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.23.0", - "@lezer/common": "^1.1.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0", - "style-mod": "^4.0.0" - } - } - } - }, - "@codemirror/lang-markdown": { - "version": "6.2.4", - "resolved": "https://registry.npmjs.org/@codemirror/lang-markdown/-/lang-markdown-6.2.4.tgz", - "integrity": "sha512-UghkA1vSMs8bT7RSZM6vsIocigyah2bV00eRQuZy76401UmFZdsTsbQNBGdyxRQDOLeEvF5iFwap0BM8LKyd+g==", - "requires": { - "@codemirror/autocomplete": "^6.7.1", - "@codemirror/lang-html": "^6.0.0", - "@codemirror/language": "^6.3.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", - "@lezer/common": "^1.2.1", - "@lezer/markdown": "^1.0.0" - }, - "dependencies": { - "@codemirror/autocomplete": { - "version": "6.16.0", - "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.16.0.tgz", - "integrity": "sha512-P/LeCTtZHRTCU4xQsa89vSKWecYv1ZqwzOd5topheGRf+qtacFgBeIMQi3eL8Kt/BUNvxUWkx+5qP2jlGoARrg==", - "requires": { - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.17.0", - "@lezer/common": "^1.0.0" - } - }, - "@codemirror/language": { - "version": "6.10.1", - "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.1.tgz", - "integrity": "sha512-5GrXzrhq6k+gL5fjkAwt90nYDmjlzTIJV8THnxNFtNKWotMIlzzN+CpqxqwXOECnUdOndmSeWntVrVcv5axWRQ==", - "requires": { - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.23.0", - "@lezer/common": "^1.1.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0", - "style-mod": "^4.0.0" - } - } - } - }, - "@codemirror/language": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.2.1.tgz", - "integrity": "sha512-MC3svxuvIj0MRpFlGHxLS6vPyIdbTr2KKPEW46kCoCXw2ktb4NTkpkPBI/lSP/FoNXLCBJ0mrnUi1OoZxtpW1Q==", - "requires": { - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", - "@lezer/common": "^1.0.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0", - "style-mod": "^4.0.0" - } - }, - "@codemirror/lint": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.2.1.tgz", - "integrity": "sha512-y1muai5U/uUPAGRyHMx9mHuHLypPcHWxzlZGknp/U5Mdb5Ol8Q5ZLp67UqyTbNFJJ3unVxZ8iX3g1fMN79S1JQ==", - "requires": { - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", - "crelt": "^1.0.5" - } - }, - "@codemirror/state": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.4.1.tgz", - "integrity": "sha512-QkEyUiLhsJoZkbumGZlswmAhA7CBU02Wrz7zvH4SrcifbsqwlXShVXg65f3v/ts57W3dqyamEriMhij1Z3Zz4A==" - }, - "@codemirror/theme-one-dark": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.2.tgz", - "integrity": "sha512-F+sH0X16j/qFLMAfbciKTxVOwkdAS336b7AXTKOZhy8BR3eH/RelsnLgLFINrpST63mmN2OuwUt0W2ndUgYwUA==", - "requires": { - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", - "@lezer/highlight": "^1.0.0" - } - }, - "@codemirror/view": { - "version": "6.26.1", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.26.1.tgz", - "integrity": "sha512-wLw0t3R9AwOSQThdZ5Onw8QQtem5asE7+bPlnzc57eubPqiuJKIzwjMZ+C42vQett+iva+J8VgFV4RYWDBh5FA==", - "requires": { - "@codemirror/state": "^6.4.0", - "style-mod": "^4.1.0", - "w3c-keyname": "^2.2.4" - } - }, - "@colors/colors": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", - "dev": true, - "optional": true - }, - "@cspotcode/source-map-support": { - "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, - "requires": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "dependencies": { - "@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "devOptional": true, - "requires": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - } - } - }, - "@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==", - "dev": true, - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "http-signature": "~1.3.6", - "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", - "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": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz", - "integrity": "sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==", - "dev": true, - "requires": { - "debug": "^3.1.0", - "lodash.once": "^4.1.1" - }, - "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" - } - } - } - }, - "@emotion/is-prop-valid": { - "version": "0.8.8", - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz", - "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==", - "optional": true, - "requires": { - "@emotion/memoize": "0.7.4" - } - }, - "@emotion/memoize": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", - "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==", - "optional": true - }, - "@esbuild/aix-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", - "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", - "optional": true - }, - "@esbuild/android-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", - "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", - "optional": true - }, - "@esbuild/android-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", - "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", - "optional": true - }, - "@esbuild/android-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", - "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", - "optional": true - }, - "@esbuild/darwin-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", - "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", - "optional": true - }, - "@esbuild/darwin-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", - "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", - "optional": true - }, - "@esbuild/freebsd-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", - "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", - "optional": true - }, - "@esbuild/freebsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", - "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", - "optional": true - }, - "@esbuild/linux-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", - "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", - "optional": true - }, - "@esbuild/linux-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", - "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", - "optional": true - }, - "@esbuild/linux-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", - "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", - "optional": true - }, - "@esbuild/linux-loong64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", - "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", - "optional": true - }, - "@esbuild/linux-mips64el": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", - "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", - "optional": true - }, - "@esbuild/linux-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", - "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", - "optional": true - }, - "@esbuild/linux-riscv64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", - "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", - "optional": true - }, - "@esbuild/linux-s390x": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", - "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", - "optional": true - }, - "@esbuild/linux-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", - "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", - "optional": true - }, - "@esbuild/netbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", - "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", - "optional": true - }, - "@esbuild/openbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", - "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", - "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 - } - } - }, - "@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 - }, - "@floating-ui/core": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.0.tgz", - "integrity": "sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==", - "requires": { - "@floating-ui/utils": "^0.2.1" - } - }, - "@floating-ui/dom": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.3.tgz", - "integrity": "sha512-RnDthu3mzPlQ31Ss/BTwQ1zjzIhr3lk1gZB1OC56h/1vEtaXkESrOqL5fQVMfXpwGtRwX+YsZBdyHtJMQnkArw==", - "requires": { - "@floating-ui/core": "^1.0.0", - "@floating-ui/utils": "^0.2.0" - } - }, - "@floating-ui/react-dom": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.8.tgz", - "integrity": "sha512-HOdqOt3R3OGeTKidaLvJKcgg75S6tibQ3Tif4eyd91QnIJWr0NLvoXFpJA/j8HqkFSL68GDca9AuyWEHlhyClw==", - "requires": { - "@floating-ui/dom": "^1.6.1" - } - }, - "@floating-ui/utils": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz", - "integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==" - }, - "@graphiql/plugin-explorer": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@graphiql/plugin-explorer/-/plugin-explorer-1.0.4.tgz", - "integrity": "sha512-Z0UDhHSX1u4PfiqtlOMrXVrSE11ifC0zycGwhzK+BeglS9z56hknEky7NwJvUb9qC7sTlTmXEgfGLsYb5DjKrg==", - "requires": { - "graphiql-explorer": "^0.9.0" - }, - "dependencies": { - "graphiql-explorer": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/graphiql-explorer/-/graphiql-explorer-0.9.0.tgz", - "integrity": "sha512-fZC/wsuatqiQDO2otchxriFO0LaWIo/ovF/CQJ1yOudmY0P7pzDiP+l9CEHUiWbizk3e99x6DQG4XG1VxA+d6A==", - "requires": {} - } - } - }, - "@graphiql/react": { - "version": "0.20.4", - "resolved": "https://registry.npmjs.org/@graphiql/react/-/react-0.20.4.tgz", - "integrity": "sha512-LDgIlHa65pSngk8G2O0hvohNz4B41VUa7Yg6iPwifa1XreXxHIXjhV6FC1qi5oSjdCIRp4T8dkZnHA6iI5eElg==", - "requires": { - "@graphiql/toolkit": "^0.9.1", - "@headlessui/react": "^1.7.15", - "@radix-ui/react-dialog": "^1.0.4", - "@radix-ui/react-dropdown-menu": "^2.0.5", - "@radix-ui/react-tooltip": "^1.0.6", - "@radix-ui/react-visually-hidden": "^1.0.3", - "@types/codemirror": "^5.60.8", - "clsx": "^1.2.1", - "codemirror": "^5.65.3", - "codemirror-graphql": "^2.0.11", - "copy-to-clipboard": "^3.2.0", - "framer-motion": "^6.5.1", - "graphql-language-service": "^5.2.0", - "markdown-it": "^12.2.0", - "set-value": "^4.1.0" - }, - "dependencies": { - "@codemirror/language": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.0.0.tgz", - "integrity": "sha512-rtjk5ifyMzOna1c7PBu7J1VCt0PvA5wy3o8eMVnxMKb7z8KA7JFecvD04dSn14vj/bBaAbqRsGed5OjtofEnLA==", - "peer": true, - "requires": { - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", - "@lezer/common": "^1.0.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0", - "style-mod": "^4.0.0" - } - }, - "clsx": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", - "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==" - }, - "codemirror-graphql": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/codemirror-graphql/-/codemirror-graphql-2.0.11.tgz", - "integrity": "sha512-j1QDDXKVkpin2VsyS0ke2nAhKal6/N1UJtgnBGrPe3gj9ZSP6/K8Xytft94k0xW6giIU/JhZjvW0GwwERNzbFA==", - "requires": { - "@types/codemirror": "^0.0.90", - "graphql-language-service": "5.2.0" - }, - "dependencies": { - "@types/codemirror": { - "version": "0.0.90", - "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-0.0.90.tgz", - "integrity": "sha512-8Z9+tSg27NPRGubbUPUCrt5DDG/OWzLph5BvcDykwR5D7RyZh5mhHG0uS1ePKV1YFCA+/cwc4Ey2AJAEFfV3IA==", - "requires": { - "@types/tern": "*" - } - } - } - } - } - }, - "@graphiql/toolkit": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/@graphiql/toolkit/-/toolkit-0.9.1.tgz", - "integrity": "sha512-LVt9pdk0830so50ZnU2Znb2rclcoWznG8r8asqAENzV0U1FM1kuY0sdPpc/rBc9MmmNgnB6A+WZzDhq6dbhTHA==", - "requires": { - "@n1ru4l/push-pull-async-iterable-iterator": "^3.1.0", - "meros": "^1.1.4" - } - }, - "@graphql-codegen/add": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@graphql-codegen/add/-/add-5.0.2.tgz", - "integrity": "sha512-ouBkSvMFUhda5VoKumo/ZvsZM9P5ZTyDsI8LW18VxSNWOjrTeLXBWHG8Gfaai0HwhflPtCYVABbriEcOmrRShQ==", - "dev": true, - "requires": { - "@graphql-codegen/plugin-helpers": "^5.0.3", - "tslib": "~2.6.0" - } - }, - "@graphql-codegen/cli": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@graphql-codegen/cli/-/cli-5.0.2.tgz", - "integrity": "sha512-MBIaFqDiLKuO4ojN6xxG9/xL9wmfD3ZjZ7RsPjwQnSHBCUXnEkdKvX+JVpx87Pq29Ycn8wTJUguXnTZ7Di0Mlw==", - "dev": true, - "requires": { - "@babel/generator": "^7.18.13", - "@babel/template": "^7.18.10", - "@babel/types": "^7.18.13", - "@graphql-codegen/client-preset": "^4.2.2", - "@graphql-codegen/core": "^4.0.2", - "@graphql-codegen/plugin-helpers": "^5.0.3", - "@graphql-tools/apollo-engine-loader": "^8.0.0", - "@graphql-tools/code-file-loader": "^8.0.0", - "@graphql-tools/git-loader": "^8.0.0", - "@graphql-tools/github-loader": "^8.0.0", - "@graphql-tools/graphql-file-loader": "^8.0.0", - "@graphql-tools/json-file-loader": "^8.0.0", - "@graphql-tools/load": "^8.0.0", - "@graphql-tools/prisma-loader": "^8.0.0", - "@graphql-tools/url-loader": "^8.0.0", - "@graphql-tools/utils": "^10.0.0", - "@whatwg-node/fetch": "^0.8.0", - "chalk": "^4.1.0", - "cosmiconfig": "^8.1.3", - "debounce": "^1.2.0", - "detect-indent": "^6.0.0", - "graphql-config": "^5.0.2", - "inquirer": "^8.0.0", - "is-glob": "^4.0.1", - "jiti": "^1.17.1", - "json-to-pretty-yaml": "^1.2.2", - "listr2": "^4.0.5", - "log-symbols": "^4.0.0", - "micromatch": "^4.0.5", - "shell-quote": "^1.7.3", - "string-env-interpolation": "^1.0.1", - "ts-log": "^2.2.3", - "tslib": "^2.4.0", - "yaml": "^2.3.1", - "yargs": "^17.0.0" - } - }, - "@graphql-codegen/client-preset": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/@graphql-codegen/client-preset/-/client-preset-4.2.5.tgz", - "integrity": "sha512-hAdB6HN8EDmkoBtr0bPUN/7NH6svzqbcTDMWBCRXPESXkl7y80po+IXrXUjsSrvhKG8xkNXgJNz/2mjwHzywcA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/template": "^7.20.7", - "@graphql-codegen/add": "^5.0.2", - "@graphql-codegen/gql-tag-operations": "4.0.6", - "@graphql-codegen/plugin-helpers": "^5.0.3", - "@graphql-codegen/typed-document-node": "^5.0.6", - "@graphql-codegen/typescript": "^4.0.6", - "@graphql-codegen/typescript-operations": "^4.2.0", - "@graphql-codegen/visitor-plugin-common": "^5.1.0", - "@graphql-tools/documents": "^1.0.0", - "@graphql-tools/utils": "^10.0.0", - "@graphql-typed-document-node/core": "3.2.0", - "tslib": "~2.6.0" - } - }, - "@graphql-codegen/core": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@graphql-codegen/core/-/core-4.0.2.tgz", - "integrity": "sha512-IZbpkhwVqgizcjNiaVzNAzm/xbWT6YnGgeOLwVjm4KbJn3V2jchVtuzHH09G5/WkkLSk2wgbXNdwjM41JxO6Eg==", - "dev": true, - "requires": { - "@graphql-codegen/plugin-helpers": "^5.0.3", - "@graphql-tools/schema": "^10.0.0", - "@graphql-tools/utils": "^10.0.0", - "tslib": "~2.6.0" - } - }, - "@graphql-codegen/gql-tag-operations": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@graphql-codegen/gql-tag-operations/-/gql-tag-operations-4.0.6.tgz", - "integrity": "sha512-y6iXEDpDNjwNxJw3WZqX1/Znj0QHW7+y8O+t2V8qvbTT+3kb2lr9ntc8By7vCr6ctw9tXI4XKaJgpTstJDOwFA==", - "dev": true, - "requires": { - "@graphql-codegen/plugin-helpers": "^5.0.3", - "@graphql-codegen/visitor-plugin-common": "5.1.0", - "@graphql-tools/utils": "^10.0.0", - "auto-bind": "~4.0.0", - "tslib": "~2.6.0" - } - }, - "@graphql-codegen/plugin-helpers": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@graphql-codegen/plugin-helpers/-/plugin-helpers-5.0.4.tgz", - "integrity": "sha512-MOIuHFNWUnFnqVmiXtrI+4UziMTYrcquljaI5f/T/Bc7oO7sXcfkAvgkNWEEi9xWreYwvuer3VHCuPI/lAFWbw==", - "dev": true, - "requires": { - "@graphql-tools/utils": "^10.0.0", - "change-case-all": "1.0.15", - "common-tags": "1.8.2", - "import-from": "4.0.0", - "lodash": "~4.17.0", - "tslib": "~2.6.0" - } - }, - "@graphql-codegen/schema-ast": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@graphql-codegen/schema-ast/-/schema-ast-4.0.2.tgz", - "integrity": "sha512-5mVAOQQK3Oz7EtMl/l3vOQdc2aYClUzVDHHkMvZlunc+KlGgl81j8TLa+X7ANIllqU4fUEsQU3lJmk4hXP6K7Q==", - "dev": true, - "requires": { - "@graphql-codegen/plugin-helpers": "^5.0.3", - "@graphql-tools/utils": "^10.0.0", - "tslib": "~2.6.0" - } - }, - "@graphql-codegen/typed-document-node": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/@graphql-codegen/typed-document-node/-/typed-document-node-5.0.6.tgz", - "integrity": "sha512-US0J95hOE2/W/h42w4oiY+DFKG7IetEN1mQMgXXeat1w6FAR5PlIz4JrRrEkiVfVetZ1g7K78SOwBD8/IJnDiA==", - "dev": true, - "requires": { - "@graphql-codegen/plugin-helpers": "^5.0.3", - "@graphql-codegen/visitor-plugin-common": "5.1.0", - "auto-bind": "~4.0.0", - "change-case-all": "1.0.15", - "tslib": "~2.6.0" - } - }, - "@graphql-codegen/typescript": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript/-/typescript-4.0.9.tgz", - "integrity": "sha512-0O35DMR4d/ctuHL1Zo6mRUUzp0BoszKfeWsa6sCm/g70+S98+hEfTwZNDkQHylLxapiyjssF9uw/F+sXqejqLw==", - "dev": true, - "requires": { - "@graphql-codegen/plugin-helpers": "^5.0.4", - "@graphql-codegen/schema-ast": "^4.0.2", - "@graphql-codegen/visitor-plugin-common": "5.3.1", - "auto-bind": "~4.0.0", - "tslib": "~2.6.0" - }, - "dependencies": { - "@graphql-codegen/visitor-plugin-common": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-5.3.1.tgz", - "integrity": "sha512-MktoBdNZhSmugiDjmFl1z6rEUUaqyxtFJYWnDilE7onkPgyw//O0M+TuPBJPBWdyV6J2ond0Hdqtq+rkghgSIQ==", - "dev": true, - "requires": { - "@graphql-codegen/plugin-helpers": "^5.0.4", - "@graphql-tools/optimize": "^2.0.0", - "@graphql-tools/relay-operation-optimizer": "^7.0.0", - "@graphql-tools/utils": "^10.0.0", - "auto-bind": "~4.0.0", - "change-case-all": "1.0.15", - "dependency-graph": "^0.11.0", - "graphql-tag": "^2.11.0", - "parse-filepath": "^1.0.2", - "tslib": "~2.6.0" - } - } - } - }, - "@graphql-codegen/typescript-operations": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript-operations/-/typescript-operations-4.2.0.tgz", - "integrity": "sha512-lmuwYb03XC7LNRS8oo9M4/vlOrq/wOKmTLBHlltK2YJ1BO/4K/Q9Jdv/jDmJpNydHVR1fmeF4wAfsIp1f9JibA==", - "dev": true, - "requires": { - "@graphql-codegen/plugin-helpers": "^5.0.3", - "@graphql-codegen/typescript": "^4.0.6", - "@graphql-codegen/visitor-plugin-common": "5.1.0", - "auto-bind": "~4.0.0", - "tslib": "~2.6.0" - } - }, - "@graphql-codegen/visitor-plugin-common": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-5.1.0.tgz", - "integrity": "sha512-eamQxtA9bjJqI2lU5eYoA1GbdMIRT2X8m8vhWYsVQVWD3qM7sx/IqJU0kx0J3Vd4/CSd36BzL6RKwksibytDIg==", - "dev": true, - "requires": { - "@graphql-codegen/plugin-helpers": "^5.0.3", - "@graphql-tools/optimize": "^2.0.0", - "@graphql-tools/relay-operation-optimizer": "^7.0.0", - "@graphql-tools/utils": "^10.0.0", - "auto-bind": "~4.0.0", - "change-case-all": "1.0.15", - "dependency-graph": "^0.11.0", - "graphql-tag": "^2.11.0", - "parse-filepath": "^1.0.2", - "tslib": "~2.6.0" - } - }, - "@graphql-tools/apollo-engine-loader": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@graphql-tools/apollo-engine-loader/-/apollo-engine-loader-8.0.1.tgz", - "integrity": "sha512-NaPeVjtrfbPXcl+MLQCJLWtqe2/E4bbAqcauEOQ+3sizw1Fc2CNmhHRF8a6W4D0ekvTRRXAMptXYgA2uConbrA==", - "dev": true, - "requires": { - "@ardatan/sync-fetch": "^0.0.1", - "@graphql-tools/utils": "^10.0.13", - "@whatwg-node/fetch": "^0.9.0", - "tslib": "^2.4.0" - }, - "dependencies": { - "@whatwg-node/events": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@whatwg-node/events/-/events-0.1.1.tgz", - "integrity": "sha512-AyQEn5hIPV7Ze+xFoXVU3QTHXVbWPrzaOkxtENMPMuNL6VVHrp4hHfDt9nrQpjO7BgvuM95dMtkycX5M/DZR3w==", - "dev": true - }, - "@whatwg-node/fetch": { - "version": "0.9.17", - "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.9.17.tgz", - "integrity": "sha512-TDYP3CpCrxwxpiNY0UMNf096H5Ihf67BK1iKGegQl5u9SlpEDYrvnV71gWBGJm+Xm31qOy8ATgma9rm8Pe7/5Q==", - "dev": true, - "requires": { - "@whatwg-node/node-fetch": "^0.5.7", - "urlpattern-polyfill": "^10.0.0" - } - }, - "@whatwg-node/node-fetch": { - "version": "0.5.10", - "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.5.10.tgz", - "integrity": "sha512-KIAHepie/T1PRkUfze4t+bPlyvpxlWiXTPtcGlbIZ0vWkBJMdRmCg4ZrJ2y4XaO1eTPo1HlWYUuj1WvoIpumqg==", - "dev": true, - "requires": { - "@kamilkisiela/fast-url-parser": "^1.1.4", - "@whatwg-node/events": "^0.1.0", - "busboy": "^1.6.0", - "fast-querystring": "^1.1.1", - "tslib": "^2.3.1" - } - }, - "urlpattern-polyfill": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz", - "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==", - "dev": true - } - } - }, - "@graphql-tools/batch-execute": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/@graphql-tools/batch-execute/-/batch-execute-9.0.4.tgz", - "integrity": "sha512-kkebDLXgDrep5Y0gK1RN3DMUlLqNhg60OAz0lTCqrYeja6DshxLtLkj+zV4mVbBA4mQOEoBmw6g1LZs3dA84/w==", - "dev": true, - "requires": { - "@graphql-tools/utils": "^10.0.13", - "dataloader": "^2.2.2", - "tslib": "^2.4.0", - "value-or-promise": "^1.0.12" - } - }, - "@graphql-tools/code-file-loader": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/@graphql-tools/code-file-loader/-/code-file-loader-8.1.1.tgz", - "integrity": "sha512-q4KN25EPSUztc8rA8YUU3ufh721Yk12xXDbtUA+YstczWS7a1RJlghYMFEfR1HsHSYbF7cUqkbnTKSGM3o52bQ==", - "dev": true, - "requires": { - "@graphql-tools/graphql-tag-pluck": "8.3.0", - "@graphql-tools/utils": "^10.0.13", - "globby": "^11.0.3", - "tslib": "^2.4.0", - "unixify": "^1.0.0" - } - }, - "@graphql-tools/delegate": { - "version": "10.0.4", - "resolved": "https://registry.npmjs.org/@graphql-tools/delegate/-/delegate-10.0.4.tgz", - "integrity": "sha512-WswZRbQZMh/ebhc8zSomK9DIh6Pd5KbuiMsyiKkKz37TWTrlCOe+4C/fyrBFez30ksq6oFyCeSKMwfrCbeGo0Q==", - "dev": true, - "requires": { - "@graphql-tools/batch-execute": "^9.0.4", - "@graphql-tools/executor": "^1.2.1", - "@graphql-tools/schema": "^10.0.3", - "@graphql-tools/utils": "^10.0.13", - "dataloader": "^2.2.2", - "tslib": "^2.5.0" - } - }, - "@graphql-tools/documents": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@graphql-tools/documents/-/documents-1.0.0.tgz", - "integrity": "sha512-rHGjX1vg/nZ2DKqRGfDPNC55CWZBMldEVcH+91BThRa6JeT80NqXknffLLEZLRUxyikCfkwMsk6xR3UNMqG0Rg==", - "dev": true, - "requires": { - "lodash.sortby": "^4.7.0", - "tslib": "^2.4.0" - } - }, - "@graphql-tools/executor": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/@graphql-tools/executor/-/executor-1.2.5.tgz", - "integrity": "sha512-s7sW4K3BUNsk9sjq+vNicwb9KwcR3G55uS/CI8KZQ4x0ZdeYMIwpeU9MVeORCCpHuQyTaV+/VnO0hFrS/ygzsg==", - "dev": true, - "requires": { - "@graphql-tools/utils": "^10.1.1", - "@graphql-typed-document-node/core": "3.2.0", - "@repeaterjs/repeater": "^3.0.4", - "tslib": "^2.4.0", - "value-or-promise": "^1.0.12" - } - }, - "@graphql-tools/executor-graphql-ws": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@graphql-tools/executor-graphql-ws/-/executor-graphql-ws-1.1.2.tgz", - "integrity": "sha512-+9ZK0rychTH1LUv4iZqJ4ESbmULJMTsv3XlFooPUngpxZkk00q6LqHKJRrsLErmQrVaC7cwQCaRBJa0teK17Lg==", - "dev": true, - "requires": { - "@graphql-tools/utils": "^10.0.13", - "@types/ws": "^8.0.0", - "graphql-ws": "^5.14.0", - "isomorphic-ws": "^5.0.0", - "tslib": "^2.4.0", - "ws": "^8.13.0" - } - }, - "@graphql-tools/executor-http": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@graphql-tools/executor-http/-/executor-http-1.0.9.tgz", - "integrity": "sha512-+NXaZd2MWbbrWHqU4EhXcrDbogeiCDmEbrAN+rMn4Nu2okDjn2MTFDbTIab87oEubQCH4Te1wDkWPKrzXup7+Q==", - "dev": true, - "requires": { - "@graphql-tools/utils": "^10.0.13", - "@repeaterjs/repeater": "^3.0.4", - "@whatwg-node/fetch": "^0.9.0", - "extract-files": "^11.0.0", - "meros": "^1.2.1", - "tslib": "^2.4.0", - "value-or-promise": "^1.0.12" - }, - "dependencies": { - "@whatwg-node/events": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@whatwg-node/events/-/events-0.1.1.tgz", - "integrity": "sha512-AyQEn5hIPV7Ze+xFoXVU3QTHXVbWPrzaOkxtENMPMuNL6VVHrp4hHfDt9nrQpjO7BgvuM95dMtkycX5M/DZR3w==", - "dev": true - }, - "@whatwg-node/fetch": { - "version": "0.9.17", - "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.9.17.tgz", - "integrity": "sha512-TDYP3CpCrxwxpiNY0UMNf096H5Ihf67BK1iKGegQl5u9SlpEDYrvnV71gWBGJm+Xm31qOy8ATgma9rm8Pe7/5Q==", - "dev": true, - "requires": { - "@whatwg-node/node-fetch": "^0.5.7", - "urlpattern-polyfill": "^10.0.0" - } - }, - "@whatwg-node/node-fetch": { - "version": "0.5.10", - "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.5.10.tgz", - "integrity": "sha512-KIAHepie/T1PRkUfze4t+bPlyvpxlWiXTPtcGlbIZ0vWkBJMdRmCg4ZrJ2y4XaO1eTPo1HlWYUuj1WvoIpumqg==", - "dev": true, - "requires": { - "@kamilkisiela/fast-url-parser": "^1.1.4", - "@whatwg-node/events": "^0.1.0", - "busboy": "^1.6.0", - "fast-querystring": "^1.1.1", - "tslib": "^2.3.1" - } - }, - "urlpattern-polyfill": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz", - "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==", - "dev": true - } - } - }, - "@graphql-tools/executor-legacy-ws": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@graphql-tools/executor-legacy-ws/-/executor-legacy-ws-1.0.6.tgz", - "integrity": "sha512-lDSxz9VyyquOrvSuCCnld3256Hmd+QI2lkmkEv7d4mdzkxkK4ddAWW1geQiWrQvWmdsmcnGGlZ7gDGbhEExwqg==", - "dev": true, - "requires": { - "@graphql-tools/utils": "^10.0.13", - "@types/ws": "^8.0.0", - "isomorphic-ws": "^5.0.0", - "tslib": "^2.4.0", - "ws": "^8.15.0" - } - }, - "@graphql-tools/git-loader": { - "version": "8.0.5", - "resolved": "https://registry.npmjs.org/@graphql-tools/git-loader/-/git-loader-8.0.5.tgz", - "integrity": "sha512-P97/1mhruDiA6D5WUmx3n/aeGPLWj2+4dpzDOxFGGU+z9NcI/JdygMkeFpGZNHeJfw+kHfxgPcMPnxHcyhAoVA==", - "dev": true, - "requires": { - "@graphql-tools/graphql-tag-pluck": "8.3.0", - "@graphql-tools/utils": "^10.0.13", - "is-glob": "4.0.3", - "micromatch": "^4.0.4", - "tslib": "^2.4.0", - "unixify": "^1.0.0" - } - }, - "@graphql-tools/github-loader": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@graphql-tools/github-loader/-/github-loader-8.0.1.tgz", - "integrity": "sha512-W4dFLQJ5GtKGltvh/u1apWRFKBQOsDzFxO9cJkOYZj1VzHCpRF43uLST4VbCfWve+AwBqOuKr7YgkHoxpRMkcg==", - "dev": true, - "requires": { - "@ardatan/sync-fetch": "^0.0.1", - "@graphql-tools/executor-http": "^1.0.9", - "@graphql-tools/graphql-tag-pluck": "^8.0.0", - "@graphql-tools/utils": "^10.0.13", - "@whatwg-node/fetch": "^0.9.0", - "tslib": "^2.4.0", - "value-or-promise": "^1.0.12" - }, - "dependencies": { - "@whatwg-node/events": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@whatwg-node/events/-/events-0.1.1.tgz", - "integrity": "sha512-AyQEn5hIPV7Ze+xFoXVU3QTHXVbWPrzaOkxtENMPMuNL6VVHrp4hHfDt9nrQpjO7BgvuM95dMtkycX5M/DZR3w==", - "dev": true - }, - "@whatwg-node/fetch": { - "version": "0.9.17", - "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.9.17.tgz", - "integrity": "sha512-TDYP3CpCrxwxpiNY0UMNf096H5Ihf67BK1iKGegQl5u9SlpEDYrvnV71gWBGJm+Xm31qOy8ATgma9rm8Pe7/5Q==", - "dev": true, - "requires": { - "@whatwg-node/node-fetch": "^0.5.7", - "urlpattern-polyfill": "^10.0.0" - } - }, - "@whatwg-node/node-fetch": { - "version": "0.5.10", - "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.5.10.tgz", - "integrity": "sha512-KIAHepie/T1PRkUfze4t+bPlyvpxlWiXTPtcGlbIZ0vWkBJMdRmCg4ZrJ2y4XaO1eTPo1HlWYUuj1WvoIpumqg==", - "dev": true, - "requires": { - "@kamilkisiela/fast-url-parser": "^1.1.4", - "@whatwg-node/events": "^0.1.0", - "busboy": "^1.6.0", - "fast-querystring": "^1.1.1", - "tslib": "^2.3.1" - } - }, - "urlpattern-polyfill": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz", - "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==", - "dev": true - } - } - }, - "@graphql-tools/graphql-file-loader": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@graphql-tools/graphql-file-loader/-/graphql-file-loader-8.0.1.tgz", - "integrity": "sha512-7gswMqWBabTSmqbaNyWSmRRpStWlcCkBc73E6NZNlh4YNuiyKOwbvSkOUYFOqFMfEL+cFsXgAvr87Vz4XrYSbA==", - "dev": true, - "requires": { - "@graphql-tools/import": "7.0.1", - "@graphql-tools/utils": "^10.0.13", - "globby": "^11.0.3", - "tslib": "^2.4.0", - "unixify": "^1.0.0" - } - }, - "@graphql-tools/graphql-tag-pluck": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/@graphql-tools/graphql-tag-pluck/-/graphql-tag-pluck-8.3.0.tgz", - "integrity": "sha512-gNqukC+s7iHC7vQZmx1SEJQmLnOguBq+aqE2zV2+o1hxkExvKqyFli1SY/9gmukFIKpKutCIj+8yLOM+jARutw==", - "dev": true, - "requires": { - "@babel/core": "^7.22.9", - "@babel/parser": "^7.16.8", - "@babel/plugin-syntax-import-assertions": "^7.20.0", - "@babel/traverse": "^7.16.8", - "@babel/types": "^7.16.8", - "@graphql-tools/utils": "^10.0.13", - "tslib": "^2.4.0" - } - }, - "@graphql-tools/import": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@graphql-tools/import/-/import-7.0.1.tgz", - "integrity": "sha512-935uAjAS8UAeXThqHfYVr4HEAp6nHJ2sximZKO1RzUTq5WoALMAhhGARl0+ecm6X+cqNUwIChJbjtaa6P/ML0w==", - "dev": true, - "requires": { - "@graphql-tools/utils": "^10.0.13", - "resolve-from": "5.0.0", - "tslib": "^2.4.0" - } - }, - "@graphql-tools/json-file-loader": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@graphql-tools/json-file-loader/-/json-file-loader-8.0.1.tgz", - "integrity": "sha512-lAy2VqxDAHjVyqeJonCP6TUemrpYdDuKt25a10X6zY2Yn3iFYGnuIDQ64cv3ytyGY6KPyPB+Kp+ZfOkNDG3FQA==", - "dev": true, - "requires": { - "@graphql-tools/utils": "^10.0.13", - "globby": "^11.0.3", - "tslib": "^2.4.0", - "unixify": "^1.0.0" - } - }, - "@graphql-tools/load": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@graphql-tools/load/-/load-8.0.2.tgz", - "integrity": "sha512-S+E/cmyVmJ3CuCNfDuNF2EyovTwdWfQScXv/2gmvJOti2rGD8jTt9GYVzXaxhblLivQR9sBUCNZu/w7j7aXUCA==", - "dev": true, - "requires": { - "@graphql-tools/schema": "^10.0.3", - "@graphql-tools/utils": "^10.0.13", - "p-limit": "3.1.0", - "tslib": "^2.4.0" - } - }, - "@graphql-tools/merge": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-9.0.3.tgz", - "integrity": "sha512-FeKv9lKLMwqDu0pQjPpF59GY3HReUkWXKsMIuMuJQOKh9BETu7zPEFUELvcw8w+lwZkl4ileJsHXC9+AnsT2Lw==", - "dev": true, - "requires": { - "@graphql-tools/utils": "^10.0.13", - "tslib": "^2.4.0" - } - }, - "@graphql-tools/optimize": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@graphql-tools/optimize/-/optimize-2.0.0.tgz", - "integrity": "sha512-nhdT+CRGDZ+bk68ic+Jw1OZ99YCDIKYA5AlVAnBHJvMawSx9YQqQAIj4refNc1/LRieGiuWvhbG3jvPVYho0Dg==", - "dev": true, - "requires": { - "tslib": "^2.4.0" - } - }, - "@graphql-tools/prisma-loader": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/@graphql-tools/prisma-loader/-/prisma-loader-8.0.3.tgz", - "integrity": "sha512-oZhxnMr3Jw2WAW1h9FIhF27xWzIB7bXWM8olz4W12oII4NiZl7VRkFw9IT50zME2Bqi9LGh9pkmMWkjvbOpl+Q==", - "dev": true, - "requires": { - "@graphql-tools/url-loader": "^8.0.2", - "@graphql-tools/utils": "^10.0.13", - "@types/js-yaml": "^4.0.0", - "@types/json-stable-stringify": "^1.0.32", - "@whatwg-node/fetch": "^0.9.0", - "chalk": "^4.1.0", - "debug": "^4.3.1", - "dotenv": "^16.0.0", - "graphql-request": "^6.0.0", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.0", - "jose": "^5.0.0", - "js-yaml": "^4.0.0", - "json-stable-stringify": "^1.0.1", - "lodash": "^4.17.20", - "scuid": "^1.1.0", - "tslib": "^2.4.0", - "yaml-ast-parser": "^0.0.43" - }, - "dependencies": { - "@whatwg-node/events": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@whatwg-node/events/-/events-0.1.1.tgz", - "integrity": "sha512-AyQEn5hIPV7Ze+xFoXVU3QTHXVbWPrzaOkxtENMPMuNL6VVHrp4hHfDt9nrQpjO7BgvuM95dMtkycX5M/DZR3w==", - "dev": true - }, - "@whatwg-node/fetch": { - "version": "0.9.17", - "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.9.17.tgz", - "integrity": "sha512-TDYP3CpCrxwxpiNY0UMNf096H5Ihf67BK1iKGegQl5u9SlpEDYrvnV71gWBGJm+Xm31qOy8ATgma9rm8Pe7/5Q==", - "dev": true, - "requires": { - "@whatwg-node/node-fetch": "^0.5.7", - "urlpattern-polyfill": "^10.0.0" - } - }, - "@whatwg-node/node-fetch": { - "version": "0.5.10", - "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.5.10.tgz", - "integrity": "sha512-KIAHepie/T1PRkUfze4t+bPlyvpxlWiXTPtcGlbIZ0vWkBJMdRmCg4ZrJ2y4XaO1eTPo1HlWYUuj1WvoIpumqg==", - "dev": true, - "requires": { - "@kamilkisiela/fast-url-parser": "^1.1.4", - "@whatwg-node/events": "^0.1.0", - "busboy": "^1.6.0", - "fast-querystring": "^1.1.1", - "tslib": "^2.3.1" - } - }, - "urlpattern-polyfill": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz", - "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==", - "dev": true - } - } - }, - "@graphql-tools/relay-operation-optimizer": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-7.0.1.tgz", - "integrity": "sha512-y0ZrQ/iyqWZlsS/xrJfSir3TbVYJTYmMOu4TaSz6F4FRDTQ3ie43BlKkhf04rC28pnUOS4BO9pDcAo1D30l5+A==", - "dev": true, - "requires": { - "@ardatan/relay-compiler": "12.0.0", - "@graphql-tools/utils": "^10.0.13", - "tslib": "^2.4.0" - } - }, - "@graphql-tools/schema": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-10.0.3.tgz", - "integrity": "sha512-p28Oh9EcOna6i0yLaCFOnkcBDQECVf3SCexT6ktb86QNj9idnkhI+tCxnwZDh58Qvjd2nURdkbevvoZkvxzCog==", - "dev": true, - "requires": { - "@graphql-tools/merge": "^9.0.3", - "@graphql-tools/utils": "^10.0.13", - "tslib": "^2.4.0", - "value-or-promise": "^1.0.12" - } - }, - "@graphql-tools/url-loader": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@graphql-tools/url-loader/-/url-loader-8.0.2.tgz", - "integrity": "sha512-1dKp2K8UuFn7DFo1qX5c1cyazQv2h2ICwA9esHblEqCYrgf69Nk8N7SODmsfWg94OEaI74IqMoM12t7eIGwFzQ==", - "dev": true, - "requires": { - "@ardatan/sync-fetch": "^0.0.1", - "@graphql-tools/delegate": "^10.0.4", - "@graphql-tools/executor-graphql-ws": "^1.1.2", - "@graphql-tools/executor-http": "^1.0.9", - "@graphql-tools/executor-legacy-ws": "^1.0.6", - "@graphql-tools/utils": "^10.0.13", - "@graphql-tools/wrap": "^10.0.2", - "@types/ws": "^8.0.0", - "@whatwg-node/fetch": "^0.9.0", - "isomorphic-ws": "^5.0.0", - "tslib": "^2.4.0", - "value-or-promise": "^1.0.11", - "ws": "^8.12.0" - }, - "dependencies": { - "@whatwg-node/events": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@whatwg-node/events/-/events-0.1.1.tgz", - "integrity": "sha512-AyQEn5hIPV7Ze+xFoXVU3QTHXVbWPrzaOkxtENMPMuNL6VVHrp4hHfDt9nrQpjO7BgvuM95dMtkycX5M/DZR3w==", - "dev": true - }, - "@whatwg-node/fetch": { - "version": "0.9.17", - "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.9.17.tgz", - "integrity": "sha512-TDYP3CpCrxwxpiNY0UMNf096H5Ihf67BK1iKGegQl5u9SlpEDYrvnV71gWBGJm+Xm31qOy8ATgma9rm8Pe7/5Q==", - "dev": true, - "requires": { - "@whatwg-node/node-fetch": "^0.5.7", - "urlpattern-polyfill": "^10.0.0" - } - }, - "@whatwg-node/node-fetch": { - "version": "0.5.10", - "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.5.10.tgz", - "integrity": "sha512-KIAHepie/T1PRkUfze4t+bPlyvpxlWiXTPtcGlbIZ0vWkBJMdRmCg4ZrJ2y4XaO1eTPo1HlWYUuj1WvoIpumqg==", - "dev": true, - "requires": { - "@kamilkisiela/fast-url-parser": "^1.1.4", - "@whatwg-node/events": "^0.1.0", - "busboy": "^1.6.0", - "fast-querystring": "^1.1.1", - "tslib": "^2.3.1" - } - }, - "urlpattern-polyfill": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz", - "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==", - "dev": true - } - } - }, - "@graphql-tools/utils": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-10.1.2.tgz", - "integrity": "sha512-fX13CYsDnX4yifIyNdiN0cVygz/muvkreWWem6BBw130+ODbRRgfiVveL0NizCEnKXkpvdeTy9Bxvo9LIKlhrw==", - "dev": true, - "requires": { - "@graphql-typed-document-node/core": "^3.1.1", - "cross-inspect": "1.0.0", - "dset": "^3.1.2", - "tslib": "^2.4.0" - } - }, - "@graphql-tools/wrap": { - "version": "10.0.5", - "resolved": "https://registry.npmjs.org/@graphql-tools/wrap/-/wrap-10.0.5.tgz", - "integrity": "sha512-Cbr5aYjr3HkwdPvetZp1cpDWTGdD1Owgsb3z/ClzhmrboiK86EnQDxDvOJiQkDCPWE9lNBwj8Y4HfxroY0D9DQ==", - "dev": true, - "requires": { - "@graphql-tools/delegate": "^10.0.4", - "@graphql-tools/schema": "^10.0.3", - "@graphql-tools/utils": "^10.1.1", - "tslib": "^2.4.0", - "value-or-promise": "^1.0.12" - } - }, - "@graphql-typed-document-node/core": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz", - "integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==", - "requires": {} - }, - "@headlessui/react": { - "version": "1.7.18", - "resolved": "https://registry.npmjs.org/@headlessui/react/-/react-1.7.18.tgz", - "integrity": "sha512-4i5DOrzwN4qSgNsL4Si61VMkUcWbcSKueUV7sFhpHzQcSShdlHENE5+QBntMSRvHt8NyoFO2AGG8si9lq+w4zQ==", - "requires": { - "@tanstack/react-virtual": "^3.0.0-beta.60", - "client-only": "^0.0.1" - } - }, - "@heroicons/react": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.1.3.tgz", - "integrity": "sha512-fEcPfo4oN345SoqdlCDdSa4ivjaKbk0jTd+oubcgNxnNgAfzysfwWfQUr+51wigiWHQQRiZNd1Ao0M5Y3M2EGg==", - "requires": {} - }, - "@hookform/error-message": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@hookform/error-message/-/error-message-2.0.1.tgz", - "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==", - "requires": { - "iconify-icon": "^2.0.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==", - "requires": { - "@iconify/types": "*" - } - }, - "@iconify/types": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", - "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==" - }, - "@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "requires": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==" - }, - "ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==" - }, - "emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" - }, - "string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "requires": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - } - }, - "strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "requires": { - "ansi-regex": "^6.0.1" - } - }, - "wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "requires": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - } - } - } - }, - "@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true - }, - "@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.27.8" - } - }, - "@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", - "requires": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==" - }, - "@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==" - }, - "@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==" - }, - "@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "requires": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "@kamilkisiela/fast-url-parser": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@kamilkisiela/fast-url-parser/-/fast-url-parser-1.1.4.tgz", - "integrity": "sha512-gbkePEBupNydxCelHCESvFSFM8XPh1Zs/OAVRW/rKpEqPAl5PbOM90Si8mv9bvnR53uPD2s/FiRxdvSejpRJew==", - "dev": true - }, - "@lezer/common": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.1.tgz", - "integrity": "sha512-yemX0ZD2xS/73llMZIK6KplkjIjf2EvAHcinDi/TfJ9hS25G0388+ClHt6/3but0oOxinTcQHJLDXh6w1crzFQ==" - }, - "@lezer/css": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.1.7.tgz", - "integrity": "sha512-7BlFFAKNn/b39jJLrhdLSX5A2k56GIJvyLqdmm7UU+7XvequY084iuKDMAEhAmAzHnwDE8FK4OQtsIUssW91tg==", - "requires": { - "@lezer/common": "^1.2.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0" - } - }, - "@lezer/highlight": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.0.tgz", - "integrity": "sha512-WrS5Mw51sGrpqjlh3d4/fOwpEV2Hd3YOkp9DBt4k8XZQcoTHZFB7sx030A6OcahF4J1nDQAa3jXlTVVYH50IFA==", - "requires": { - "@lezer/common": "^1.0.0" - } - }, - "@lezer/html": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.8.tgz", - "integrity": "sha512-EXseJ3pUzWxE6XQBQdqWHZqqlGQRSuNMBcLb6mZWS2J2v+QZhOObD+3ZIKIcm59ntTzyor4LqFTb72iJc3k23Q==", - "requires": { - "@lezer/common": "^1.2.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0" - } - }, - "@lezer/javascript": { - "version": "1.4.13", - "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.4.13.tgz", - "integrity": "sha512-5IBr8LIO3xJdJH1e9aj/ZNLE4LSbdsx25wFmGRAZsj2zSmwAYjx26JyU/BYOCpRQlu1jcv1z3vy4NB9+UkfRow==", - "requires": { - "@lezer/common": "^1.2.0", - "@lezer/highlight": "^1.1.3", - "@lezer/lr": "^1.3.0" - } - }, - "@lezer/lr": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.0.tgz", - "integrity": "sha512-Wst46p51km8gH0ZUmeNrtpRYmdlRHUpN1DQd3GFAyKANi8WVz8c2jHYTf1CVScFaCjQw1iO3ZZdqGDxQPRErTg==", - "requires": { - "@lezer/common": "^1.0.0" - } - }, - "@lezer/markdown": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@lezer/markdown/-/markdown-1.2.0.tgz", - "integrity": "sha512-d7MwsfAukZJo1GpPrcPGa3MxaFFOqNp0gbqF+3F7pTeNDOgeJN1muXzx1XXDPt+Ac+/voCzsH7qXqnn+xReG/g==", - "requires": { - "@lezer/common": "^1.0.0", - "@lezer/highlight": "^1.0.0" - } - }, - "@motionone/animation": { - "version": "10.17.0", - "resolved": "https://registry.npmjs.org/@motionone/animation/-/animation-10.17.0.tgz", - "integrity": "sha512-ANfIN9+iq1kGgsZxs+Nz96uiNcPLGTXwfNo2Xz/fcJXniPYpaz/Uyrfa+7I5BPLxCP82sh7quVDudf1GABqHbg==", - "requires": { - "@motionone/easing": "^10.17.0", - "@motionone/types": "^10.17.0", - "@motionone/utils": "^10.17.0", - "tslib": "^2.3.1" - } - }, - "@motionone/dom": { - "version": "10.12.0", - "resolved": "https://registry.npmjs.org/@motionone/dom/-/dom-10.12.0.tgz", - "integrity": "sha512-UdPTtLMAktHiqV0atOczNYyDd/d8Cf5fFsd1tua03PqTwwCe/6lwhLSQ8a7TbnQ5SN0gm44N1slBfj+ORIhrqw==", - "requires": { - "@motionone/animation": "^10.12.0", - "@motionone/generators": "^10.12.0", - "@motionone/types": "^10.12.0", - "@motionone/utils": "^10.12.0", - "hey-listen": "^1.0.8", - "tslib": "^2.3.1" - } - }, - "@motionone/easing": { - "version": "10.17.0", - "resolved": "https://registry.npmjs.org/@motionone/easing/-/easing-10.17.0.tgz", - "integrity": "sha512-Bxe2wSuLu/qxqW4rBFS5m9tMLOw+QBh8v5A7Z5k4Ul4sTj5jAOfZG5R0bn5ywmk+Fs92Ij1feZ5pmC4TeXA8Tg==", - "requires": { - "@motionone/utils": "^10.17.0", - "tslib": "^2.3.1" - } - }, - "@motionone/generators": { - "version": "10.17.0", - "resolved": "https://registry.npmjs.org/@motionone/generators/-/generators-10.17.0.tgz", - "integrity": "sha512-T6Uo5bDHrZWhIfxG/2Aut7qyWQyJIWehk6OB4qNvr/jwA/SRmixwbd7SOrxZi1z5rH3LIeFFBKK1xHnSbGPZSQ==", - "requires": { - "@motionone/types": "^10.17.0", - "@motionone/utils": "^10.17.0", - "tslib": "^2.3.1" - } - }, - "@motionone/types": { - "version": "10.17.0", - "resolved": "https://registry.npmjs.org/@motionone/types/-/types-10.17.0.tgz", - "integrity": "sha512-EgeeqOZVdRUTEHq95Z3t8Rsirc7chN5xFAPMYFobx8TPubkEfRSm5xihmMUkbaR2ErKJTUw3347QDPTHIW12IA==" - }, - "@motionone/utils": { - "version": "10.17.0", - "resolved": "https://registry.npmjs.org/@motionone/utils/-/utils-10.17.0.tgz", - "integrity": "sha512-bGwrki4896apMWIj9yp5rAS2m0xyhxblg6gTB/leWDPt+pb410W8lYWsxyurX+DH+gO1zsQsfx2su/c1/LtTpg==", - "requires": { - "@motionone/types": "^10.17.0", - "hey-listen": "^1.0.8", - "tslib": "^2.3.1" - } - }, - "@n1ru4l/push-pull-async-iterable-iterator": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@n1ru4l/push-pull-async-iterable-iterator/-/push-pull-async-iterable-iterator-3.2.0.tgz", - "integrity": "sha512-3fkKj25kEjsfObL6IlKPAlHYPq/oYwUkkQ03zsTTiDjD7vg/RxjdiLeCydqtxHZP0JgsXL3D/X5oAkMGzuUp/Q==" - }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } - }, - "@parcel/watcher": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.4.0.tgz", - "integrity": "sha512-XJLGVL0DEclX5pcWa2N9SX1jCGTDd8l972biNooLFtjneuGqodupPQh6XseXIBBeVIMaaJ7bTcs3qGvXwsp4vg==", - "dev": true, - "optional": true, - "peer": true, - "requires": { - "@parcel/watcher-android-arm64": "2.4.0", - "@parcel/watcher-darwin-arm64": "2.4.0", - "@parcel/watcher-darwin-x64": "2.4.0", - "@parcel/watcher-freebsd-x64": "2.4.0", - "@parcel/watcher-linux-arm-glibc": "2.4.0", - "@parcel/watcher-linux-arm64-glibc": "2.4.0", - "@parcel/watcher-linux-arm64-musl": "2.4.0", - "@parcel/watcher-linux-x64-glibc": "2.4.0", - "@parcel/watcher-linux-x64-musl": "2.4.0", - "@parcel/watcher-win32-arm64": "2.4.0", - "@parcel/watcher-win32-ia32": "2.4.0", - "@parcel/watcher-win32-x64": "2.4.0", - "detect-libc": "^1.0.3", - "is-glob": "^4.0.3", - "micromatch": "^4.0.5", - "node-addon-api": "^7.0.0" - } - }, - "@parcel/watcher-darwin-arm64": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.4.0.tgz", - "integrity": "sha512-T/At5pansFuQ8VJLRx0C6C87cgfqIYhW2N/kBfLCUvDhCah0EnLLwaD/6MW3ux+rpgkpQAnMELOCTKlbwncwiA==", - "dev": true, - "optional": true, - "peer": true - }, - "@peculiar/asn1-schema": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.3.8.tgz", - "integrity": "sha512-ULB1XqHKx1WBU/tTFIA+uARuRoBVZ4pNdOA878RDrRbBfBGcSzi5HBkdScC6ZbHn8z7L8gmKCgPC1LHRrP46tA==", - "dev": true, - "requires": { - "asn1js": "^3.0.5", - "pvtsutils": "^1.3.5", - "tslib": "^2.6.2" - } - }, - "@peculiar/json-schema": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/@peculiar/json-schema/-/json-schema-1.1.12.tgz", - "integrity": "sha512-coUfuoMeIB7B8/NMekxaDzLhaYmp0HZNPEjYRm9goRou8UZIC3z21s0sL9AWoCw4EG876QyO3kYrc61WNF9B/w==", - "dev": true, - "requires": { - "tslib": "^2.0.0" - } - }, - "@peculiar/webcrypto": { - "version": "1.4.6", - "resolved": "https://registry.npmjs.org/@peculiar/webcrypto/-/webcrypto-1.4.6.tgz", - "integrity": "sha512-YBcMfqNSwn3SujUJvAaySy5tlYbYm6tVt9SKoXu8BaTdKGROiJDgPR3TXpZdAKUfklzm3lRapJEAltiMQtBgZg==", - "dev": true, - "requires": { - "@peculiar/asn1-schema": "^2.3.8", - "@peculiar/json-schema": "^1.1.12", - "pvtsutils": "^1.3.5", - "tslib": "^2.6.2", - "webcrypto-core": "^1.7.9" - } - }, - "@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "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==", - "dev": true, - "requires": { - "playwright": "1.47.0" - } - }, - "@popperjs/core": { - "version": "2.11.8", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", - "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==" - }, - "@radix-ui/primitive": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.1.tgz", - "integrity": "sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==", - "requires": { - "@babel/runtime": "^7.13.10" - } - }, - "@radix-ui/react-arrow": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.0.3.tgz", - "integrity": "sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==", - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-primitive": "1.0.3" - } - }, - "@radix-ui/react-collection": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.0.3.tgz", - "integrity": "sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==", - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-slot": "1.0.2" - } - }, - "@radix-ui/react-compose-refs": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz", - "integrity": "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==", - "requires": { - "@babel/runtime": "^7.13.10" - } - }, - "@radix-ui/react-context": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.0.1.tgz", - "integrity": "sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==", - "requires": { - "@babel/runtime": "^7.13.10" - } - }, - "@radix-ui/react-dialog": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.0.5.tgz", - "integrity": "sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q==", - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-dismissable-layer": "1.0.5", - "@radix-ui/react-focus-guards": "1.0.1", - "@radix-ui/react-focus-scope": "1.0.4", - "@radix-ui/react-id": "1.0.1", - "@radix-ui/react-portal": "1.0.4", - "@radix-ui/react-presence": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-slot": "1.0.2", - "@radix-ui/react-use-controllable-state": "1.0.1", - "aria-hidden": "^1.1.1", - "react-remove-scroll": "2.5.5" - } - }, - "@radix-ui/react-direction": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.0.1.tgz", - "integrity": "sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==", - "requires": { - "@babel/runtime": "^7.13.10" - } - }, - "@radix-ui/react-dismissable-layer": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.5.tgz", - "integrity": "sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==", - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-callback-ref": "1.0.1", - "@radix-ui/react-use-escape-keydown": "1.0.3" - } - }, - "@radix-ui/react-dropdown-menu": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.0.6.tgz", - "integrity": "sha512-i6TuFOoWmLWq+M/eCLGd/bQ2HfAX1RJgvrBQ6AQLmzfvsLdefxbWu8G9zczcPFfcSPehz9GcpF6K9QYreFV8hA==", - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-id": "1.0.1", - "@radix-ui/react-menu": "2.0.6", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-controllable-state": "1.0.1" - } - }, - "@radix-ui/react-focus-guards": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.1.tgz", - "integrity": "sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==", - "requires": { - "@babel/runtime": "^7.13.10" - } - }, - "@radix-ui/react-focus-scope": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.4.tgz", - "integrity": "sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA==", - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-callback-ref": "1.0.1" - } - }, - "@radix-ui/react-id": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.1.tgz", - "integrity": "sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==", - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-layout-effect": "1.0.1" - } - }, - "@radix-ui/react-label": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.0.2.tgz", - "integrity": "sha512-N5ehvlM7qoTLx7nWPodsPYPgMzA5WM8zZChQg8nyFJKnDO5WHdba1vv5/H6IO5LtJMfD2Q3wh1qHFGNtK0w3bQ==", - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-primitive": "1.0.3" - } - }, - "@radix-ui/react-menu": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.0.6.tgz", - "integrity": "sha512-BVkFLS+bUC8HcImkRKPSiVumA1VPOOEC5WBMiT+QAVsPzW1FJzI9KnqgGxVDPBcql5xXrHkD3JOVoXWEXD8SYA==", - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-collection": "1.0.3", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-direction": "1.0.1", - "@radix-ui/react-dismissable-layer": "1.0.5", - "@radix-ui/react-focus-guards": "1.0.1", - "@radix-ui/react-focus-scope": "1.0.4", - "@radix-ui/react-id": "1.0.1", - "@radix-ui/react-popper": "1.1.3", - "@radix-ui/react-portal": "1.0.4", - "@radix-ui/react-presence": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-roving-focus": "1.0.4", - "@radix-ui/react-slot": "1.0.2", - "@radix-ui/react-use-callback-ref": "1.0.1", - "aria-hidden": "^1.1.1", - "react-remove-scroll": "2.5.5" - } - }, - "@radix-ui/react-popover": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.0.7.tgz", - "integrity": "sha512-shtvVnlsxT6faMnK/a7n0wptwBD23xc1Z5mdrtKLwVEfsEMXodS0r5s0/g5P0hX//EKYZS2sxUjqfzlg52ZSnQ==", - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-dismissable-layer": "1.0.5", - "@radix-ui/react-focus-guards": "1.0.1", - "@radix-ui/react-focus-scope": "1.0.4", - "@radix-ui/react-id": "1.0.1", - "@radix-ui/react-popper": "1.1.3", - "@radix-ui/react-portal": "1.0.4", - "@radix-ui/react-presence": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-slot": "1.0.2", - "@radix-ui/react-use-controllable-state": "1.0.1", - "aria-hidden": "^1.1.1", - "react-remove-scroll": "2.5.5" - } - }, - "@radix-ui/react-popper": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.1.3.tgz", - "integrity": "sha512-cKpopj/5RHZWjrbF2846jBNacjQVwkP068DfmgrNJXpvVWrOvlAmE9xSiy5OqeE+Gi8D9fP+oDhUnPqNMY8/5w==", - "requires": { - "@babel/runtime": "^7.13.10", - "@floating-ui/react-dom": "^2.0.0", - "@radix-ui/react-arrow": "1.0.3", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-callback-ref": "1.0.1", - "@radix-ui/react-use-layout-effect": "1.0.1", - "@radix-ui/react-use-rect": "1.0.1", - "@radix-ui/react-use-size": "1.0.1", - "@radix-ui/rect": "1.0.1" - } - }, - "@radix-ui/react-portal": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.4.tgz", - "integrity": "sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==", - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-primitive": "1.0.3" - } - }, - "@radix-ui/react-presence": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.0.1.tgz", - "integrity": "sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==", - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-use-layout-effect": "1.0.1" - } - }, - "@radix-ui/react-primitive": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz", - "integrity": "sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==", - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-slot": "1.0.2" - } - }, - "@radix-ui/react-progress": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.0.3.tgz", - "integrity": "sha512-5G6Om/tYSxjSeEdrb1VfKkfZfn/1IlPWd731h2RfPuSbIfNUgfqAwbKfJCg/PP6nuUCTrYzalwHSpSinoWoCag==", - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-primitive": "1.0.3" - } - }, - "@radix-ui/react-roving-focus": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.0.4.tgz", - "integrity": "sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==", - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-collection": "1.0.3", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-direction": "1.0.1", - "@radix-ui/react-id": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-callback-ref": "1.0.1", - "@radix-ui/react-use-controllable-state": "1.0.1" - } - }, - "@radix-ui/react-slot": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz", - "integrity": "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==", - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-compose-refs": "1.0.1" - } - }, - "@radix-ui/react-tooltip": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.0.7.tgz", - "integrity": "sha512-lPh5iKNFVQ/jav/j6ZrWq3blfDJ0OH9R6FlNUHPMqdLuQ9vwDgFsRxvl8b7Asuy5c8xmoojHUxKHQSOAvMHxyw==", - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-dismissable-layer": "1.0.5", - "@radix-ui/react-id": "1.0.1", - "@radix-ui/react-popper": "1.1.3", - "@radix-ui/react-portal": "1.0.4", - "@radix-ui/react-presence": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-slot": "1.0.2", - "@radix-ui/react-use-controllable-state": "1.0.1", - "@radix-ui/react-visually-hidden": "1.0.3" - } - }, - "@radix-ui/react-use-callback-ref": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz", - "integrity": "sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==", - "requires": { - "@babel/runtime": "^7.13.10" - } - }, - "@radix-ui/react-use-controllable-state": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.1.tgz", - "integrity": "sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==", - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-callback-ref": "1.0.1" - } - }, - "@radix-ui/react-use-escape-keydown": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.3.tgz", - "integrity": "sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==", - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-callback-ref": "1.0.1" - } - }, - "@radix-ui/react-use-layout-effect": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz", - "integrity": "sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==", - "requires": { - "@babel/runtime": "^7.13.10" - } - }, - "@radix-ui/react-use-rect": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.0.1.tgz", - "integrity": "sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==", - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/rect": "1.0.1" - } - }, - "@radix-ui/react-use-size": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.0.1.tgz", - "integrity": "sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g==", - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-use-layout-effect": "1.0.1" - } - }, - "@radix-ui/react-visually-hidden": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.0.3.tgz", - "integrity": "sha512-D4w41yN5YRKtu464TLnByKzMDG/JlMPHtfZgQAu9v6mNakUqGUI9vUrfQKz8NK41VMm/xbZbh76NUTVtIYqOMA==", - "requires": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-primitive": "1.0.3" - } - }, - "@radix-ui/rect": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.0.1.tgz", - "integrity": "sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==", - "requires": { - "@babel/runtime": "^7.13.10" - } - }, - "@redocly/ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/@redocly/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-9GWx27t7xWhDIR02PA18nzBdLcKQRgc46xNQvjFkrYk4UOmvKhJ/dawwiX0cCOeetN5LcaaiqQbVOWYK62SGHw==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "dependencies": { - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - } - } - }, - "@redocly/config": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/@redocly/config/-/config-0.6.3.tgz", - "integrity": "sha512-hGWJgCsXRw0Ow4rplqRlUQifZvoSwZipkYnt11e3SeH1Eb23VUIDBcRuaQOUqy1wn0eevXkU2GzzQ8fbKdQ7Mg==", - "dev": true - }, - "@redocly/openapi-core": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.18.0.tgz", - "integrity": "sha512-kcbt7w23pcVYGLnJkh2LZpXF1OX5RDM4DLOtwPug2HvRE8ow/YfY8ZEM1YCFlA41D8rBPBVP918cYeIx4BVUbw==", - "dev": true, - "requires": { - "@redocly/ajv": "^8.11.0", - "@redocly/config": "^0.6.2", - "colorette": "^1.2.0", - "https-proxy-agent": "^7.0.4", - "js-levenshtein": "^1.1.6", - "js-yaml": "^4.1.0", - "lodash.isequal": "^4.5.0", - "minimatch": "^5.0.1", - "node-fetch": "^2.6.1", - "pluralize": "^8.0.0", - "yaml-ast-parser": "0.0.43" - }, - "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" - } - }, - "colorette": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", - "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", - "dev": true - }, - "minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - } - } - } - }, - "@remix-run/router": { - "version": "1.15.3", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.3.tgz", - "integrity": "sha512-Oy8rmScVrVxWZVOpEF57ovlnhpZ8CCPlnIIumVcV9nFdiSIrus99+Lw78ekXyGvVDlIsFJbSfmSovJUhCWYV3w==" - }, - "@repeaterjs/repeater": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@repeaterjs/repeater/-/repeater-3.0.5.tgz", - "integrity": "sha512-l3YHBLAol6d/IKnB9LhpD0cEZWAoe3eFKUyTYWmFmCO2Q/WOckxLQAUyMZWwZV2M/m3+4vgRoaolFqaII82/TA==", - "dev": true - }, - "@rollup/pluginutils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz", - "integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==", - "requires": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^2.3.1" - } - }, - "@rollup/rollup-android-arm-eabi": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.4.tgz", - "integrity": "sha512-Fxamp4aEZnfPOcGA8KSNEohV8hX7zVHOemC8jVBoBUHu5zpJK/Eu3uJwt6BMgy9fkvzxDaurgj96F/NiLukF2w==", - "optional": true - }, - "@rollup/rollup-android-arm64": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.22.4.tgz", - "integrity": "sha512-VXoK5UMrgECLYaMuGuVTOx5kcuap1Jm8g/M83RnCHBKOqvPPmROFJGQaZhGccnsFtfXQ3XYa4/jMCJvZnbJBdA==", - "optional": true - }, - "@rollup/rollup-darwin-arm64": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.22.4.tgz", - "integrity": "sha512-xMM9ORBqu81jyMKCDP+SZDhnX2QEVQzTcC6G18KlTQEzWK8r/oNZtKuZaCcHhnsa6fEeOBionoyl5JsAbE/36Q==", - "optional": true - }, - "@rollup/rollup-darwin-x64": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.22.4.tgz", - "integrity": "sha512-aJJyYKQwbHuhTUrjWjxEvGnNNBCnmpHDvrb8JFDbeSH3m2XdHcxDd3jthAzvmoI8w/kSjd2y0udT+4okADsZIw==", - "optional": true - }, - "@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.22.4.tgz", - "integrity": "sha512-j63YtCIRAzbO+gC2L9dWXRh5BFetsv0j0va0Wi9epXDgU/XUi5dJKo4USTttVyK7fGw2nPWK0PbAvyliz50SCQ==", - "optional": true - }, - "@rollup/rollup-linux-arm-musleabihf": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.22.4.tgz", - "integrity": "sha512-dJnWUgwWBX1YBRsuKKMOlXCzh2Wu1mlHzv20TpqEsfdZLb3WoJW2kIEsGwLkroYf24IrPAvOT/ZQ2OYMV6vlrg==", - "optional": true - }, - "@rollup/rollup-linux-arm64-gnu": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.22.4.tgz", - "integrity": "sha512-AdPRoNi3NKVLolCN/Sp4F4N1d98c4SBnHMKoLuiG6RXgoZ4sllseuGioszumnPGmPM2O7qaAX/IJdeDU8f26Aw==", - "optional": true - }, - "@rollup/rollup-linux-arm64-musl": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.22.4.tgz", - "integrity": "sha512-Gl0AxBtDg8uoAn5CCqQDMqAx22Wx22pjDOjBdmG0VIWX3qUBHzYmOKh8KXHL4UpogfJ14G4wk16EQogF+v8hmA==", - "optional": true - }, - "@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.22.4.tgz", - "integrity": "sha512-3aVCK9xfWW1oGQpTsYJJPF6bfpWfhbRnhdlyhak2ZiyFLDaayz0EP5j9V1RVLAAxlmWKTDfS9wyRyY3hvhPoOg==", - "optional": true - }, - "@rollup/rollup-linux-riscv64-gnu": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.22.4.tgz", - "integrity": "sha512-ePYIir6VYnhgv2C5Xe9u+ico4t8sZWXschR6fMgoPUK31yQu7hTEJb7bCqivHECwIClJfKgE7zYsh1qTP3WHUA==", - "optional": true - }, - "@rollup/rollup-linux-s390x-gnu": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.22.4.tgz", - "integrity": "sha512-GqFJ9wLlbB9daxhVlrTe61vJtEY99/xB3C8e4ULVsVfflcpmR6c8UZXjtkMA6FhNONhj2eA5Tk9uAVw5orEs4Q==", - "optional": true - }, - "@rollup/rollup-linux-x64-gnu": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.22.4.tgz", - "integrity": "sha512-87v0ol2sH9GE3cLQLNEy0K/R0pz1nvg76o8M5nhMR0+Q+BBGLnb35P0fVz4CQxHYXaAOhE8HhlkaZfsdUOlHwg==", - "optional": true - }, - "@rollup/rollup-linux-x64-musl": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.22.4.tgz", - "integrity": "sha512-UV6FZMUgePDZrFjrNGIWzDo/vABebuXBhJEqrHxrGiU6HikPy0Z3LfdtciIttEUQfuDdCn8fqh7wiFJjCNwO+g==", - "optional": true - }, - "@rollup/rollup-win32-arm64-msvc": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.22.4.tgz", - "integrity": "sha512-BjI+NVVEGAXjGWYHz/vv0pBqfGoUH0IGZ0cICTn7kB9PyjrATSkX+8WkguNjWoj2qSr1im/+tTGRaY+4/PdcQw==", - "optional": true - }, - "@rollup/rollup-win32-ia32-msvc": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.22.4.tgz", - "integrity": "sha512-SiWG/1TuUdPvYmzmYnmd3IEifzR61Tragkbx9D3+R8mzQqDBz8v+BvZNDlkiTtI9T15KYZhP0ehn3Dld4n9J5g==", - "optional": true - }, - "@rollup/rollup-win32-x64-msvc": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.22.4.tgz", - "integrity": "sha512-j8pPKp53/lq9lMXN57S8cFz0MynJk8OWNuUnXct/9KCpKU7DgU3bYMJhwWmcqC0UU29p8Lr0/7KEVcaM6bf47Q==", - "optional": true - }, - "@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==", - "dev": true - }, - "@svgr/babel-plugin-add-jsx-attribute": { - "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==", - "requires": {} - }, - "@svgr/babel-plugin-remove-jsx-attribute": { - "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==", - "requires": {} - }, - "@svgr/babel-plugin-remove-jsx-empty-expression": { - "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==", - "requires": {} - }, - "@svgr/babel-plugin-replace-jsx-attribute-value": { - "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==", - "requires": {} - }, - "@svgr/babel-plugin-svg-dynamic-title": { - "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==", - "requires": {} - }, - "@svgr/babel-plugin-svg-em-dimensions": { - "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==", - "requires": {} - }, - "@svgr/babel-plugin-transform-react-native-svg": { - "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==", - "requires": {} - }, - "@svgr/babel-plugin-transform-svg-component": { - "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==", - "requires": {} - }, - "@svgr/babel-preset": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-8.1.0.tgz", - "integrity": "sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==", - "requires": { - "@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" - } - }, - "@svgr/core": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", - "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", - "requires": { - "@babel/core": "^7.21.3", - "@svgr/babel-preset": "8.1.0", - "camelcase": "^6.2.0", - "cosmiconfig": "^8.1.3", - "snake-case": "^3.0.4" - } - }, - "@svgr/hast-util-to-babel-ast": { - "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==", - "requires": { - "@babel/types": "^7.21.3", - "entities": "^4.4.0" - } - }, - "@svgr/plugin-jsx": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-8.1.0.tgz", - "integrity": "sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==", - "requires": { - "@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" - } - }, - "@svgr/plugin-svgo": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-8.1.0.tgz", - "integrity": "sha512-Ywtl837OGO9pTLIN/onoWLmDQ4zFUycI1g76vuKGEz6evR/ZTJlJuz3G/fIkb6OVBJ2g0o6CGJzaEjfmEo3AHA==", - "requires": { - "cosmiconfig": "^8.1.3", - "deepmerge": "^4.3.1", - "svgo": "^3.0.2" - } - }, - "@svgr/rollup": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@svgr/rollup/-/rollup-8.1.0.tgz", - "integrity": "sha512-0XR1poYvPQoPpmfDYLEqUGu5ePAQ4pdgN3VFsZBNAeze7qubVpsIY1o1R6PZpKep/DKu33GSm2NhwpCLkMs2Cw==", - "requires": { - "@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.21.0", - "@rollup/pluginutils": "^5.0.2", - "@svgr/core": "8.1.0", - "@svgr/plugin-jsx": "8.1.0", - "@svgr/plugin-svgo": "8.1.0" - } - }, - "@tailwindcss/forms": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.7.tgz", - "integrity": "sha512-QE7X69iQI+ZXwldE+rzasvbJiyV/ju1FGHH0Qn2W3FKbuYtqp8LKcy6iSw79fVUT5/Vvf+0XgLCeYVG+UV6hOw==", - "requires": { - "mini-svg-data-uri": "^1.2.3" - } - }, - "@tanstack/react-virtual": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.0.2.tgz", - "integrity": "sha512-9XbRLPKgnhMwwmuQMnJMv+5a9sitGNCSEtf/AZXzmJdesYk7XsjYHaEDny+IrJzvPNwZliIIDwCRiaUqR3zzCA==", - "requires": { - "@tanstack/virtual-core": "3.0.0" - } - }, - "@tanstack/virtual-core": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.0.0.tgz", - "integrity": "sha512-SYXOBTjJb05rXa2vl55TTwO40A6wKu0R5i1qQwhJYNDIqaIGF7D0HsLw+pJAyi2OvntlEIVusx3xtbbgSUi6zg==" - }, - "@testing-library/dom": { - "version": "9.3.4", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.4.tgz", - "integrity": "sha512-FlS4ZWlp97iiNWig0Muq8p+3rVDjRiYE+YKGbAqXOu9nwJFFOdL00kFpz42M+4huzYi86vAK1sOOfyOG45muIQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/runtime": "^7.12.5", - "@types/aria-query": "^5.0.1", - "aria-query": "5.1.3", - "chalk": "^4.1.0", - "dom-accessibility-api": "^0.5.9", - "lz-string": "^1.5.0", - "pretty-format": "^27.0.2" - } - }, - "@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==", - "dev": true, - "requires": { - "@babel/runtime": "^7.12.5", - "@testing-library/dom": "^9.0.0", - "@types/react-dom": "^18.0.0" - } - }, - "@trysound/sax": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", - "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==" - }, - "@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "devOptional": 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 - }, - "@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 - }, - "@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 - }, - "@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 - }, - "@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "requires": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "@types/babel__generator": { - "version": "7.6.8", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", - "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@types/babel__traverse": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.5.tgz", - "integrity": "sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==", - "requires": { - "@babel/types": "^7.20.7" - } - }, - "@types/codemirror": { - "version": "5.60.15", - "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-5.60.15.tgz", - "integrity": "sha512-dTOvwEQ+ouKJ/rE9LT1Ue2hmP6H1mZv5+CCnNWu2qtiOe2LQa9lCprEY20HxiDmV/Bxh+dXjywmy5aKvoGjULA==", - "requires": { - "@types/tern": "*" - } - }, - "@types/d3-array": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz", - "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==" - }, - "@types/d3-color": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", - "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==" - }, - "@types/d3-ease": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", - "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==" - }, - "@types/d3-interpolate": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", - "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", - "requires": { - "@types/d3-color": "*" - } - }, - "@types/d3-path": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.0.tgz", - "integrity": "sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ==" - }, - "@types/d3-scale": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz", - "integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==", - "requires": { - "@types/d3-time": "*" - } - }, - "@types/d3-shape": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.6.tgz", - "integrity": "sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==", - "requires": { - "@types/d3-path": "*" - } - }, - "@types/d3-time": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.3.tgz", - "integrity": "sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==" - }, - "@types/d3-timer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", - "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==" - }, - "@types/debug": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", - "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", - "requires": { - "@types/ms": "*" - } - }, - "@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" - }, - "@types/estree-jsx": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.4.tgz", - "integrity": "sha512-5idy3hvI9lAMqsyilBM+N+boaCf1MgoefbDxN6KEO5aK17TOHwFAYT9sjxzeKAiIWRUBgLxmZ9mPcnzZXtTcRQ==", - "requires": { - "@types/estree": "*" - } - }, - "@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", - "requires": { - "@types/unist": "*" - } - }, - "@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==", - "dev": true - }, - "@types/js-yaml": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", - "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", - "dev": true - }, - "@types/json-stable-stringify": { - "version": "1.0.36", - "resolved": "https://registry.npmjs.org/@types/json-stable-stringify/-/json-stable-stringify-1.0.36.tgz", - "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", - "integrity": "sha512-QWOtIkwZqHNdQj3nixQ8oyihQiTMKZLk/DNuvNxMSbTfxf47w+kqcbnxlUeBgAxdOtW0Dh48dTAIp83iJKtnrQ==", - "dev": true, - "requires": { - "@types/react": "*" - } - }, - "@types/mdast": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.3.tgz", - "integrity": "sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==", - "requires": { - "@types/unist": "*" - } - }, - "@types/ms": { - "version": "0.7.34", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", - "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==" - }, - "@types/node": { - "version": "20.12.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.3.tgz", - "integrity": "sha512-sD+ia2ubTeWrOu+YMF+MTAB7E+O7qsMqAbMfW7DG3K1URwhZ5hN1pLlRVGbf4wDFzSfikL05M17EyorS86jShw==", - "devOptional": true, - "requires": { - "undici-types": "~5.26.4" - } - }, - "@types/prismjs": { - "version": "1.26.3", - "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.3.tgz", - "integrity": "sha512-A0D0aTXvjlqJ5ZILMz3rNfDBOx9hHxLZYv2by47Sm/pqW35zzjusrZTryatjN/Rf8Us2gZrJD+KeHbUSTux1Cw==", - "dev": true - }, - "@types/prop-types": { - "version": "15.7.11", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", - "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==" - }, - "@types/ramda": { - "version": "0.29.12", - "resolved": "https://registry.npmjs.org/@types/ramda/-/ramda-0.29.12.tgz", - "integrity": "sha512-sgIEjpJhdQPB52gDF4aphs9nl0xe54CR22DPdWqT8gQHjZYmVApgA0R3/CpMbl0Y8az2TEZrPNL2zy0EvjbkLA==", - "dev": true, - "requires": { - "types-ramda": "^0.29.10" - } - }, - "@types/react": { - "version": "18.2.74", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.74.tgz", - "integrity": "sha512-9AEqNZZyBx8OdZpxzQlaFEVCSFUM2YXJH46yPOiOpm078k6ZLOCcuAzGum/zK8YBwY+dbahVNbHrbgrAwIRlqw==", - "requires": { - "@types/prop-types": "*", - "csstype": "^3.0.2" - } - }, - "@types/react-datepicker": { - "version": "4.19.5", - "resolved": "https://registry.npmjs.org/@types/react-datepicker/-/react-datepicker-4.19.5.tgz", - "integrity": "sha512-tKpuj19p9T4sBQm3Bw13CPuhalo4CFOe/LcSUGJ5z6DmHoiBX3uq33iMKePeSEq7OxyU8O1rh5emAm92nyXZLg==", - "dev": true, - "requires": { - "@popperjs/core": "^2.9.2", - "@types/react": "*", - "date-fns": "^2.0.1", - "react-popper": "^2.2.5" - }, - "dependencies": { - "date-fns": { - "version": "2.30.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", - "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", - "dev": true, - "requires": { - "@babel/runtime": "^7.21.0" - } - } - } - }, - "@types/react-dom": { - "version": "18.2.23", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.23.tgz", - "integrity": "sha512-ZQ71wgGOTmDYpnav2knkjr3qXdAFu0vsk8Ci5w3pGAIdj7/kKAyn+VsQDhXsmzzzepAiI9leWMmubXz690AI/A==", - "devOptional": true, - "requires": { - "@types/react": "*" - } - }, - "@types/react-test-renderer": { - "version": "18.0.7", - "resolved": "https://registry.npmjs.org/@types/react-test-renderer/-/react-test-renderer-18.0.7.tgz", - "integrity": "sha512-1+ANPOWc6rB3IkSnElhjv6VLlKg2dSv/OWClUyZimbLsQyBn8Js9Vtdsi3UICJ2rIQ3k2la06dkB+C92QfhKmg==", - "dev": true, - "requires": { - "@types/react": "*" - } - }, - "@types/sha1": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@types/sha1/-/sha1-1.1.5.tgz", - "integrity": "sha512-eE1PzjW7u2VfxI+bTsvjzjBfpwqvxSpgfUmnRNVY+PJU1NBsdGZlaO/qnVnPKHzzpgIl9YyBIxvrgBvt1mzt2A==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/sinonjs__fake-timers": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz", - "integrity": "sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==", - "dev": true - }, - "@types/sizzle": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.8.tgz", - "integrity": "sha512-0vWLNK2D5MT9dg0iOo8GlKguPAU02QjmZitPEsXRuJXU/OGIOt9vT9Fc26wtYuavLxtO45v9PGleoL9Z0k1LHg==", - "dev": true - }, - "@types/tern": { - "version": "0.23.9", - "resolved": "https://registry.npmjs.org/@types/tern/-/tern-0.23.9.tgz", - "integrity": "sha512-ypzHFE/wBzh+BlH6rrBgS5I/Z7RD21pGhZ2rltb/+ZrVM1awdZwjx7hE5XfuYgHWk9uvV5HLZN3SloevCAp3Bw==", - "requires": { - "@types/estree": "*" - } - }, - "@types/unist": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz", - "integrity": "sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==" - }, - "@types/ws": { - "version": "8.5.10", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", - "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/yauzl": { - "version": "2.10.3", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", - "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", - "dev": true, - "optional": true, - "requires": { - "@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", - "integrity": "sha512-L421mBAT2NRsmYv7BQvofOEwV0iKee1upPVxMjo2NnkJWyIu4I+H1RxK9m3uT8yvcOlStZhv7BQBsFyJCGmIMg==", - "requires": {} - }, - "@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==", - "requires": { - "@uiw/color-convert": "2.1.1", - "@uiw/react-color-alpha": "2.1.1", - "@uiw/react-color-block": "2.1.1", - "@uiw/react-color-chrome": "2.1.1", - "@uiw/react-color-circle": "2.1.1", - "@uiw/react-color-colorful": "2.1.1", - "@uiw/react-color-compact": "2.1.1", - "@uiw/react-color-editable-input": "2.1.1", - "@uiw/react-color-editable-input-hsla": "2.1.1", - "@uiw/react-color-editable-input-rgba": "2.1.1", - "@uiw/react-color-github": "2.1.1", - "@uiw/react-color-hue": "2.1.1", - "@uiw/react-color-material": "2.1.1", - "@uiw/react-color-name": "2.1.1", - "@uiw/react-color-saturation": "2.1.1", - "@uiw/react-color-shade-slider": "2.1.1", - "@uiw/react-color-sketch": "2.1.1", - "@uiw/react-color-slider": "2.1.1", - "@uiw/react-color-swatch": "2.1.1", - "@uiw/react-color-wheel": "2.1.1" - } - }, - "@uiw/react-color-alpha": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@uiw/react-color-alpha/-/react-color-alpha-2.1.1.tgz", - "integrity": "sha512-6wvWLn4Dgb3jIaveLdjhSg2RJIWKJbRU/uHSFtEd8rvXebRt9P7NFr5YsnkHDBUitx9KFxRL6kaI/GQCYU+8nA==", - "requires": { - "@uiw/color-convert": "2.1.1", - "@uiw/react-drag-event-interactive": "2.1.1" - } - }, - "@uiw/react-color-block": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@uiw/react-color-block/-/react-color-block-2.1.1.tgz", - "integrity": "sha512-c4xA42z7aLou8jBjxwLgUYZ+SiaZbVMADPLE/CcBi8EY/NcvvvtrL2wJGqE0g2Aqfey5RjB7nFxUeqSG1N00aA==", - "requires": { - "@uiw/color-convert": "2.1.1", - "@uiw/react-color-editable-input": "2.1.1", - "@uiw/react-color-swatch": "2.1.1" - } - }, - "@uiw/react-color-chrome": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@uiw/react-color-chrome/-/react-color-chrome-2.1.1.tgz", - "integrity": "sha512-tv51lG6Wol8skiclLXXc8yf5nAVig5OjYtuNxsnFr165GP1YJ/mdnS7OIprYF/wP5mz66W7K0iz/8hAIof5/ug==", - "requires": { - "@uiw/color-convert": "2.1.1", - "@uiw/react-color-alpha": "2.1.1", - "@uiw/react-color-editable-input": "2.1.1", - "@uiw/react-color-editable-input-hsla": "2.1.1", - "@uiw/react-color-editable-input-rgba": "2.1.1", - "@uiw/react-color-github": "2.1.1", - "@uiw/react-color-hue": "2.1.1", - "@uiw/react-color-saturation": "2.1.1" - } - }, - "@uiw/react-color-circle": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@uiw/react-color-circle/-/react-color-circle-2.1.1.tgz", - "integrity": "sha512-t/Wr6eT9GLOzywaTmJclOZ3NuirJlMk8eVxoLKM7eRePbN5WowIEMwug/towSU3YrBrEbSSWjZRhfVUqdh7kMw==", - "requires": { - "@uiw/color-convert": "2.1.1", - "@uiw/react-color-swatch": "2.1.1" - } - }, - "@uiw/react-color-colorful": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@uiw/react-color-colorful/-/react-color-colorful-2.1.1.tgz", - "integrity": "sha512-7cGRtYv+llXO7Tmpfska9HxjQAbkqBP5P63wned6JD/0lOM4KXELxhWI3044nO/Osi5r3FDsGh0HRqiLgUK75Q==", - "requires": { - "@uiw/color-convert": "2.1.1", - "@uiw/react-color-alpha": "2.1.1", - "@uiw/react-color-hue": "2.1.1", - "@uiw/react-color-saturation": "2.1.1" - } - }, - "@uiw/react-color-compact": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@uiw/react-color-compact/-/react-color-compact-2.1.1.tgz", - "integrity": "sha512-Af9skc0Bx3lot2zg58SlpMoQEKqyMbKvr7y2O541Hodc89ykgEDJZXqLrKiKnacBWMZxSAcAHoSFf70RLeoTlw==", - "requires": { - "@uiw/color-convert": "2.1.1", - "@uiw/react-color-editable-input": "2.1.1", - "@uiw/react-color-editable-input-rgba": "2.1.1", - "@uiw/react-color-swatch": "2.1.1" - } - }, - "@uiw/react-color-editable-input": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@uiw/react-color-editable-input/-/react-color-editable-input-2.1.1.tgz", - "integrity": "sha512-mEohydHWV49iQ3RuH/3My20T7wDtOPzzGEBOMJeHIxMnN6FMwl9U1bAAgDb2ovnt5Ws0PaCWcBjNKHARpVSZ1Q==", - "requires": {} - }, - "@uiw/react-color-editable-input-hsla": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@uiw/react-color-editable-input-hsla/-/react-color-editable-input-hsla-2.1.1.tgz", - "integrity": "sha512-2Eqcd0hUa5qLVxT062Vf9vsxS2/6X+AJ6f6Wfs6/UAM6iUWqWPkJxajRmEtFfB6Zv5bcaYjhSZNGgeEf7azAvQ==", - "requires": { - "@uiw/color-convert": "2.1.1", - "@uiw/react-color-editable-input-rgba": "2.1.1" - } - }, - "@uiw/react-color-editable-input-rgba": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@uiw/react-color-editable-input-rgba/-/react-color-editable-input-rgba-2.1.1.tgz", - "integrity": "sha512-6YtDaBWTXu27MK6s3HZty0qg3mYb4GN/8dI8T39R/qEiMX/SButMfC09pnygN74InyuG8MzobUg2GowfTRUG5A==", - "requires": { - "@uiw/color-convert": "2.1.1", - "@uiw/react-color-editable-input": "2.1.1" - } - }, - "@uiw/react-color-github": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@uiw/react-color-github/-/react-color-github-2.1.1.tgz", - "integrity": "sha512-5balACHzjVqrkdEsGXI2Ir4iXQrTAHQ7uRzqY+op41uuciIb8yGI1PecJd2qIjUJhk/kZ4nmp6KAjQEAK2iVIQ==", - "requires": { - "@uiw/color-convert": "2.1.1", - "@uiw/react-color-swatch": "2.1.1" - } - }, - "@uiw/react-color-hue": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@uiw/react-color-hue/-/react-color-hue-2.1.1.tgz", - "integrity": "sha512-IvN2acCV35yRfmbscUQbNfwjKF+g51kMONv9j0zxDlTct2R0x4gatsVjA1tTpLv5UCIkFvhw80xg04QATrJ4Nw==", - "requires": { - "@uiw/color-convert": "2.1.1", - "@uiw/react-color-alpha": "2.1.1" - } - }, - "@uiw/react-color-material": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@uiw/react-color-material/-/react-color-material-2.1.1.tgz", - "integrity": "sha512-Pcp/kpBnTGYXqP0up3rqTdJWKtnD2XdiA5Zdh5bdwqssI+qHo0cVELXOnpwU3LiSCZykTDauvGoOqTWprvox4g==", - "requires": { - "@uiw/color-convert": "2.1.1", - "@uiw/react-color-editable-input": "2.1.1", - "@uiw/react-color-editable-input-rgba": "2.1.1" - } - }, - "@uiw/react-color-name": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@uiw/react-color-name/-/react-color-name-2.1.1.tgz", - "integrity": "sha512-k+19NgPHPZ88oqzCfAcVd7fT4F6XywkeZkX3DDyRG3Skc8zuGdIS2xT7Ne7ZSQb31UT0+UfuOiwOST+r+kGnFA==", - "requires": { - "colors-named": "^1.0.1", - "colors-named-hex": "^1.0.1" - } - }, - "@uiw/react-color-saturation": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@uiw/react-color-saturation/-/react-color-saturation-2.1.1.tgz", - "integrity": "sha512-lg3ElCNuiHt7wsfR9FQpgFcg9zht+GAuVhemvgLq6twR62ZUgFd58in42T1F8l2ZpimXu8SgLGEtvc7XB2i8CQ==", - "requires": { - "@uiw/color-convert": "2.1.1", - "@uiw/react-drag-event-interactive": "2.1.1" - } - }, - "@uiw/react-color-shade-slider": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@uiw/react-color-shade-slider/-/react-color-shade-slider-2.1.1.tgz", - "integrity": "sha512-7DO2d53GGFR6fXPS7g3hUNTlQCwNjKSQum90h4HeJa5jxIQiuSpeMVPo6IoG8EAuP88Au5C55SYV+qjNgLvG8Q==", - "requires": { - "@uiw/color-convert": "2.1.1", - "@uiw/react-color-alpha": "2.1.1" - } - }, - "@uiw/react-color-sketch": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@uiw/react-color-sketch/-/react-color-sketch-2.1.1.tgz", - "integrity": "sha512-hiHhwSJnMzRs9mUfOiqekPosQQ60mhzRN1LDfD/z4sW7GHxWgV9sl9jdoBeN3RRS0O4i/qHjNrJqTad6D4rV7g==", - "requires": { - "@uiw/color-convert": "2.1.1", - "@uiw/react-color-alpha": "2.1.1", - "@uiw/react-color-editable-input": "2.1.1", - "@uiw/react-color-editable-input-rgba": "2.1.1", - "@uiw/react-color-hue": "2.1.1", - "@uiw/react-color-saturation": "2.1.1", - "@uiw/react-color-swatch": "2.1.1" - } - }, - "@uiw/react-color-slider": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@uiw/react-color-slider/-/react-color-slider-2.1.1.tgz", - "integrity": "sha512-J0nqSpiJS4lZCUudAFo8sFMTZgiPgQ0iR4ADx1Hc5vGJr5KfpGwOVq68cUvqiAqXplUQZPVcjwoBhxl4M4fCzg==", - "requires": { - "@uiw/color-convert": "2.1.1", - "@uiw/react-color-alpha": "2.1.1" - } - }, - "@uiw/react-color-swatch": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@uiw/react-color-swatch/-/react-color-swatch-2.1.1.tgz", - "integrity": "sha512-soKsfVgflcKSBx47PaBocKZ0beIXfk9ruE4r9778mGnDDpxc2RC5zPNfvzQkSLKW+siXIS0cscuvb/8s1zK5jw==", - "requires": { - "@uiw/color-convert": "2.1.1" - } - }, - "@uiw/react-color-wheel": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@uiw/react-color-wheel/-/react-color-wheel-2.1.1.tgz", - "integrity": "sha512-88TYL9GCStBNULEkZ6qlQt8z/jnAf1ZSJwpbVK1JGUwogPJaMAJt8FRSUfzTNpIwYA8ymgK9Y1seng6Z8YkS9Q==", - "requires": { - "@uiw/color-convert": "2.1.1", - "@uiw/react-drag-event-interactive": "2.1.1" - } - }, - "@uiw/react-drag-event-interactive": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@uiw/react-drag-event-interactive/-/react-drag-event-interactive-2.1.1.tgz", - "integrity": "sha512-hJjoJg9ZASzhY6HFwZSnNhx+BJ5rfqqUnpTm6ZtfjCO5DKRZW3CDios0cmMu2Ojvdu0a9qE9x8CURCUuoCzqxw==", - "requires": {} - }, - "@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==" - }, - "@vitejs/plugin-react": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.2.1.tgz", - "integrity": "sha512-oojO9IDc4nCUUi8qIR11KoQm0XFFLIwsRBwHRR4d/88IWghn1y6ckz/bJ8GHDCsYEJee8mDzqtJxh15/cisJNQ==", - "requires": { - "@babel/core": "^7.23.5", - "@babel/plugin-transform-react-jsx-self": "^7.23.3", - "@babel/plugin-transform-react-jsx-source": "^7.23.3", - "@types/babel__core": "^7.20.5", - "react-refresh": "^0.14.0" - } - }, - "@vitest/coverage-v8": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-1.4.0.tgz", - "integrity": "sha512-4hDGyH1SvKpgZnIByr9LhGgCEuF9DKM34IBLCC/fVfy24Z3+PZ+Ii9hsVBsHvY1umM1aGPEjceRkzxCfcQ10wg==", - "dev": true, - "requires": { - "@ampproject/remapping": "^2.2.1", - "@bcoe/v8-coverage": "^0.2.3", - "debug": "^4.3.4", - "istanbul-lib-coverage": "^3.2.2", - "istanbul-lib-report": "^3.0.1", - "istanbul-lib-source-maps": "^5.0.4", - "istanbul-reports": "^3.1.6", - "magic-string": "^0.30.5", - "magicast": "^0.3.3", - "picocolors": "^1.0.0", - "std-env": "^3.5.0", - "strip-literal": "^2.0.0", - "test-exclude": "^6.0.0", - "v8-to-istanbul": "^9.2.0" - } - }, - "@vitest/expect": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.4.0.tgz", - "integrity": "sha512-Jths0sWCJZ8BxjKe+p+eKsoqev1/T8lYcrjavEaz8auEJ4jAVY0GwW3JKmdVU4mmNPLPHixh4GNXP7GFtAiDHA==", - "dev": true, - "requires": { - "@vitest/spy": "1.4.0", - "@vitest/utils": "1.4.0", - "chai": "^4.3.10" - } - }, - "@vitest/runner": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.4.0.tgz", - "integrity": "sha512-EDYVSmesqlQ4RD2VvWo3hQgTJ7ZrFQ2VSJdfiJiArkCerDAGeyF1i6dHkmySqk573jLp6d/cfqCN+7wUB5tLgg==", - "dev": true, - "requires": { - "@vitest/utils": "1.4.0", - "p-limit": "^5.0.0", - "pathe": "^1.1.1" - }, - "dependencies": { - "p-limit": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-5.0.0.tgz", - "integrity": "sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==", - "dev": true, - "requires": { - "yocto-queue": "^1.0.0" - } - }, - "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==", - "dev": true - } - } - }, - "@vitest/snapshot": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.4.0.tgz", - "integrity": "sha512-saAFnt5pPIA5qDGxOHxJ/XxhMFKkUSBJmVt5VgDsAqPTX6JP326r5C/c9UuCMPoXNzuudTPsYDZCoJ5ilpqG2A==", - "dev": true, - "requires": { - "magic-string": "^0.30.5", - "pathe": "^1.1.1", - "pretty-format": "^29.7.0" - }, - "dependencies": { - "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 - }, - "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.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - } - } - }, - "@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, - "requires": { - "tinyspy": "^2.2.0" - } - }, - "@vitest/utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.4.0.tgz", - "integrity": "sha512-mx3Yd1/6e2Vt/PUC98DcqTirtfxUyAZ32uK82r8rZzbtBeBo+nqgnjx/LvqQdWsrvNtm14VmurNgcf4nqY5gJg==", - "dev": true, - "requires": { - "diff-sequences": "^29.6.3", - "estree-walker": "^3.0.3", - "loupe": "^2.3.7", - "pretty-format": "^29.7.0" - }, - "dependencies": { - "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 - }, - "estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "dev": true, - "requires": { - "@types/estree": "^1.0.0" - } - }, - "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.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - } - } - }, - "@whatwg-node/events": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/@whatwg-node/events/-/events-0.0.3.tgz", - "integrity": "sha512-IqnKIDWfXBJkvy/k6tzskWTc2NK3LcqHlb+KHGCrjOCH4jfQckRX0NAiIcC/vIqQkzLYw2r2CTSwAxcrtcD6lA==", - "dev": true - }, - "@whatwg-node/fetch": { - "version": "0.8.8", - "resolved": "https://registry.npmjs.org/@whatwg-node/fetch/-/fetch-0.8.8.tgz", - "integrity": "sha512-CdcjGC2vdKhc13KKxgsc6/616BQ7ooDIgPeTuAiE8qfCnS0mGzcfCOoZXypQSz73nxI+GWc7ZReIAVhxoE1KCg==", - "dev": true, - "requires": { - "@peculiar/webcrypto": "^1.4.0", - "@whatwg-node/node-fetch": "^0.3.6", - "busboy": "^1.6.0", - "urlpattern-polyfill": "^8.0.0", - "web-streams-polyfill": "^3.2.1" - } - }, - "@whatwg-node/node-fetch": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/@whatwg-node/node-fetch/-/node-fetch-0.3.6.tgz", - "integrity": "sha512-w9wKgDO4C95qnXZRwZTfCmLWqyRnooGjcIwG0wADWjw9/HN0p7dtvtgSvItZtUyNteEvgTrd8QojNEqV6DAGTA==", - "dev": true, - "requires": { - "@whatwg-node/events": "^0.0.3", - "busboy": "^1.6.0", - "fast-querystring": "^1.1.1", - "fast-url-parser": "^1.1.3", - "tslib": "^2.3.1" - } - }, - "@wry/caches": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@wry/caches/-/caches-1.0.1.tgz", - "integrity": "sha512-bXuaUNLVVkD20wcGBWRyo7j9N3TxePEWFZj2Y+r9OoUzfqmavM84+mFykRicNsBqatba5JLay1t48wxaXaWnlA==", - "requires": { - "tslib": "^2.3.0" - } - }, - "@wry/context": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/@wry/context/-/context-0.7.4.tgz", - "integrity": "sha512-jmT7Sb4ZQWI5iyu3lobQxICu2nC/vbUhP0vIdd6tHC9PTfenmRmuIFqktc6GH9cgi+ZHnsLWPvfSvc4DrYmKiQ==", - "requires": { - "tslib": "^2.3.0" - } - }, - "@wry/equality": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/@wry/equality/-/equality-0.5.7.tgz", - "integrity": "sha512-BRFORjsTuQv5gxcXsuDXx6oGRhuVsEGwZy6LOzRRfgu+eSfxbhUQ9L9YtSEIuIjY/o7g3iWFjrc5eSY1GXP2Dw==", - "requires": { - "tslib": "^2.3.0" - } - }, - "@wry/trie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@wry/trie/-/trie-0.5.0.tgz", - "integrity": "sha512-FNoYzHawTMk/6KMQoEG5O4PuioX19UbwdQKF44yw0nLfOypfQdjtfZzo/UIJWAJ23sNIFbD1Ug9lbaDGMwbqQA==", - "requires": { - "tslib": "^2.3.0" - } - }, - "acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "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", - "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", - "devOptional": true - }, - "agent-base": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", - "dev": true, - "requires": { - "debug": "^4.3.4" - } - }, - "aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "requires": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - } - }, - "ajv": { - "version": "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", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "dev": true - }, - "ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "requires": { - "type-fest": "^0.21.3" - } - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==" - }, - "anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "arch": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", - "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", - "dev": true - }, - "arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" - }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, - "aria-hidden": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.4.tgz", - "integrity": "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==", - "requires": { - "tslib": "^2.0.0" - } - }, - "aria-query": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", - "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", - "dev": true, - "requires": { - "deep-equal": "^2.0.5" - } - }, - "array-buffer-byte-length": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", - "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", - "dev": true, - "requires": { - "call-bind": "^1.0.5", - "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", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "dev": true - }, - "asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "dev": true, - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "asn1js": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.5.tgz", - "integrity": "sha512-FVnvrKJwpt9LP2lAMl8qZswRNm3T4q9CON+bxldk2iwk3FFpuwhx2FfinyitizWHsVYyaY+y5JzDR0rCMV5yTQ==", - "dev": true, - "requires": { - "pvtsutils": "^1.3.2", - "pvutils": "^1.1.3", - "tslib": "^2.4.0" - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", - "dev": true - }, - "assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true - }, - "astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true - }, - "async": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", - "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", - "dev": true - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true - }, - "at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "dev": true - }, - "auto-bind": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/auto-bind/-/auto-bind-4.0.0.tgz", - "integrity": "sha512-Hdw8qdNiqdJ8LqT0iK0sVzkFbzg6fhnQqqfWhBDxcHZvU75+B+ayzTy8x+k5Ix0Y92XOhOUlx74ps+bA6BeYMQ==", - "dev": true - }, - "autoprefixer": { - "version": "10.4.19", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz", - "integrity": "sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==", - "requires": { - "browserslist": "^4.23.0", - "caniuse-lite": "^1.0.30001599", - "fraction.js": "^4.3.7", - "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", - "postcss-value-parser": "^4.2.0" - } - }, - "available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", - "dev": true, - "requires": { - "possible-typed-array-names": "^1.0.0" - } - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", - "dev": true - }, - "aws4": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", - "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==", - "dev": true - }, - "babel-plugin-polyfill-corejs2": { - "version": "0.4.10", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.10.tgz", - "integrity": "sha512-rpIuu//y5OX6jVU+a5BCn1R5RSZYWAl2Nar76iwaOdycqb6JPxediskWFMMl7stfwNJR4b7eiQvh5fB5TEQJTQ==", - "requires": { - "@babel/compat-data": "^7.22.6", - "@babel/helper-define-polyfill-provider": "^0.6.1", - "semver": "^6.3.1" - } - }, - "babel-plugin-polyfill-corejs3": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.4.tgz", - "integrity": "sha512-25J6I8NGfa5YkCDogHRID3fVCadIR8/pGl1/spvCkzb6lVn6SR3ojpx9nOn9iEBcUsjY24AmdKm5khcfKdylcg==", - "requires": { - "@babel/helper-define-polyfill-provider": "^0.6.1", - "core-js-compat": "^3.36.1" - } - }, - "babel-plugin-polyfill-regenerator": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.1.tgz", - "integrity": "sha512-JfTApdE++cgcTWjsiCQlLyFBMbTUft9ja17saCc93lgV33h4tuCVj7tlvu//qpLwaG+3yEz7/KhahGrUMkVq9g==", - "requires": { - "@babel/helper-define-polyfill-provider": "^0.6.1" - } - }, - "babel-plugin-syntax-trailing-function-commas": { - "version": "7.0.0-beta.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-7.0.0-beta.0.tgz", - "integrity": "sha512-Xj9XuRuz3nTSbaTXWv3itLOcxyF4oPD8douBBmj7U9BBC6nEBYfyOJYQMf/8PJAFotC62UY5dFfIGEPr7WswzQ==", - "dev": true - }, - "babel-preset-fbjs": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/babel-preset-fbjs/-/babel-preset-fbjs-3.4.0.tgz", - "integrity": "sha512-9ywCsCvo1ojrw0b+XYk7aFvTH6D9064t0RIL1rtMf3nsa02Xw41MS7sZw216Im35xj/UY0PDBQsa1brUDDF1Ow==", - "dev": true, - "requires": { - "@babel/plugin-proposal-class-properties": "^7.0.0", - "@babel/plugin-proposal-object-rest-spread": "^7.0.0", - "@babel/plugin-syntax-class-properties": "^7.0.0", - "@babel/plugin-syntax-flow": "^7.0.0", - "@babel/plugin-syntax-jsx": "^7.0.0", - "@babel/plugin-syntax-object-rest-spread": "^7.0.0", - "@babel/plugin-transform-arrow-functions": "^7.0.0", - "@babel/plugin-transform-block-scoped-functions": "^7.0.0", - "@babel/plugin-transform-block-scoping": "^7.0.0", - "@babel/plugin-transform-classes": "^7.0.0", - "@babel/plugin-transform-computed-properties": "^7.0.0", - "@babel/plugin-transform-destructuring": "^7.0.0", - "@babel/plugin-transform-flow-strip-types": "^7.0.0", - "@babel/plugin-transform-for-of": "^7.0.0", - "@babel/plugin-transform-function-name": "^7.0.0", - "@babel/plugin-transform-literals": "^7.0.0", - "@babel/plugin-transform-member-expression-literals": "^7.0.0", - "@babel/plugin-transform-modules-commonjs": "^7.0.0", - "@babel/plugin-transform-object-super": "^7.0.0", - "@babel/plugin-transform-parameters": "^7.0.0", - "@babel/plugin-transform-property-literals": "^7.0.0", - "@babel/plugin-transform-react-display-name": "^7.0.0", - "@babel/plugin-transform-react-jsx": "^7.0.0", - "@babel/plugin-transform-shorthand-properties": "^7.0.0", - "@babel/plugin-transform-spread": "^7.0.0", - "@babel/plugin-transform-template-literals": "^7.0.0", - "babel-plugin-syntax-trailing-function-commas": "^7.0.0-beta.0" - } - }, - "backo2": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", - "integrity": "sha512-zj6Z6M7Eq+PBZ7PQxl5NT665MvJdAkzp0f60nAJ+sLaSCBPMwVak5ZegFbgVCzFcCJTKFoMizvM5Ld7+JrRJHA==" - }, - "bail": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", - "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==" - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true - }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", - "dev": true, - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" - }, - "bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dev": true, - "requires": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "blob-util": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/blob-util/-/blob-util-2.0.2.tgz", - "integrity": "sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==", - "dev": true - }, - "bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true - }, - "boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "requires": { - "fill-range": "^7.1.1" - } - }, - "browserslist": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", - "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", - "requires": { - "caniuse-lite": "^1.0.30001587", - "electron-to-chromium": "^1.4.668", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" - } - }, - "bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "requires": { - "node-int64": "^0.4.0" - } - }, - "buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, - "requires": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "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", - "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", - "dev": true, - "requires": { - "streamsearch": "^1.1.0" - } - }, - "cac": { - "version": "6.7.14", - "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", - "dev": true - }, - "cachedir": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.4.0.tgz", - "integrity": "sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ==", - "dev": true - }, - "call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", - "dev": true, - "requires": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" - }, - "camel-case": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", - "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", - "dev": true, - "requires": { - "pascal-case": "^3.1.2", - "tslib": "^2.0.3" - } - }, - "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==" - }, - "camelcase-css": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", - "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==" - }, - "caniuse-lite": { - "version": "1.0.30001605", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001605.tgz", - "integrity": "sha512-nXwGlFWo34uliI9z3n6Qc0wZaf7zaZWA1CPZ169La5mV3I/gem7bst0vr5XQH5TJXZIMfDeZyOrZnSlVzKxxHQ==" - }, - "capital-case": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/capital-case/-/capital-case-1.0.4.tgz", - "integrity": "sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==", - "dev": true, - "requires": { - "no-case": "^3.0.4", - "tslib": "^2.0.3", - "upper-case-first": "^2.0.2" - } - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", - "dev": true - }, - "ccount": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", - "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==", - "dev": true, - "requires": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.3", - "deep-eql": "^4.1.3", - "get-func-name": "^2.0.2", - "loupe": "^2.3.6", - "pathval": "^1.1.1", - "type-detect": "^4.0.8" - } - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "change-case": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/change-case/-/change-case-4.1.2.tgz", - "integrity": "sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A==", - "dev": true, - "requires": { - "camel-case": "^4.1.2", - "capital-case": "^1.0.4", - "constant-case": "^3.0.4", - "dot-case": "^3.0.4", - "header-case": "^2.0.4", - "no-case": "^3.0.4", - "param-case": "^3.0.4", - "pascal-case": "^3.1.2", - "path-case": "^3.0.4", - "sentence-case": "^3.0.4", - "snake-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "change-case-all": { - "version": "1.0.15", - "resolved": "https://registry.npmjs.org/change-case-all/-/change-case-all-1.0.15.tgz", - "integrity": "sha512-3+GIFhk3sNuvFAJKU46o26OdzudQlPNBCu1ZQi3cMeMHhty1bhDxu2WrEilVNYaGvqUtR1VSigFcJOiS13dRhQ==", - "dev": true, - "requires": { - "change-case": "^4.1.2", - "is-lower-case": "^2.0.2", - "is-upper-case": "^2.0.2", - "lower-case": "^2.0.2", - "lower-case-first": "^2.0.2", - "sponge-case": "^1.0.1", - "swap-case": "^2.0.2", - "title-case": "^3.0.3", - "upper-case": "^2.0.2", - "upper-case-first": "^2.0.2" - } - }, - "character-entities": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", - "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==" - }, - "character-entities-html4": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", - "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==" - }, - "character-entities-legacy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", - "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==" - }, - "character-reference-invalid": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", - "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==" - }, - "chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true - }, - "charenc": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", - "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==" - }, - "check-error": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", - "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", - "dev": true, - "requires": { - "get-func-name": "^2.0.2" - } - }, - "check-more-types": { - "version": "2.24.0", - "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", - "integrity": "sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==", - "dev": true - }, - "chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "requires": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "fsevents": "~2.3.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - } - }, - "ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "dev": true - }, - "class-variance-authority": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.0.tgz", - "integrity": "sha512-jFI8IQw4hczaL4ALINxqLEXQbWcNjoSkloa4IaufXCJr6QawJyw7tuRysRsrE8w2p/4gGaxKIt/hX3qz/IbD1A==", - "requires": { - "clsx": "2.0.0" - }, - "dependencies": { - "clsx": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz", - "integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==" - } - } - }, - "classnames": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", - "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==" - }, - "clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true - }, - "cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "requires": { - "restore-cursor": "^3.1.0" - } - }, - "cli-spinners": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", - "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", - "dev": true - }, - "cli-table3": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz", - "integrity": "sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==", - "dev": true, - "requires": { - "@colors/colors": "1.5.0", - "string-width": "^4.2.0" - } - }, - "cli-truncate": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", - "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", - "dev": true, - "requires": { - "slice-ansi": "^3.0.0", - "string-width": "^4.2.0" - } - }, - "cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", - "dev": true - }, - "client-only": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", - "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" - }, - "cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "dependencies": { - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - } - } - }, - "clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "dev": true - }, - "clsx": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.0.tgz", - "integrity": "sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==" - }, - "cm6-graphql": { - "version": "0.0.14", - "resolved": "https://registry.npmjs.org/cm6-graphql/-/cm6-graphql-0.0.14.tgz", - "integrity": "sha512-iiRnTVcALKSbxp7ExenZm5qC56Tjgc7wahMPLtj+XFTOIldrrwMDPTv2NGF/WB3x+PUi568w5pB73lpH9cSQGw==", - "requires": { - "graphql-language-service": "^5.2.0" - } - }, - "cm6-theme-basic-light": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/cm6-theme-basic-light/-/cm6-theme-basic-light-0.2.0.tgz", - "integrity": "sha512-1prg2gv44sYfpHscP26uLT/ePrh0mlmVwMSoSd3zYKQ92Ab3jPRLzyCnpyOCQLJbK+YdNs4HvMRqMNYdy4pMhA==", - "requires": {} - }, - "codemirror": { - "version": "5.65.16", - "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.16.tgz", - "integrity": "sha512-br21LjYmSlVL0vFCPWPfhzUCT34FM/pAdK7rRIZwa0rrtrIdotvP4Oh4GUHsu2E3IrQMCfRkL/fN3ytMNxVQvg==" - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "dev": true - }, - "colors-named": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/colors-named/-/colors-named-1.0.2.tgz", - "integrity": "sha512-2ANq2r393PV9njYUD66UdfBcxR1slMqRA3QRTWgCx49JoCJ+kOhyfbQYxKJbPZQIhZUcNjVOs5AlyY1WwXec3w==" - }, - "colors-named-hex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/colors-named-hex/-/colors-named-hex-1.0.2.tgz", - "integrity": "sha512-k6kq1e1pUCQvSVwIaGFq2l0LrkAPQZWyeuZn1Z8nOiYSEZiKoFj4qx690h2Kd34DFl9Me0gKS6MUwAMBJj8nuA==" - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "comma-separated-tokens": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", - "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==" - }, - "commander": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", - "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", - "dev": true - }, - "common-tags": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", - "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "constant-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/constant-case/-/constant-case-3.0.4.tgz", - "integrity": "sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==", - "dev": true, - "requires": { - "no-case": "^3.0.4", - "tslib": "^2.0.3", - "upper-case": "^2.0.2" - } - }, - "convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" - }, - "copy-to-clipboard": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz", - "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==", - "requires": { - "toggle-selection": "^1.0.6" - } - }, - "core-js-compat": { - "version": "3.36.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.36.1.tgz", - "integrity": "sha512-Dk997v9ZCt3X/npqzyGdTlq6t7lDBhZwGvV94PKzDArjp7BTRm7WlDAXYd/OWdeFHO8OChQYRJNJvUCqCbrtKA==", - "requires": { - "browserslist": "^4.23.0" - } - }, - "core-util-is": { - "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 - }, - "cosmiconfig": { - "version": "8.3.6", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", - "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", - "requires": { - "import-fresh": "^3.3.0", - "js-yaml": "^4.1.0", - "parse-json": "^5.2.0", - "path-type": "^4.0.0" - } - }, - "create-require": { - "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 - }, - "crelt": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", - "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==" - }, - "cross-fetch": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", - "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", - "requires": { - "node-fetch": "^2.6.12" - } - }, - "cross-inspect": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/cross-inspect/-/cross-inspect-1.0.0.tgz", - "integrity": "sha512-4PFfn4b5ZN6FMNGSZlyb7wUhuN8wvj8t/VQHZdM4JsDcruGJ8L2kf9zao98QIrBPFCpdk27qst/AGTl7pL3ypQ==", - "dev": true, - "requires": { - "tslib": "^2.4.0" - } - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "crypt": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", - "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==" - }, - "css-select": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", - "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", - "requires": { - "boolbase": "^1.0.0", - "css-what": "^6.1.0", - "domhandler": "^5.0.2", - "domutils": "^3.0.1", - "nth-check": "^2.0.1" - } - }, - "css-tree": { - "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==", - "requires": { - "mdn-data": "2.0.30", - "source-map-js": "^1.0.1" - } - }, - "css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==" - }, - "cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==" - }, - "csso": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz", - "integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==", - "requires": { - "css-tree": "~2.2.0" - }, - "dependencies": { - "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==", - "requires": { - "mdn-data": "2.0.28", - "source-map-js": "^1.0.1" - } - }, - "mdn-data": { - "version": "2.0.28", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz", - "integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==" - } - } - }, - "cssstyle": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.0.1.tgz", - "integrity": "sha512-8ZYiJ3A/3OkDd093CBT/0UKDWry7ak4BdPTFP2+QEP7cmhouyq/Up709ASSj2cK02BbZiMgk7kYjZNS4QP5qrQ==", - "dev": true, - "requires": { - "rrweb-cssom": "^0.6.0" - } - }, - "csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "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==", - "dev": true, - "requires": { - "@cypress/request": "^3.0.0", - "@cypress/xvfb": "^1.2.4", - "@types/sinonjs__fake-timers": "8.1.1", - "@types/sizzle": "^2.3.2", - "arch": "^2.2.0", - "blob-util": "^2.0.2", - "bluebird": "^3.7.2", - "buffer": "^5.7.1", - "cachedir": "^2.3.0", - "chalk": "^4.1.0", - "check-more-types": "^2.24.0", - "cli-cursor": "^3.1.0", - "cli-table3": "~0.6.1", - "commander": "^6.2.1", - "common-tags": "^1.8.0", - "dayjs": "^1.10.4", - "debug": "^4.3.4", - "enquirer": "^2.3.6", - "eventemitter2": "6.4.7", - "execa": "4.1.0", - "executable": "^4.1.1", - "extract-zip": "2.0.1", - "figures": "^3.2.0", - "fs-extra": "^9.1.0", - "getos": "^3.2.1", - "is-ci": "^3.0.1", - "is-installed-globally": "~0.4.0", - "lazy-ass": "^1.6.0", - "listr2": "^3.8.3", - "lodash": "^4.17.21", - "log-symbols": "^4.0.0", - "minimist": "^1.2.8", - "ospath": "^1.2.2", - "pretty-bytes": "^5.6.0", - "process": "^0.11.10", - "proxy-from-env": "1.0.0", - "request-progress": "^3.0.0", - "semver": "^7.5.3", - "supports-color": "^8.1.1", - "tmp": "~0.2.1", - "untildify": "^4.0.0", - "yauzl": "^2.10.0" - }, - "dependencies": { - "listr2": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.14.0.tgz", - "integrity": "sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g==", - "dev": true, - "requires": { - "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.1", - "through": "^2.3.8", - "wrap-ansi": "^7.0.0" - } - }, - "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" - } - }, - "proxy-from-env": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", - "integrity": "sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==", - "dev": true - }, - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } - } - }, - "d3-array": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", - "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", - "requires": { - "internmap": "1 - 2" - } - }, - "d3-color": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", - "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==" - }, - "d3-ease": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", - "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==" - }, - "d3-format": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", - "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==" - }, - "d3-interpolate": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", - "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", - "requires": { - "d3-color": "1 - 3" - } - }, - "d3-path": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", - "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==" - }, - "d3-scale": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", - "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", - "requires": { - "d3-array": "2.10.0 - 3", - "d3-format": "1 - 3", - "d3-interpolate": "1.2.0 - 3", - "d3-time": "2.1.1 - 3", - "d3-time-format": "2 - 4" - } - }, - "d3-shape": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", - "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", - "requires": { - "d3-path": "^3.1.0" - } - }, - "d3-time": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", - "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", - "requires": { - "d3-array": "2 - 3" - } - }, - "d3-time-format": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", - "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", - "requires": { - "d3-time": "1 - 3" - } - }, - "d3-timer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", - "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==" - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, - "data-urls": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", - "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", - "dev": true, - "requires": { - "whatwg-mimetype": "^4.0.0", - "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", - "integrity": "sha512-8YnDaaf7N3k/q5HnTJVuzSyLETjoZjVmHc4AeKAzOvKHEFQKcn64OKBfzHYtE9zGjctNM7V9I0MfnUVLpi7M5g==", - "dev": true - }, - "date-fns": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", - "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==" - }, - "dayjs": { - "version": "1.11.10", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.10.tgz", - "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==", - "dev": true - }, - "debounce": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", - "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==", - "dev": true - }, - "debug": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", - "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", - "requires": { - "ms": "2.1.2" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "dev": true - }, - "decimal.js": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", - "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", - "dev": true - }, - "decimal.js-light": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", - "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==" - }, - "decode-named-character-reference": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", - "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", - "requires": { - "character-entities": "^2.0.0" - } - }, - "decode-uri-component": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.4.1.tgz", - "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==", - "dev": true, - "requires": { - "type-detect": "^4.0.0" - } - }, - "deep-equal": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", - "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==", - "dev": true, - "requires": { - "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.5", - "es-get-iterator": "^1.1.3", - "get-intrinsic": "^1.2.2", - "is-arguments": "^1.1.1", - "is-array-buffer": "^3.0.2", - "is-date-object": "^1.0.5", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "isarray": "^2.0.5", - "object-is": "^1.1.5", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.1", - "side-channel": "^1.0.4", - "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", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==" - }, - "defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "dev": true, - "requires": { - "clone": "^1.0.2" - } - }, - "define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dev": true, - "requires": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - } - }, - "define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dev": true, - "requires": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true - }, - "dependency-graph": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz", - "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==", - "dev": true - }, - "dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "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" - } - }, - "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, - "requires": { - "es-errors": "^1.3.0" - } - }, - "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" - } - }, - "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, - "requires": { - "hasown": "^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" - } - }, - "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" - } - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" - }, - "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 - }, - "eslint": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", - "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", - "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 - } - } - }, - "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 - } - } - }, - "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": {} - }, - "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, - "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" - } - } - } - }, - "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, - "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" - } - } - } - }, - "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, - "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 - } - } - }, - "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, - "requires": { - "@eslint-community/eslint-utils": "^4.1.2", - "@eslint-community/regexpp": "^4.6.0", - "eslint-compat-utils": "^0.5.0" - } - }, - "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, - "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" - } - } - } - }, - "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==", - "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 - } - } - }, - "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": {} - }, - "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" - } - } - } - }, - "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, - "requires": { - "eslint-rule-composer": "^0.3.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==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - } - }, - "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==", - "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, - "requires": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - } - }, - "esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - } - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "requires": { - "estraverse": "^5.2.0" - } - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - }, - "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==" - }, - "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==" - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" - }, - "eventemitter2": { - "version": "6.4.7", - "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.7.tgz", - "integrity": "sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==", - "dev": true - }, - "eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", - "dev": true - }, - "execa": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", - "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", - "dev": true, - "requires": { - "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" - } - }, - "executable": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz", - "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", - "dev": true, - "requires": { - "pify": "^2.2.0" - } - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dev": true, - "requires": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - }, - "dependencies": { - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "requires": { - "os-tmpdir": "~1.0.2" - } - } - } - }, - "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 - }, - "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, - "requires": { - "@types/yauzl": "^2.9.1", - "debug": "^4.1.1", - "get-stream": "^5.1.0", - "yauzl": "^2.10.0" - } - }, - "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 - }, - "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 - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "fast-equals": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.0.1.tgz", - "integrity": "sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ==" - }, - "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==", - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - } - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "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", - "integrity": "sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==", - "dev": true, - "requires": { - "fast-decode-uri-component": "^1.0.1" - } - }, - "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, - "requires": { - "punycode": "^1.3.2" - } - }, - "fastq": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.0.tgz", - "integrity": "sha512-zGygtijUMT7jnk3h26kUms3BkSDp4IfIKjmnqI2tvx6nuBfiF1UqOxbnLfzdv+apBy+53oaImsKtMw/xYbW+1w==", - "requires": { - "reusify": "^1.0.4" - } - }, - "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, - "requires": { - "bser": "2.1.1" - } - }, - "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, - "requires": { - "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" - }, - "dependencies": { - "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, - "requires": { - "node-fetch": "^2.6.12" - } - } - } - }, - "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 - }, - "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, - "requires": { - "pend": "~1.2.0" - } - }, - "figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - }, - "dependencies": { - "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 - } - } - }, - "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", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "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==" - }, - "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", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dev": true, - "requires": { - "is-callable": "^1.1.3" - } - }, - "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 - }, - "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, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, - "fraction.js": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", - "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==" - }, - "framer-motion": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-6.5.1.tgz", - "integrity": "sha512-o1BGqqposwi7cgDrtg0dNONhkmPsUFDaLcKXigzuTFC5x58mE8iyTazxSudFzmT6MEyJKfjjU8ItoMe3W+3fiw==", - "requires": { - "@emotion/is-prop-valid": "^0.8.2", - "@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" - } - }, - "framesync": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/framesync/-/framesync-6.0.1.tgz", - "integrity": "sha512-fUY88kXvGiIItgNC7wcTOl0SNRCVXMKSWW2Yzfmn7EKNc+MpCzcz9DhdHcdjbrtN3c6R4H5dTY2jiCpPdysEjA==", - "requires": { - "tslib": "^2.1.0" - } - }, - "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, - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, - "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 - }, - "fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "optional": true - }, - "function-bind": { - "version": "1.1.2", - "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", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true - }, - "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==" - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "get-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 - }, - "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 - }, - "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, - "requires": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - } - }, - "get-nonce": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", - "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==" - }, - "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, - "requires": { - "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", - "integrity": "sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==", - "dev": true, - "requires": { - "async": "^3.2.0" - } - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, - "gitdiff-parser": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/gitdiff-parser/-/gitdiff-parser-0.3.1.tgz", - "integrity": "sha512-YQJnY8aew65id8okGxKCksH3efDCJ9HzV7M9rsvd65habf39Pkh4cgYJ27AaoDMqo1X98pgNJhNMrm/kpV7UVQ==" - }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "requires": { - "is-glob": "^4.0.1" - } - }, - "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, - "requires": { - "ini": "2.0.0" - } - }, - "globals": { - "version": "11.12.0", - "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", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "requires": { - "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" - } - }, - "globrex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", - "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==" - }, - "gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, - "requires": { - "get-intrinsic": "^1.1.3" - } - }, - "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 - }, - "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", - "integrity": "sha512-k3p2k+7ZgARdLnqMDV192VL47cTmPNn02n5ullULnBE1nv1dtJfUve+AJxaU+kU8JcbwCxSxu3qlIxuu1N3mDQ==", - "requires": { - "@graphiql/react": "^0.20.4", - "@graphiql/toolkit": "^0.9.1", - "graphql-language-service": "^5.2.0", - "markdown-it": "^12.2.0" - } - }, - "graphql": { - "version": "16.8.1", - "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.8.1.tgz", - "integrity": "sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==" - }, - "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, - "requires": { - "@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" - }, - "dependencies": { - "minimatch": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.3.tgz", - "integrity": "sha512-lIUdtK5hdofgCTu3aT0sOaHsYR37viUuIc0rwnnDXImbwFRcumyLMeZaM0t0I/fgxS6s6JMfu0rLD1Wz9pv1ng==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - } - } - }, - "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==", - "requires": { - "nullthrows": "^1.0.0", - "vscode-languageserver-types": "^3.17.1" - } - }, - "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, - "requires": { - "@graphql-typed-document-node/core": "^3.2.0", - "cross-fetch": "^3.1.5" - }, - "dependencies": { - "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, - "requires": { - "node-fetch": "^2.6.12" - } - } - } - }, - "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==", - "requires": { - "tslib": "^2.1.0" - } - }, - "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, - "requires": {} - }, - "handlebars": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", - "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", - "requires": { - "minimist": "^1.2.5", - "neo-async": "^2.6.2", - "source-map": "^0.6.1", - "uglify-js": "^3.1.4", - "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", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "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, - "requires": { - "es-define-property": "^1.0.0" - } - }, - "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 - }, - "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 - }, - "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, - "requires": { - "has-symbols": "^1.0.3" - } - }, - "hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "requires": { - "function-bind": "^1.1.2" - } - }, - "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==", - "requires": { - "@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" - } - }, - "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==", - "requires": { - "@types/hast": "^3.0.0" - } - }, - "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, - "requires": { - "capital-case": "^1.0.4", - "tslib": "^2.0.3" - } - }, - "hey-listen": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/hey-listen/-/hey-listen-1.0.8.tgz", - "integrity": "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==" - }, - "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==", - "requires": { - "react-is": "^16.7.0" - } - }, - "html-encoding-sniffer": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", - "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", - "dev": true, - "requires": { - "whatwg-encoding": "^3.1.1" - } - }, - "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 - }, - "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==" - }, - "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, - "requires": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - } - }, - "http-signature": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz", - "integrity": "sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^2.0.2", - "sshpk": "^1.14.1" - } - }, - "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, - "requires": { - "agent-base": "^7.0.2", - "debug": "4" - } - }, - "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 - }, - "husky": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz", - "integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==", - "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==", - "requires": { - "@iconify/types": "^2.0.0" - } - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true - }, - "ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", - "dev": true - }, - "immutable": { - "version": "3.7.6", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.7.6.tgz", - "integrity": "sha512-AizQPcaofEtO11RZhPPHBOJRdo/20MKQF9mBLnVkBoyHi1/zXK8fzVdnEpSV9gxqtnh6Qomfp3F0xT5qP/vThw==", - "dev": true - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "dependencies": { - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" - } - } - }, - "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 - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true - }, - "indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true - }, - "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 - }, - "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, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "ini": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", - "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", - "dev": true - }, - "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==" - }, - "inquirer": { - "version": "8.2.6", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz", - "integrity": "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==", - "dev": true, - "requires": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.1", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.21", - "mute-stream": "0.0.8", - "ora": "^5.4.1", - "run-async": "^2.4.0", - "rxjs": "^7.5.5", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6", - "wrap-ansi": "^6.0.1" - } - }, - "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, - "requires": { - "es-errors": "^1.3.0", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" - } - }, - "internmap": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", - "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==" - }, - "invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "requires": { - "loose-envify": "^1.0.0" - } - }, - "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, - "requires": { - "is-relative": "^1.0.0", - "is-windows": "^1.0.1" - } - }, - "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==" - }, - "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==", - "requires": { - "is-alphabetical": "^2.0.0", - "is-decimal": "^2.0.0" - } - }, - "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, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "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, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1" - } - }, - "is-arrayish": { - "version": "0.2.1", - "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", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, - "requires": { - "has-bigints": "^1.0.1" - } - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-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, - "requires": { - "call-bind": "^1.0.2", - "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", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true - }, - "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, - "requires": { - "ci-info": "^3.2.0" - } - }, - "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==", - "requires": { - "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", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "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==" - }, - "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" - } - }, - "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", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "requires": { - "is-extglob": "^2.1.1" - } - }, - "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==" - }, - "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, - "requires": { - "global-dirs": "^3.0.0", - "is-path-inside": "^3.0.2" - } - }, - "is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "dev": true - }, - "is-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, - "requires": { - "tslib": "^2.0.3" - } - }, - "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 - }, - "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", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" - }, - "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, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "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 - }, - "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==" - }, - "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==", - "requires": { - "isobject": "^3.0.1" - } - }, - "is-potential-custom-element-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", - "dev": true - }, - "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==" - }, - "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, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "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, - "requires": { - "is-unc-path": "^1.0.0" - } - }, - "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 - }, - "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, - "requires": { - "call-bind": "^1.0.7" - } - }, - "is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true - }, - "is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "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, - "requires": { - "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", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "dev": true - }, - "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, - "requires": { - "unc-path-regex": "^0.1.2" - } - }, - "is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true - }, - "is-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, - "requires": { - "tslib": "^2.0.3" - } - }, - "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 - }, - "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", - "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - } - }, - "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 - }, - "isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==" - }, - "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, - "requires": {} - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", - "dev": true - }, - "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 - }, - "istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - } - }, - "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, - "requires": { - "@jridgewell/trace-mapping": "^0.3.23", - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0" - } - }, - "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, - "requires": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - } - }, - "iterall": { - "version": "1.3.0", - "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", - "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", - "requires": { - "@isaacs/cliui": "^8.0.2", - "@pkgjs/parseargs": "^0.11.0" - } - }, - "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==" - }, - "jose": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/jose/-/jose-5.2.3.tgz", - "integrity": "sha512-KUXdbctm1uHVL8BYhnyHkgp3zDX5KW8ZhAKVFEfUbU2P8Alpzjb+48hHvjOdQIyPshoblhzsuqOwEEAbtHVirA==", - "dev": true - }, - "jotai": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/jotai/-/jotai-2.7.2.tgz", - "integrity": "sha512-6Ft5kpNu8p93Ssf1Faoza3hYQZRIYp7rioK8MwTTFnbQKwUyZElwquPwl1h6U0uo9hC0jr+ghO3gcSjc6P35/Q==", - "requires": {} - }, - "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 - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "requires": { - "argparse": "^2.0.1" - } - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", - "dev": true - }, - "jsdom": { - "version": "24.0.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-24.0.0.tgz", - "integrity": "sha512-UDS2NayCvmXSXVP6mpTj+73JnNQadZlr9N68189xib2tx5Mls7swlTNao26IoHv46BZJFvXygyRtyXd1feAk1A==", - "dev": true, - "requires": { - "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" - } - }, - "jsesc": { - "version": "2.5.2", - "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", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" - }, - "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 - }, - "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", - "integrity": "sha512-SU/971Kt5qVQfJpyDveVhQ/vya+5hvrjClFOcr8c0Fq5aODJjMwutrOfCU+eCnVD5gpx1Q3fEqkyom77zH1iIg==", - "dev": true, - "requires": { - "call-bind": "^1.0.5", - "isarray": "^2.0.5", - "jsonify": "^0.0.1", - "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", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "dev": true - }, - "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==" - }, - "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, - "requires": { - "remedial": "^1.0.7", - "remove-trailing-spaces": "^1.0.6" - } - }, - "json5": { - "version": "2.2.3", - "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", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" - } - }, - "jsonify": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz", - "integrity": "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==", - "dev": true - }, - "jsprim": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz", - "integrity": "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==", - "dev": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "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", - "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==" - }, - "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==" - }, - "linkify-it": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", - "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", - "requires": { - "uc.micro": "^1.0.1" - } - }, - "lint-staged": { - "version": "15.2.10", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.10.tgz", - "integrity": "sha512-5dY5t743e1byO19P9I4b3x8HJwalIznL5E1FWYnU6OWw33KxNBSLAc6Cy7F2PsFEO8FKnLwjwm5hx7aMF0jzZg==", - "dev": true, - "requires": { - "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" - }, - "dependencies": { - "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, - "requires": { - "environment": "^1.0.0" - } - }, - "ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true - }, - "ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true - }, - "chalk": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz", - "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==", - "dev": true - }, - "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, - "requires": { - "restore-cursor": "^5.0.0" - } - }, - "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, - "requires": { - "slice-ansi": "^5.0.0", - "string-width": "^7.0.0" - } - }, - "commander": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", - "dev": true - }, - "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 - }, - "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, - "requires": { - "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" - } - }, - "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 - }, - "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 - }, - "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 - }, - "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 - }, - "lilconfig": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", - "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", - "dev": true - }, - "listr2": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.4.tgz", - "integrity": "sha512-opevsywziHd3zHCVQGAj8zu+Z3yHNkkoYhWIGnq54RrCVwLz0MozotJEDnKsIBLvkfLGN6BLOyAeRrYI0pKA4g==", - "dev": true, - "requires": { - "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" - } - }, - "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, - "requires": { - "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" - }, - "dependencies": { - "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, - "requires": { - "get-east-asian-width": "^1.0.0" - } - }, - "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, - "requires": { - "ansi-styles": "^6.2.1", - "is-fullwidth-code-point": "^5.0.0" - } - } - } - }, - "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 - }, - "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, - "requires": { - "path-key": "^4.0.0" - } - }, - "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, - "requires": { - "mimic-fn": "^4.0.0" - } - }, - "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 - }, - "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, - "requires": { - "onetime": "^7.0.0", - "signal-exit": "^4.1.0" - }, - "dependencies": { - "onetime": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", - "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", - "dev": true, - "requires": { - "mimic-function": "^5.0.0" - } - } - } - }, - "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 - }, - "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, - "requires": { - "ansi-styles": "^6.0.0", - "is-fullwidth-code-point": "^4.0.0" - } - }, - "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, - "requires": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - } - }, - "strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "requires": { - "ansi-regex": "^6.0.1" - } - }, - "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 - }, - "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, - "requires": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" - } - } + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/lint-staged/node_modules/yaml": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz", + "integrity": "sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" } }, - "listr2": { + "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, - "requires": { + "license": "MIT", + "dependencies": { "cli-truncate": "^2.1.0", "colorette": "^2.0.16", "log-update": "^4.0.0", @@ -29100,339 +12418,345 @@ "through": "^2.3.8", "wrap-ansi": "^7.0.0" }, - "dependencies": { - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "enquirer": ">= 2.3.0 < 3" + }, + "peerDependenciesMeta": { + "enquirer": { + "optional": true } } }, - "local-pkg": { + "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, + "license": "MIT", + "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, - "requires": { + "license": "MIT", + "dependencies": { "mlly": "^1.4.2", "pkg-types": "^1.0.3" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" } }, - "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==", + "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, - "requires": { - "p-locate": "^5.0.0" + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" } }, - "lodash": { + "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" }, - "lodash.debounce": { + "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==" + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "license": "MIT" }, - "lodash.isequal": { + "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 - }, - "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 + "dev": true, + "license": "MIT" }, - "lodash.once": { + "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 + "dev": true, + "license": "MIT" }, - "lodash.sortby": { + "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 + "dev": true, + "license": "MIT" }, - "log-symbols": { + "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, - "requires": { + "license": "MIT", + "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "log-update": { + "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, - "requires": { + "license": "MIT", + "dependencies": { "ansi-escapes": "^4.3.0", "cli-cursor": "^3.1.0", "slice-ansi": "^4.0.0", "wrap-ansi": "^6.2.0" }, - "dependencies": { - "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, - "requires": { - "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/sponsors/sindresorhus" } }, - "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==", + "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, - "requires": { - "chalk": "^1.1.3", - "loglevel": "^1.4.1" - }, + "license": "MIT", "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 - } + "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" } }, - "longest-streak": { + "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==" + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } }, - "loose-envify": { + "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==", - "requires": { + "license": "MIT", + "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" } }, - "loupe": { + "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, - "requires": { + "license": "MIT", + "dependencies": { "get-func-name": "^2.0.1" } }, - "lower-case": { + "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==", - "requires": { + "license": "MIT", + "dependencies": { "tslib": "^2.0.3" } }, - "lower-case-first": { + "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, - "requires": { + "license": "MIT", + "dependencies": { "tslib": "^2.0.3" } }, - "lru-cache": { + "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==", - "requires": { + "license": "ISC", + "dependencies": { "yallist": "^3.0.2" } }, - "lz-string": { + "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 + "dev": true, + "license": "MIT", + "bin": { + "lz-string": "bin/bin.js" + } }, - "magic-string": { - "version": "0.30.8", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", - "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==", + "node_modules/magic-string": { + "version": "0.30.12", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.12.tgz", + "integrity": "sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==", "dev": true, - "requires": { - "@jridgewell/sourcemap-codec": "^1.4.15" + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" } }, - "magicast": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.3.tgz", - "integrity": "sha512-ZbrP1Qxnpoes8sz47AM0z08U+jW6TyRgZzcWy3Ma3vDhJttwMwAFDMMQFobwdBxByBD46JYmxRzeF7w2+wJEuw==", + "node_modules/magicast": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", + "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==", "dev": true, - "requires": { - "@babel/parser": "^7.23.6", - "@babel/types": "^7.23.6", - "source-map-js": "^1.0.2" + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.25.4", + "@babel/types": "^7.25.4", + "source-map-js": "^1.2.0" } }, - "make-dir": { + "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, - "requires": { + "license": "MIT", + "dependencies": { "semver": "^7.5.3" }, - "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 - } + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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/make-dir/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } }, - "map-cache": { + "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 + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } }, - "markdown-it": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", - "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", - "requires": { + "node_modules/markdown-it": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "license": "MIT", + "dependencies": { "argparse": "^2.0.1", - "entities": "~2.1.0", - "linkify-it": "^3.0.1", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" }, - "dependencies": { - "entities": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", - "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==" - } + "bin": { + "markdown-it": "bin/markdown-it.mjs" } }, - "markdown-table": { + "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==" + "integrity": "sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } }, - "mdast-util-find-and-replace": { + "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==", - "requires": { + "license": "MIT", + "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" }, - "dependencies": { - "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==" - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "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==", - "requires": { + "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==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.1.tgz", + "integrity": "sha512-aJEUyzZ6TzlsX2s5B4Of7lN7EQtAxvtradMMglCQDyaTFgse6CmtmdJ15ElnVRlCg1vpNyVtbem0PWzlNieZsA==", + "license": "MIT", + "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "decode-named-character-reference": "^1.0.0", @@ -29445,13 +12769,18 @@ "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" } }, - "mdast-util-gfm": { + "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==", - "requires": { + "license": "MIT", + "dependencies": { "mdast-util-from-markdown": "^2.0.0", "mdast-util-gfm-autolink-literal": "^2.0.0", "mdast-util-gfm-footnote": "^2.0.0", @@ -29459,83 +12788,118 @@ "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" } }, - "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==", - "requires": { + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", + "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", + "license": "MIT", + "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" } }, - "mdast-util-gfm-footnote": { + "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==", - "requires": { + "license": "MIT", + "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" } }, - "mdast-util-gfm-strikethrough": { + "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==", - "requires": { + "license": "MIT", + "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" } }, - "mdast-util-gfm-table": { + "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==", - "requires": { + "license": "MIT", + "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" } }, - "mdast-util-gfm-task-list-item": { + "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==", - "requires": { + "license": "MIT", + "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" } }, - "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==", - "requires": { + "node_modules/mdast-util-mdx-expression": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", + "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", + "license": "MIT", + "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" } }, - "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==", - "requires": { + "node_modules/mdast-util-mdx-jsx": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.1.3.tgz", + "integrity": "sha512-bfOjvNt+1AcbPLTFMFWY149nJz0OjmewJs3LQQ5pIyVGxP4CdOqNVJL6kTaM5c68p8q82Xv3nCyFfUnuEcH3UQ==", + "license": "MIT", + "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", @@ -29546,38 +12910,52 @@ "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" } }, - "mdast-util-mdxjs-esm": { + "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==", - "requires": { + "license": "MIT", + "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" } }, - "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==", - "requires": { + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", + "license": "MIT", + "dependencies": { "@types/mdast": "^4.0.0", "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "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==", - "requires": { + "node_modules/mdast-util-to-hast": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz", + "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==", + "license": "MIT", + "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@ungap/structured-clone": "^1.0.0", @@ -29587,13 +12965,18 @@ "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "mdast-util-to-markdown": { + "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==", - "requires": { + "license": "MIT", + "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "longest-streak": "^3.0.0", @@ -29602,48 +12985,86 @@ "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" } }, - "mdast-util-to-string": { + "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==", - "requires": { + "license": "MIT", + "dependencies": { "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "mdn-data": { + "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==" + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", + "license": "CC0-1.0" }, - "mdurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==" + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "license": "MIT" }, - "merge-stream": { + "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 + "dev": true, + "license": "MIT" }, - "merge2": { + "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "license": "MIT", + "engines": { + "node": ">= 8" + } }, - "meros": { + "node_modules/meros": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/meros/-/meros-1.3.0.tgz", "integrity": "sha512-2BNGOimxEz5hmjUG2FwoxCt5HN7BXdaWyFqEwxPTrJzVdABtrL4TiHTcsWSFAxPQ/tOnEaQEJh3qWq71QRMY+w==", - "requires": {} + "license": "MIT", + "engines": { + "node": ">=13" + }, + "peerDependencies": { + "@types/node": ">=13" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } }, - "micromark": { + "node_modules/micromark": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.0.tgz", "integrity": "sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==", - "requires": { + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { "@types/debug": "^4.0.0", "debug": "^4.0.0", "decode-named-character-reference": "^1.0.0", @@ -29663,11 +13084,22 @@ "micromark-util-types": "^2.0.0" } }, - "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==", - "requires": { + "node_modules/micromark-core-commonmark": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.1.tgz", + "integrity": "sha512-CUQyKr1e///ZODyD1U3xit6zXwy1a8q2a1S1HKtIlmgvurrEpaw/Y9y6KSIbF8P59cn/NjzHyO+Q2fAyYLQrAA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-destination": "^2.0.0", @@ -29686,11 +13118,12 @@ "micromark-util-types": "^2.0.0" } }, - "micromark-extension-gfm": { + "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==", - "requires": { + "license": "MIT", + "dependencies": { "micromark-extension-gfm-autolink-literal": "^2.0.0", "micromark-extension-gfm-footnote": "^2.0.0", "micromark-extension-gfm-strikethrough": "^2.0.0", @@ -29699,24 +13132,34 @@ "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" } }, - "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==", - "requires": { + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", + "license": "MIT", + "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" } }, - "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==", - "requires": { + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", + "license": "MIT", + "dependencies": { "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", @@ -29725,630 +13168,937 @@ "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" } }, - "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==", - "requires": { + "node_modules/micromark-extension-gfm-strikethrough": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", + "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", + "license": "MIT", + "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" } }, - "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==", - "requires": { + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.0.tgz", + "integrity": "sha512-Ub2ncQv+fwD70/l4ou27b4YzfNaCJOvyX4HxXU15m7mpYY+rjuWzsLIPZHJL253Z643RpbcP1oeIJlQ/SKW67g==", + "license": "MIT", + "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" } }, - "micromark-extension-gfm-tagfilter": { + "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==", - "requires": { + "license": "MIT", + "dependencies": { "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "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==", - "requires": { + "node_modules/micromark-extension-gfm-task-list-item": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", + "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", + "license": "MIT", + "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" } }, - "micromark-factory-destination": { + "node_modules/micromark-factory-destination": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.0.tgz", "integrity": "sha512-j9DGrQLm/Uhl2tCzcbLhy5kXsgkHUrjJHg4fFAeoMRwJmJerT9aw4FEhIbZStWN8A3qMwOp1uzHr4UL8AInxtA==", - "requires": { + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, - "micromark-factory-label": { + "node_modules/micromark-factory-label": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.0.tgz", "integrity": "sha512-RR3i96ohZGde//4WSe/dJsxOX6vxIg9TimLAS3i4EhBAFx8Sm5SmqVfR8E87DPSR31nEAjZfbt91OMZWcNgdZw==", - "requires": { + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { "devlop": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, - "micromark-factory-space": { + "node_modules/micromark-factory-space": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.0.tgz", "integrity": "sha512-TKr+LIDX2pkBJXFLzpyPyljzYK3MtmllMUMODTQJIUfDGncESaqB90db9IAUcz4AZAJFdd8U9zOp9ty1458rxg==", - "requires": { + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-types": "^2.0.0" } }, - "micromark-factory-title": { + "node_modules/micromark-factory-title": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.0.tgz", "integrity": "sha512-jY8CSxmpWLOxS+t8W+FG3Xigc0RDQA9bKMY/EwILvsesiRniiVMejYTE4wumNc2f4UbAa4WsHqe3J1QS1sli+A==", - "requires": { + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, - "micromark-factory-whitespace": { + "node_modules/micromark-factory-whitespace": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.0.tgz", "integrity": "sha512-28kbwaBjc5yAI1XadbdPYHX/eDnqaUFVikLwrO7FDnKG7lpgxnvk/XGRhX/PN0mOZ+dBSZ+LgunHS+6tYQAzhA==", - "requires": { + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, - "micromark-util-character": { + "node_modules/micromark-util-character": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.0.tgz", "integrity": "sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==", - "requires": { + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, - "micromark-util-chunked": { + "node_modules/micromark-util-chunked": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.0.tgz", "integrity": "sha512-anK8SWmNphkXdaKgz5hJvGa7l00qmcaUQoMYsBwDlSKFKjc6gjGXPDw3FNL3Nbwq5L8gE+RCbGqTw49FK5Qyvg==", - "requires": { + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { "micromark-util-symbol": "^2.0.0" } }, - "micromark-util-classify-character": { + "node_modules/micromark-util-classify-character": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.0.tgz", "integrity": "sha512-S0ze2R9GH+fu41FA7pbSqNWObo/kzwf8rN/+IGlW/4tC6oACOs8B++bh+i9bVyNnwCcuksbFwsBme5OCKXCwIw==", - "requires": { + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, - "micromark-util-combine-extensions": { + "node_modules/micromark-util-combine-extensions": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.0.tgz", "integrity": "sha512-vZZio48k7ON0fVS3CUgFatWHoKbbLTK/rT7pzpJ4Bjp5JjkZeasRfrS9wsBdDJK2cJLHMckXZdzPSSr1B8a4oQ==", - "requires": { + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { "micromark-util-chunked": "^2.0.0", "micromark-util-types": "^2.0.0" } }, - "micromark-util-decode-numeric-character-reference": { + "node_modules/micromark-util-decode-numeric-character-reference": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.1.tgz", "integrity": "sha512-bmkNc7z8Wn6kgjZmVHOX3SowGmVdhYS7yBpMnuMnPzDq/6xwVA604DuOXMZTO1lvq01g+Adfa0pE2UKGlxL1XQ==", - "requires": { + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { "micromark-util-symbol": "^2.0.0" } }, - "micromark-util-decode-string": { + "node_modules/micromark-util-decode-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.0.tgz", "integrity": "sha512-r4Sc6leeUTn3P6gk20aFMj2ntPwn6qpDZqWvYmAG6NgvFTIlj4WtrAudLi65qYoaGdXYViXYw2pkmn7QnIFasA==", - "requires": { + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { "decode-named-character-reference": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, - "micromark-util-encode": { + "node_modules/micromark-util-encode": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.0.tgz", - "integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==" + "integrity": "sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" }, - "micromark-util-html-tag-name": { + "node_modules/micromark-util-html-tag-name": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.0.tgz", - "integrity": "sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==" + "integrity": "sha512-xNn4Pqkj2puRhKdKTm8t1YHC/BAjx6CEwRFXntTaRf/x16aqka6ouVoutm+QdkISTlT7e2zU7U4ZdlDLJd2Mcw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" }, - "micromark-util-normalize-identifier": { + "node_modules/micromark-util-normalize-identifier": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.0.tgz", "integrity": "sha512-2xhYT0sfo85FMrUPtHcPo2rrp1lwbDEEzpx7jiH2xXJLqBuy4H0GgXk5ToU8IEwoROtXuL8ND0ttVa4rNqYK3w==", - "requires": { + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { "micromark-util-symbol": "^2.0.0" } }, - "micromark-util-resolve-all": { + "node_modules/micromark-util-resolve-all": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.0.tgz", "integrity": "sha512-6KU6qO7DZ7GJkaCgwBNtplXCvGkJToU86ybBAUdavvgsCiG8lSSvYxr9MhwmQ+udpzywHsl4RpGJsYWG1pDOcA==", - "requires": { + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { "micromark-util-types": "^2.0.0" } }, - "micromark-util-sanitize-uri": { + "node_modules/micromark-util-sanitize-uri": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.0.tgz", "integrity": "sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==", - "requires": { + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, - "micromark-util-subtokenize": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.0.tgz", - "integrity": "sha512-vc93L1t+gpR3p8jxeVdaYlbV2jTYteDje19rNSS/H5dlhxUYll5Fy6vJ2cDwP8RnsXi818yGty1ayP55y3W6fg==", - "requires": { + "node_modules/micromark-util-subtokenize": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.0.1.tgz", + "integrity": "sha512-jZNtiFl/1aY73yS3UGQkutD0UbhTt68qnRpw2Pifmz5wV9h8gOVsN70v+Lq/f1rKaU/W8pxRe8y8Q9FX1AOe1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, - "micromark-util-symbol": { + "node_modules/micromark-util-symbol": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.0.tgz", - "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==" + "integrity": "sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" }, - "micromark-util-types": { + "node_modules/micromark-util-types": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.0.tgz", - "integrity": "sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==" + "integrity": "sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" }, - "micromatch": { + "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "requires": { + "license": "MIT", + "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" } }, - "mime-db": { + "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } }, - "mime-types": { + "node_modules/mime-types": { "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" } }, - "mimic-fn": { + "node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } }, - "mimic-function": { + "node_modules/mimic-function": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "mini-svg-data-uri": { + "node_modules/mini-svg-data-uri": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz", - "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==" + "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==", + "license": "MIT", + "bin": { + "mini-svg-data-uri": "cli.js" + } }, - "minimatch": { + "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "requires": { + "license": "ISC", + "dependencies": { "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, - "minimist": { + "node_modules/minimist": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "minipass": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", - "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==" + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } }, - "mlly": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.6.1.tgz", - "integrity": "sha512-vLgaHvaeunuOXHSmEbZ9izxPx3USsk8KCQ8iC+aTlp5sKRSoZvwhHh5L9VbKSaVC6sJDqbyohIS76E2VmHIPAA==", + "node_modules/mlly": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.2.tgz", + "integrity": "sha512-tN3dvVHYVz4DhSXinXIk7u9syPYaJvio118uomkovAtWBT+RdbP6Lfh/5Lvo519YMmwBafwlh20IPTXIStscpA==", "dev": true, - "requires": { - "acorn": "^8.11.3", + "license": "MIT", + "dependencies": { + "acorn": "^8.12.1", "pathe": "^1.1.2", - "pkg-types": "^1.0.3", - "ufo": "^1.3.2" + "pkg-types": "^1.2.0", + "ufo": "^1.5.4" } }, - "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", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "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==", + "license": "MIT" }, - "mute-stream": { + "node_modules/mute-stream": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "dev": true + "dev": true, + "license": "ISC" }, - "mz": { + "node_modules/mz": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "requires": { + "license": "MIT", + "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, - "nanoid": { + "node_modules/nanoid": { "version": "3.3.7", "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 + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } }, - "neo-async": { + "node_modules/neo-async": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "license": "MIT" }, - "no-case": { + "node_modules/no-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", - "requires": { + "license": "MIT", + "dependencies": { "lower-case": "^2.0.2", "tslib": "^2.0.3" } }, - "node-addon-api": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.0.tgz", - "integrity": "sha512-mNcltoe1R8o7STTegSOHdnJNN7s5EUvhoS7ShnTHDyOSd+8H+UdWODq6qSv67PjC8Zc5JRT8+oLAMCr0SIXw7g==", - "dev": true, - "optional": true, - "peer": true - }, - "node-fetch": { + "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "requires": { + "license": "MIT", + "dependencies": { "whatwg-url": "^5.0.0" }, - "dependencies": { - "tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "requires": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true } } }, - "node-int64": { + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true + "dev": true, + "license": "MIT" }, - "node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==" + "node_modules/node-releases": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "license": "MIT" }, - "normalize-path": { + "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } }, - "normalize-range": { + "node_modules/normalize-range": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==" + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } }, - "npm-run-path": { + "node_modules/npm-run-path": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "nth-check": { + "node_modules/nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "requires": { + "license": "BSD-2-Clause", + "dependencies": { "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" } }, - "nullthrows": { + "node_modules/nullthrows": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz", - "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==" + "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==", + "license": "MIT" }, - "nwsapi": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz", - "integrity": "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==", - "dev": true + "node_modules/nwsapi": { + "version": "2.2.13", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.13.tgz", + "integrity": "sha512-cTGB9ptp9dY9A5VbMSe7fQBcl/tt22Vcqdq8+eN93rblOuE0aCFu4aZ2vMwct/2t+lFnosm8RkQW1I0Omb1UtQ==", + "dev": true, + "license": "MIT" }, - "object-assign": { + "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } }, - "object-hash": { + "node_modules/object-hash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==" + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "license": "MIT", + "engines": { + "node": ">= 6" + } }, - "object-inspect": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", - "dev": true + "node_modules/object-inspect": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", + "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "object-is": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", - "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", + "node_modules/object-is": { + "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" + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "object-keys": { + "node_modules/object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } }, - "object.assign": { + "node_modules/object.assign": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "call-bind": "^1.0.5", "define-properties": "^1.2.1", "has-symbols": "^1.0.3", "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "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": { + "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, - "requires": { + "license": "ISC", + "dependencies": { "wrappy": "1" } }, - "onetime": { + "node_modules/onetime": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "openapi-typescript": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/openapi-typescript/-/openapi-typescript-7.0.2.tgz", - "integrity": "sha512-BBrYEf0YdW31Ernd07cD/qHoalSuiiUQvy+rHvU/1Iz9WbcFpRsIXrnfEnrEuiGTRuKCG6cDQCrxNK/rbwQRLg==", + "node_modules/openapi-typescript": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/openapi-typescript/-/openapi-typescript-7.4.1.tgz", + "integrity": "sha512-HrRoWveViADezHCNgQqZmPKmQ74q7nuH/yg9ursFucZaYQNUqsX38fE/V2sKBHVM+pws4tAHpuh/ext2UJ/AoQ==", "dev": true, - "requires": { - "@redocly/openapi-core": "^1.16.0", + "license": "MIT", + "dependencies": { + "@redocly/openapi-core": "^1.25.3", "ansi-colors": "^4.1.3", + "change-case": "^5.4.4", "parse-json": "^8.1.0", "supports-color": "^9.4.0", "yargs-parser": "^21.1.1" }, + "bin": { + "openapi-typescript": "bin/cli.js" + }, + "peerDependencies": { + "typescript": "^5.x" + } + }, + "node_modules/openapi-typescript/node_modules/change-case": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/change-case/-/change-case-5.4.4.tgz", + "integrity": "sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/openapi-typescript/node_modules/parse-json": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.1.0.tgz", + "integrity": "sha512-rum1bPifK5SSar35Z6EKZuYPJx85pkNaFrxBK3mwdfSJ1/WKbYrjoW/zTPSjRRamfmVX1ACBIdFAO0VRErW/EA==", + "dev": true, + "license": "MIT", "dependencies": { - "parse-json": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.1.0.tgz", - "integrity": "sha512-rum1bPifK5SSar35Z6EKZuYPJx85pkNaFrxBK3mwdfSJ1/WKbYrjoW/zTPSjRRamfmVX1ACBIdFAO0VRErW/EA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.22.13", - "index-to-position": "^0.1.2", - "type-fest": "^4.7.1" - } - }, - "supports-color": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.4.0.tgz", - "integrity": "sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==", - "dev": true - }, - "type-fest": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.21.0.tgz", - "integrity": "sha512-ADn2w7hVPcK6w1I0uWnM//y1rLXZhzB9mr0a3OirzclKF1Wp6VzevUmzz/NRAWunOT6E8HrnpGY7xOfc6K57fA==", - "dev": true - }, - "yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true - } + "@babel/code-frame": "^7.22.13", + "index-to-position": "^0.1.2", + "type-fest": "^4.7.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/openapi-typescript/node_modules/supports-color": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.4.0.tgz", + "integrity": "sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/openapi-typescript/node_modules/type-fest": { + "version": "4.26.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.26.1.tgz", + "integrity": "sha512-yOGpmOAL7CkKe/91I5O3gPICmJNLJ1G4zFYVAsRHg7M64biSnPtRj0WNQt++bRkjYOqjWXrhnUw1utzmVErAdg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "optimism": { + "node_modules/optimism": { "version": "0.18.0", "resolved": "https://registry.npmjs.org/optimism/-/optimism-0.18.0.tgz", "integrity": "sha512-tGn8+REwLRNFnb9WmcY5IfpOqeX2kpaYJ1s6Ae3mn12AeydLkR3j+jSCmVQFoXqU8D41PAJ1RG1rCRNWmNZVmQ==", - "requires": { + "license": "MIT", + "dependencies": { "@wry/caches": "^1.0.0", "@wry/context": "^0.7.0", "@wry/trie": "^0.4.3", "tslib": "^2.3.0" - }, - "dependencies": { - "@wry/trie": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@wry/trie/-/trie-0.4.3.tgz", - "integrity": "sha512-I6bHwH0fSf6RqQcnnXLJKhkSXG45MFral3GxPaY4uAl0LYDZM+YDVDAiU9bYwjTuysy1S0IeecWtmq1SZA3M1w==", - "requires": { - "tslib": "^2.3.0" - } - } } }, - "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" + "node_modules/optimism/node_modules/@wry/trie": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@wry/trie/-/trie-0.4.3.tgz", + "integrity": "sha512-I6bHwH0fSf6RqQcnnXLJKhkSXG45MFral3GxPaY4uAl0LYDZM+YDVDAiU9bYwjTuysy1S0IeecWtmq1SZA3M1w==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": ">=8" } }, - "ora": { + "node_modules/ora": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "bl": "^4.1.0", "chalk": "^4.1.0", "cli-cursor": "^3.1.0", @@ -30358,76 +14108,137 @@ "log-symbols": "^4.1.0", "strip-ansi": "^6.0.0", "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "os-tmpdir": { + "node_modules/os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } }, - "ospath": { + "node_modules/ospath": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz", "integrity": "sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==", - "dev": true + "dev": true, + "license": "MIT" }, - "p-limit": { + "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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==", + "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, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-locate/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, - "requires": { - "p-limit": "^3.0.2" + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "p-map": { + "node_modules/p-map": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "p-try": { + "node_modules/p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" }, - "param-case": { + "node_modules/param-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "dot-case": "^3.0.4", "tslib": "^2.0.3" } }, - "parent-module": { + "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "requires": { + "license": "MIT", + "dependencies": { "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" } }, - "parse-entities": { + "node_modules/parse-entities": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.1.tgz", "integrity": "sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w==", - "requires": { + "license": "MIT", + "dependencies": { "@types/unist": "^2.0.0", "character-entities": "^2.0.0", "character-entities-legacy": "^3.0.0", @@ -30437,657 +14248,715 @@ "is-decimal": "^2.0.0", "is-hexadecimal": "^2.0.0" }, - "dependencies": { - "@types/unist": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.10.tgz", - "integrity": "sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==" - } + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "parse-filepath": { + "node_modules/parse-entities/node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "license": "MIT" + }, + "node_modules/parse-filepath": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", "integrity": "sha512-FwdRXKCohSVeXqwtYonZTXtbGJKrn+HNyWDYVcp5yuJlesTwNH4rsmRZ+GrKAPJ5bLpRxESMeS+Rl0VCHRvB2Q==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "is-absolute": "^1.0.0", "map-cache": "^0.2.0", "path-root": "^0.1.1" + }, + "engines": { + "node": ">=0.8" } }, - "parse-json": { + "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "requires": { + "license": "MIT", + "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "parse5": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", - "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "node_modules/parse5": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.0.tgz", + "integrity": "sha512-ZkDsAOcxsUMZ4Lz5fVciOehNcJ+Gb8gTzcA4yl3wnc273BAybYWrQ+Ks/OjCjSEpjvQkDSeZbybK9qj2VHHdGA==", "dev": true, - "requires": { - "entities": "^4.4.0" + "license": "MIT", + "dependencies": { + "entities": "^4.5.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" } }, - "pascal-case": { + "node_modules/pascal-case": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3" } }, - "path-case": { + "node_modules/path-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/path-case/-/path-case-3.0.4.tgz", "integrity": "sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "dot-case": "^3.0.4", "tslib": "^2.0.3" } }, - "path-exists": { + "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "path-is-absolute": { + "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } }, - "path-key": { + "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "path-parse": { + "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" }, - "path-root": { + "node_modules/path-root": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", "integrity": "sha512-QLcPegTHF11axjfojBIoDygmS2E3Lf+8+jI6wOVmNVenrKSo3mFdSGiIgdSHenczw3wPtlVMQaFVwGmM7BJdtg==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "path-root-regex": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "path-root-regex": { + "node_modules/path-root-regex": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", "integrity": "sha512-4GlJ6rZDhQZFE0DPVKh0e9jmZ5egZfxTkp7bcRDuPlJXbAwhxcl2dINPUAsjLdejqaLsCeg8axcLjIbvBjN4pQ==", - "dev": true - }, - "path-scurry": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", - "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", - "requires": { - "lru-cache": "^9.1.1 || ^10.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, - "dependencies": { - "lru-cache": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", - "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==" - } + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "path-type": { + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "pathe": { + "node_modules/pathe": { "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" }, - "pathval": { + "node_modules/pathval": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } }, - "pend": { + "node_modules/pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", - "dev": true + "dev": true, + "license": "MIT" }, - "performance-now": { + "node_modules/performance-now": { "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" }, - "picocolors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", - "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==" + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" }, - "picomatch": { + "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } }, - "pidtree": { + "node_modules/pidtree": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", - "dev": true + "dev": true, + "license": "MIT", + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } }, - "pify": { + "node_modules/pify": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==" + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } }, - "pirates": { + "node_modules/pirates": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==" + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "license": "MIT", + "engines": { + "node": ">= 6" + } }, - "pkg-types": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.0.3.tgz", - "integrity": "sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==", + "node_modules/pkg-types": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.2.1.tgz", + "integrity": "sha512-sQoqa8alT3nHjGuTjuKgOnvjo4cljkufdtLMnO2LBP/wRwuDlo1tkaEdMxCRhyGRPacv/ztlZgDPm2b7FAmEvw==", "dev": true, - "requires": { - "jsonc-parser": "^3.2.0", - "mlly": "^1.2.0", - "pathe": "^1.1.0" + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.2", + "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==", + "node_modules/playwright": { + "version": "1.48.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.48.1.tgz", + "integrity": "sha512-j8CiHW/V6HxmbntOfyB4+T/uk08tBy6ph0MpBXwuoofkSnLmlfdYNNkFTYD6ofzzlSqLA1fwH4vwvVFvJgLN0w==", "dev": true, - "requires": { - "fsevents": "2.3.2", - "playwright-core": "1.47.0" - }, + "license": "Apache-2.0", "dependencies": { - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - } + "playwright-core": "1.48.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" } }, - "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==", - "dev": true + "node_modules/playwright-core": { + "version": "1.48.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.48.1.tgz", + "integrity": "sha512-Yw/t4VAFX/bBr1OzwCuOMZkY1Cnb4z/doAFSwf4huqAGWmf9eMNjmK7NiOljCdLmxeRYcGPPmcDgU0zOlzP0YA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } }, - "pluralize": { + "node_modules/pluralize": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } }, - "popmotion": { + "node_modules/popmotion": { "version": "11.0.3", "resolved": "https://registry.npmjs.org/popmotion/-/popmotion-11.0.3.tgz", "integrity": "sha512-Y55FLdj3UxkR7Vl3s7Qr4e9m0onSnP8W7d/xQLsoJM40vs6UKHFdygs6SWryasTZYqugMjm3BepCF4CWXDiHgA==", - "requires": { + "license": "MIT", + "dependencies": { "framesync": "6.0.1", "hey-listen": "^1.0.8", "style-value-types": "5.0.0", "tslib": "^2.1.0" } }, - "possible-typed-array-names": { + "node_modules/possible-typed-array-names": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } }, - "postcss": { + "node_modules/postcss": { "version": "8.4.47", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", - "requires": { + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { "nanoid": "^3.3.7", "picocolors": "^1.1.0", "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" } }, - "postcss-import": { + "node_modules/postcss-import": { "version": "15.1.0", "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", - "requires": { + "license": "MIT", + "dependencies": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" } }, - "postcss-js": { + "node_modules/postcss-js": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", - "requires": { + "license": "MIT", + "dependencies": { "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" } }, - "postcss-load-config": { + "node_modules/postcss-load-config": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", - "requires": { + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { "lilconfig": "^3.0.0", "yaml": "^2.3.4" }, - "dependencies": { - "lilconfig": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.0.0.tgz", - "integrity": "sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g==" + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true } } }, - "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" + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" } }, - "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": { + "node_modules/postcss-selector-parser": { + "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" + }, + "engines": { + "node": ">=4" } }, - "postcss-value-parser": { + "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==" - }, - "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 - } - } + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "license": "MIT" }, - "pretty-bytes": { + "node_modules/pretty-bytes": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "pretty-format": { + "node_modules/pretty-format": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", "react-is": "^17.0.1" }, - "dependencies": { - "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 - }, - "react-is": { - "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 - } + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "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==", + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, - "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 - } + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "prismjs": { + "node_modules/pretty-format/node_modules/react-is": { + "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, + "license": "MIT" + }, + "node_modules/prismjs": { "version": "1.29.0", "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", - "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==" + "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } }, - "process": { + "node_modules/process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } }, - "promise": { + "node_modules/promise": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "asap": "~2.0.3" } }, - "prop-types": { + "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "requires": { + "license": "MIT", + "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, - "property-information": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.4.1.tgz", - "integrity": "sha512-OHYtXfu5aI2sS2LWFSN5rgJjrQ4pCy8i1jubJLe2QvMF8JJ++HXTUIVWFLfXJoaOfvYYjk2SN8J2wFUWIGXT4w==" + "node_modules/property-information": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", + "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/proxy-from-env": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", + "integrity": "sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==", + "dev": true, + "license": "MIT" }, - "psl": { + "node_modules/psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", - "dev": true + "dev": true, + "license": "MIT" }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "node_modules/pump": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==", - "dev": true - }, - "pvtsutils": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.5.tgz", - "integrity": "sha512-ARvb14YB9Nm2Xi6nBq1ZX6dAM0FsJnuk+31aUp4TrcZEdKUlSqOqsxJHUPJDNE3qiIp+iUPEIeR6Je/tgV7zsA==", + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, - "requires": { - "tslib": "^2.6.1" + "license": "MIT", + "engines": { + "node": ">=6" } }, - "pvutils": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.1.3.tgz", - "integrity": "sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ==", - "dev": true + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "license": "MIT", + "engines": { + "node": ">=6" + } }, - "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==", + "node_modules/qs": { + "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" + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "query-string": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-9.0.0.tgz", - "integrity": "sha512-4EWwcRGsO2H+yzq6ddHcVqkCQ2EFUSfDMEjF8ryp8ReymyZhIuaFRGLomeOQLkrzacMHoyky2HW0Qe30UbzkKw==", - "requires": { + "node_modules/query-string": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-9.1.1.tgz", + "integrity": "sha512-MWkCOVIcJP9QSKU52Ngow6bsAWAPlPK2MludXvcrS2bGZSl+T1qX9MZvRIkqUIkGLJquMJHWfsT6eRqUpp4aWg==", + "license": "MIT", + "dependencies": { "decode-uri-component": "^0.4.1", "filter-obj": "^5.1.0", "split-on-first": "^3.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "querystringify": { + "node_modules/querystringify": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "dev": true + "dev": true, + "license": "MIT" }, - "queue-microtask": { + "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" }, - "ramda": { + "node_modules/ramda": { "version": "0.29.1", "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.29.1.tgz", - "integrity": "sha512-OfxIeWzd4xdUNxlWhgFazxsA/nl3mS4/jGZI5n00uWOoSSFRhC1b6gl6xvmzUamgmqELraWp0J/qqVlXYPDPyA==" + "integrity": "sha512-OfxIeWzd4xdUNxlWhgFazxsA/nl3mS4/jGZI5n00uWOoSSFRhC1b6gl6xvmzUamgmqELraWp0J/qqVlXYPDPyA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ramda" + } }, - "react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", - "requires": { + "node_modules/react": { + "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" + }, + "engines": { + "node": ">=0.10.0" } }, - "react-accessible-treeview": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/react-accessible-treeview/-/react-accessible-treeview-2.8.3.tgz", - "integrity": "sha512-taDTIYZ6p96/zIhJBUKvyGTXcInudatP/9fwKG0BW+VRf1PmU5hOT2FkDovDKzSwj2VSOj1PRx+E6ojhOA+2xA==", - "requires": {} + "node_modules/react-accessible-treeview": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/react-accessible-treeview/-/react-accessible-treeview-2.9.1.tgz", + "integrity": "sha512-UlmeFJtlh1SryN8kHLf4ICLlF4HeIpiacWwHh+7AzhmOmOs9vObXAjJWTiLpxP342TQ1QbCdSGq086Y8oD7NSw==", + "license": "MIT", + "peerDependencies": { + "classnames": "^2.2.6", + "prop-types": "^15.7.2", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } }, - "react-datepicker": { + "node_modules/react-datepicker": { "version": "4.25.0", "resolved": "https://registry.npmjs.org/react-datepicker/-/react-datepicker-4.25.0.tgz", "integrity": "sha512-zB7CSi44SJ0sqo8hUQ3BF1saE/knn7u25qEMTO1CQGofY1VAKahO8k9drZtp0cfW1DMfoYLR3uSY1/uMvbEzbg==", - "requires": { + "license": "MIT", + "dependencies": { "@popperjs/core": "^2.11.8", "classnames": "^2.2.6", "date-fns": "^2.30.0", @@ -31095,74 +14964,116 @@ "react-onclickoutside": "^6.13.0", "react-popper": "^2.3.0" }, + "peerDependencies": { + "react": "^16.9.0 || ^17 || ^18", + "react-dom": "^16.9.0 || ^17 || ^18" + } + }, + "node_modules/react-datepicker/node_modules/date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "license": "MIT", "dependencies": { - "date-fns": { - "version": "2.30.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", - "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", - "requires": { - "@babel/runtime": "^7.21.0" - } - } + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" } }, - "react-diff-view": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/react-diff-view/-/react-diff-view-3.2.0.tgz", - "integrity": "sha512-p58XoqMxgt71ujpiDQTs9Za3nqTawt1E4bTzKsYSqr8I8br6cjQj1b66HxGnV8Yrc6MD6iQPqS1aZiFoGqEw+g==", - "requires": { + "node_modules/react-diff-view": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/react-diff-view/-/react-diff-view-3.2.1.tgz", + "integrity": "sha512-JoDahgiyeReeH9W9lrI3Z4c4esbd/HNAOdThj6Pce/ZAukFBmXSbZ4Qv8ayo7yow+fTpRNfqtQ9gX5nArEi08w==", + "license": "MIT", + "dependencies": { "classnames": "^2.3.2", "diff-match-patch": "^1.0.5", "gitdiff-parser": "^0.3.1", "lodash": "^4.17.21", "shallow-equal": "^3.1.0", "warning": "^4.0.3" + }, + "peerDependencies": { + "react": ">=16.14.0" } }, - "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==", - "requires": { + "node_modules/react-dom": { + "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.3.1" } }, - "react-error-boundary": { - "version": "4.0.13", - "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-4.0.13.tgz", - "integrity": "sha512-b6PwbdSv8XeOSYvjt8LpgpKrZ0yGdtZokYwkwV2wlcZbxgopHX/hgPl5VgpnoVOWd868n1hktM8Qm4b+02MiLQ==", - "requires": { + "node_modules/react-error-boundary": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-4.1.1.tgz", + "integrity": "sha512-EOAEsbVm2EQD8zPS4m24SiaR/506RPC3CjMcjJ5JWKECsctyLsDTKxB26Hvl7jcz7KweSOkBYAcY/hmMpMn2jA==", + "license": "MIT", + "dependencies": { "@babel/runtime": "^7.12.5" + }, + "engines": { + "pnpm": "=9" + }, + "peerDependencies": { + "react": ">=16.13.1" } }, - "react-fast-compare": { + "node_modules/react-fast-compare": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", - "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==" + "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==", + "license": "MIT" }, - "react-hook-form": { - "version": "7.51.5", - "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.51.5.tgz", - "integrity": "sha512-J2ILT5gWx1XUIJRETiA7M19iXHlG74+6O3KApzvqB/w8S5NQR7AbU8HVZrMALdmDgWpRPYiZJl0zx8Z4L2mP6Q==", - "requires": {} + "node_modules/react-hook-form": { + "version": "7.53.0", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.53.0.tgz", + "integrity": "sha512-M1n3HhqCww6S2hxLxciEXy2oISPnAzxY7gvwVPrtlczTM/1dDadXgUxDpHMrMTblDOcm/AXtXxHwZ3jpg1mqKQ==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18 || ^19" + } }, - "react-is": { + "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" }, - "react-loading": { + "node_modules/react-loading": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/react-loading/-/react-loading-2.0.3.tgz", "integrity": "sha512-Vdqy79zq+bpeWJqC+xjltUjuGApyoItPgL0vgVfcJHhqwU7bAMKzysfGW/ADu6i0z0JiOCRJjo+IkFNkRNbA3A==", - "requires": {} + "license": "MIT", + "peerDependencies": { + "prop-types": "^15.6.0", + "react": ">=0.14.0" + } }, - "react-markdown": { + "node_modules/react-markdown": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-9.0.1.tgz", "integrity": "sha512-186Gw/vF1uRkydbsOIkcGXw7aHq0sZOCRFFjGrr7b9+nVZg4UfA4enXCaxm4fUzecU38sWfrNDitGhshuU7rdg==", - "requires": { + "license": "MIT", + "dependencies": { "@types/hast": "^3.0.0", "devlop": "^1.0.0", "hast-util-to-jsx-runtime": "^2.0.0", @@ -31173,789 +15084,1107 @@ "unified": "^11.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=18", + "react": ">=18" } }, - "react-onclickoutside": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/react-onclickoutside/-/react-onclickoutside-6.13.0.tgz", - "integrity": "sha512-ty8So6tcUpIb+ZE+1HAhbLROvAIJYyJe/1vRrrcmW+jLsaM+/powDRqxzo6hSh9CuRZGSL1Q8mvcF5WRD93a0A==", - "requires": {} + "node_modules/react-onclickoutside": { + "version": "6.13.1", + "resolved": "https://registry.npmjs.org/react-onclickoutside/-/react-onclickoutside-6.13.1.tgz", + "integrity": "sha512-LdrrxK/Yh9zbBQdFbMTXPp3dTSN9B+9YJQucdDu3JNKRrbdU+H+/TVONJoWtOwy4II8Sqf1y/DTI6w/vGPYW0w==", + "license": "MIT", + "funding": { + "type": "individual", + "url": "https://github.com/Pomax/react-onclickoutside/blob/master/FUNDING.md" + }, + "peerDependencies": { + "react": "^15.5.x || ^16.x || ^17.x || ^18.x", + "react-dom": "^15.5.x || ^16.x || ^17.x || ^18.x" + } }, - "react-paginate": { + "node_modules/react-paginate": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/react-paginate/-/react-paginate-8.2.0.tgz", "integrity": "sha512-sJCz1PW+9PNIjUSn919nlcRVuleN2YPoFBOvL+6TPgrH/3lwphqiSOgdrLafLdyLDxsgK+oSgviqacF4hxsDIw==", - "requires": { + "license": "MIT", + "dependencies": { "prop-types": "^15" + }, + "peerDependencies": { + "react": "^16 || ^17 || ^18" } }, - "react-popper": { + "node_modules/react-popper": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.3.0.tgz", "integrity": "sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==", - "requires": { + "license": "MIT", + "dependencies": { "react-fast-compare": "^3.0.1", "warning": "^4.0.2" + }, + "peerDependencies": { + "@popperjs/core": "^2.0.0", + "react": "^16.8.0 || ^17 || ^18", + "react-dom": "^16.8.0 || ^17 || ^18" } }, - "react-refresh": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", - "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==" + "node_modules/react-refresh": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", + "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } }, - "react-remove-scroll": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz", - "integrity": "sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==", - "requires": { - "react-remove-scroll-bar": "^2.3.3", + "node_modules/react-remove-scroll": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.6.0.tgz", + "integrity": "sha512-I2U4JVEsQenxDAKaVa3VZ/JeJZe0/2DxPWL8Tj8yLKctQJQiZM52pn/GWFpSp8dftjM3pSAHVJZscAnC/y+ySQ==", + "license": "MIT", + "dependencies": { + "react-remove-scroll-bar": "^2.3.6", "react-style-singleton": "^2.2.1", "tslib": "^2.1.0", "use-callback-ref": "^1.3.0", "use-sidecar": "^1.1.2" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "react-remove-scroll-bar": { + "node_modules/react-remove-scroll-bar": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.6.tgz", "integrity": "sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==", - "requires": { + "license": "MIT", + "dependencies": { "react-style-singleton": "^2.2.1", "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-resizable-panels": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/react-resizable-panels/-/react-resizable-panels-2.1.5.tgz", + "integrity": "sha512-JMSe18rYupmx+dzYcdfWYZ93ZdxqQmLum3xWDVSUMI0UVwl9bB9gUaFmPbxYoO4G+m5sqgdXQCYQxnOysytfnw==", + "license": "MIT", + "peerDependencies": { + "react": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, - "react-router": { - "version": "6.22.3", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.3.tgz", - "integrity": "sha512-dr2eb3Mj5zK2YISHK++foM9w4eBnO23eKnZEDs7c880P6oKbrjz/Svg9+nxqtHQK+oMW4OtjZca0RqPglXxguQ==", - "requires": { - "@remix-run/router": "1.15.3" + "node_modules/react-router": { + "version": "6.27.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.27.0.tgz", + "integrity": "sha512-YA+HGZXz4jaAkVoYBE98VQl+nVzI+cVI2Oj/06F5ZM+0u3TgedN9Y9kmMRo2mnkSK2nCpNQn0DVob4HCsY/WLw==", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.20.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" } }, - "react-router-dom": { - "version": "6.22.3", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.3.tgz", - "integrity": "sha512-7ZILI7HjcE+p31oQvwbokjk6OA/bnFxrhJ19n82Ex9Ph8fNAq+Hm/7KchpMGlTgWhUxRHMMCut+vEtNpWpowKw==", - "requires": { - "@remix-run/router": "1.15.3", - "react-router": "6.22.3" + "node_modules/react-router-dom": { + "version": "6.27.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.27.0.tgz", + "integrity": "sha512-+bvtFWMC0DgAFrfKXKG9Fc+BcXWRUO1aJIihbB79xaeq0v5UzfvnM5houGUm1Y461WVRcgAQ+Clh5rdb1eCx4g==", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.20.0", + "react-router": "6.27.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" } }, - "react-shallow-renderer": { + "node_modules/react-shallow-renderer": { "version": "16.15.0", "resolved": "https://registry.npmjs.org/react-shallow-renderer/-/react-shallow-renderer-16.15.0.tgz", "integrity": "sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "object-assign": "^4.1.1", "react-is": "^16.12.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependencies": { + "react": "^16.0.0 || ^17.0.0 || ^18.0.0" } }, - "react-simple-code-editor": { + "node_modules/react-simple-code-editor": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/react-simple-code-editor/-/react-simple-code-editor-0.13.1.tgz", "integrity": "sha512-XYeVwRZwgyKtjNIYcAEgg2FaQcCZwhbarnkJIV20U2wkCU9q/CPFBo8nRXrK4GXUz3AvbqZFsZRrpUTkqqEYyQ==", - "requires": {} + "license": "MIT", + "peerDependencies": { + "react": "*", + "react-dom": "*" + } }, - "react-smooth": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.0.tgz", - "integrity": "sha512-2NMXOBY1uVUQx1jBeENGA497HK20y6CPGYL1ZnJLeoQ8rrc3UfmOM82sRxtzpcoCkUMy4CS0RGylfuVhuFjBgg==", - "requires": { + "node_modules/react-smooth": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.1.tgz", + "integrity": "sha512-OE4hm7XqR0jNOq3Qmk9mFLyd6p2+j6bvbPJ7qlB7+oo0eNcL2l7WQzG6MBnT3EXY6xzkLMUBec3AfewJdA0J8w==", + "license": "MIT", + "dependencies": { "fast-equals": "^5.0.1", "prop-types": "^15.8.1", "react-transition-group": "^4.4.5" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, - "react-style-singleton": { + "node_modules/react-style-singleton": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz", "integrity": "sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==", - "requires": { + "license": "MIT", + "dependencies": { "get-nonce": "^1.0.0", "invariant": "^2.2.4", "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "react-test-renderer": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-18.2.0.tgz", - "integrity": "sha512-JWD+aQ0lh2gvh4NM3bBM42Kx+XybOxCpgYK7F8ugAlpaTSnWsX+39Z4XkOykGZAHrjwwTZT3x3KxswVWxHPUqA==", + "node_modules/react-test-renderer": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-18.3.1.tgz", + "integrity": "sha512-KkAgygexHUkQqtvvx/otwxtuFu5cVjfzTCtjXLH9boS19/Nbtg84zS7wIQn39G8IlrhThBpQsMKkq5ZHZIYFXA==", "dev": true, - "requires": { - "react-is": "^18.2.0", + "license": "MIT", + "dependencies": { + "react-is": "^18.3.1", "react-shallow-renderer": "^16.15.0", - "scheduler": "^0.23.0" + "scheduler": "^0.23.2" }, - "dependencies": { - "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 - } + "peerDependencies": { + "react": "^18.3.1" } }, - "react-toastify": { + "node_modules/react-test-renderer/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, + "license": "MIT" + }, + "node_modules/react-toastify": { "version": "9.1.3", "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-9.1.3.tgz", "integrity": "sha512-fPfb8ghtn/XMxw3LkxQBk3IyagNpF/LIKjOBflbexr2AWxAH1MJgvnESwEwBn9liLFXgTKWgBSdZpw9m4OTHTg==", - "requires": { + "license": "MIT", + "dependencies": { "clsx": "^1.1.1" }, - "dependencies": { - "clsx": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", - "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==" - } + "peerDependencies": { + "react": ">=16", + "react-dom": ">=16" + } + }, + "node_modules/react-toastify/node_modules/clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "license": "MIT", + "engines": { + "node": ">=6" } }, - "react-transition-group": { + "node_modules/react-transition-group": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", - "requires": { + "license": "BSD-3-Clause", + "dependencies": { "@babel/runtime": "^7.5.5", "dom-helpers": "^5.0.1", "loose-envify": "^1.4.0", "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" } }, - "read-cache": { + "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", - "requires": { + "license": "MIT", + "dependencies": { "pify": "^2.3.0" } }, - "readable-stream": { + "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" } }, - "readdirp": { + "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "requires": { + "license": "MIT", + "dependencies": { "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" } }, - "recharts": { - "version": "2.12.3", - "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.12.3.tgz", - "integrity": "sha512-vE/F7wTlokf5mtCqVDJlVKelCjliLSJ+DJxj79XlMREm7gpV7ljwbrwE3CfeaoDlOaLX+6iwHaVRn9587YkwIg==", - "requires": { + "node_modules/recharts": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.13.0.tgz", + "integrity": "sha512-sbfxjWQ+oLWSZEWmvbq/DFVdeRLqqA6d0CDjKx2PkxVVdoXo16jvENCE+u/x7HxOO+/fwx//nYRwb8p8X6s/lQ==", + "license": "MIT", + "dependencies": { "clsx": "^2.0.0", "eventemitter3": "^4.0.1", "lodash": "^4.17.21", - "react-is": "^16.10.2", + "react-is": "^18.3.1", "react-smooth": "^4.0.0", "recharts-scale": "^0.4.4", "tiny-invariant": "^1.3.1", "victory-vendor": "^36.6.8" }, - "dependencies": { - "eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" - } + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": "^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" } }, - "recharts-scale": { + "node_modules/recharts-scale": { "version": "0.4.5", "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz", "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", - "requires": { + "license": "MIT", + "dependencies": { "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" - } + "node_modules/recharts/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==", + "license": "MIT" }, - "regenerate": { + "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==" + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "license": "MIT" }, - "regenerate-unicode-properties": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz", - "integrity": "sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==", - "requires": { + "node_modules/regenerate-unicode-properties": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", + "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", + "license": "MIT", + "dependencies": { "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" } }, - "regenerator-runtime": { + "node_modules/regenerator-runtime": { "version": "0.14.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "license": "MIT" }, - "regenerator-transform": { + "node_modules/regenerator-transform": { "version": "0.15.2", "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", - "requires": { + "license": "MIT", + "dependencies": { "@babel/runtime": "^7.8.4" } }, - "regexp.prototype.flags": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", - "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", + "node_modules/regexp.prototype.flags": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.3.tgz", + "integrity": "sha512-vqlC04+RQoFalODCbCumG2xIOvapzVMHwsyIGM/SIE8fRhFFsXeH8/QQ+s0T0kDAhKc4k30s73/0ydkHQz6HlQ==", "dev": true, - "requires": { - "call-bind": "^1.0.6", + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", "define-properties": "^1.2.1", "es-errors": "^1.3.0", - "set-function-name": "^2.0.1" + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "regexpu-core": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", - "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", - "requires": { - "@babel/regjsgen": "^0.8.0", + "node_modules/regexpu-core": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.1.1.tgz", + "integrity": "sha512-k67Nb9jvwJcJmVpw0jPttR1/zVfnKf8Km0IPatrU/zJ5XeG3+Slx0xLXs9HByJSzXzrlz5EDvN6yLNMDc2qdnw==", + "license": "MIT", + "dependencies": { "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.1.0", - "regjsparser": "^0.9.1", + "regenerate-unicode-properties": "^10.2.0", + "regjsgen": "^0.8.0", + "regjsparser": "^0.11.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.1.0" + }, + "engines": { + "node": ">=4" } }, - "regjsparser": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", - "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", - "requires": { - "jsesc": "~0.5.0" - }, + "node_modules/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", + "license": "MIT" + }, + "node_modules/regjsparser": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.11.1.tgz", + "integrity": "sha512-1DHODs4B8p/mQHU9kr+jv8+wIC9mtG4eBHxWxIq5mhjE3D5oORhCc6deRKzTjs9DcfRFmj9BHSDguZklqCGFWQ==", + "license": "BSD-2-Clause", "dependencies": { - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==" - } + "jsesc": "~3.0.2" + }, + "bin": { + "regjsparser": "bin/parser" } }, - "rehackt": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/rehackt/-/rehackt-0.0.6.tgz", - "integrity": "sha512-l3WEzkt4ntlEc/IB3/mF6SRgNHA6zfQR7BlGOgBTOmx7IJJXojDASav+NsgXHFjHn+6RmwqsGPFgZpabWpeOdw==", - "requires": {} + "node_modules/rehackt": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/rehackt/-/rehackt-0.1.0.tgz", + "integrity": "sha512-7kRDOuLHB87D/JESKxQoRwv4DzbIdwkAGQ7p6QKGdVlY1IZheUnVhlk/4UZlNUVxdAXpyxikE3URsG067ybVzw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "*" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + } + } }, - "relay-runtime": { + "node_modules/relay-runtime": { "version": "12.0.0", "resolved": "https://registry.npmjs.org/relay-runtime/-/relay-runtime-12.0.0.tgz", "integrity": "sha512-QU6JKr1tMsry22DXNy9Whsq5rmvwr3LSZiiWV/9+DFpuTWvp+WFhobWMc8TC4OjKFfNhEZy7mOiqUAn5atQtug==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "@babel/runtime": "^7.0.0", "fbjs": "^3.0.0", "invariant": "^2.2.4" } }, - "remark-gfm": { + "node_modules/remark-gfm": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.0.tgz", "integrity": "sha512-U92vJgBPkbw4Zfu/IiW2oTZLSL3Zpv+uI7My2eq8JxKgqraFdU8YUGicEJCEgSbeaG+QDFqIcwwfMTOEelPxuA==", - "requires": { + "license": "MIT", + "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-gfm": "^3.0.0", "micromark-extension-gfm": "^3.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "remark-parse": { + "node_modules/remark-parse": { "version": "11.0.0", "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", - "requires": { + "license": "MIT", + "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "micromark-util-types": "^2.0.0", "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "remark-rehype": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.0.tgz", - "integrity": "sha512-z3tJrAs2kIs1AqIIy6pzHmAHlF1hWQ+OdY4/hv+Wxe35EhyLKcajL33iUEn3ScxtFox9nUvRufR/Zre8Q08H/g==", - "requires": { + "node_modules/remark-rehype": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.1.tgz", + "integrity": "sha512-g/osARvjkBXb6Wo0XvAeXQohVta8i84ACbenPpoSsxTOQH/Ae0/RGP4WZgnMH5pMLpsj4FG7OHmcIcXxpza8eQ==", + "license": "MIT", + "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "mdast-util-to-hast": "^13.0.0", "unified": "^11.0.0", "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "remark-stringify": { + "node_modules/remark-stringify": { "version": "11.0.0", "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", - "requires": { + "license": "MIT", + "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-to-markdown": "^2.0.0", "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "remeda": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/remeda/-/remeda-2.6.0.tgz", - "integrity": "sha512-uwq9c2s91Mry5YQiPGWtW9vF2DgTIVLzCIZZYz6Rcv8hsgWFQjavOaP24m9ZNF2mur+eq0x2EqRQlPBaLZuYrA==", - "requires": { - "type-fest": "^4.21.0" - }, + "node_modules/remeda": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/remeda/-/remeda-2.15.0.tgz", + "integrity": "sha512-Q0Xdg6z3pDKMGVCAI9wGZ+Yz0y0HOzaxxY3wc9gdjJyxqH93LywDUJ4PJ/zhweicYgcB4HbbOliR8HsIdse6mA==", + "license": "MIT", "dependencies": { - "type-fest": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.22.1.tgz", - "integrity": "sha512-9tHNEa0Ov81YOopiVkcCJVz5TM6AEQ+CHHjFIktqPnE3NV0AHIkx+gh9tiCl58m/66wWxkOC9eltpa75J4lQPA==" - } + "type-fest": "^4.26.1" + } + }, + "node_modules/remeda/node_modules/type-fest": { + "version": "4.26.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.26.1.tgz", + "integrity": "sha512-yOGpmOAL7CkKe/91I5O3gPICmJNLJ1G4zFYVAsRHg7M64biSnPtRj0WNQt++bRkjYOqjWXrhnUw1utzmVErAdg==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "remedial": { + "node_modules/remedial": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/remedial/-/remedial-1.0.8.tgz", "integrity": "sha512-/62tYiOe6DzS5BqVsNpH/nkGlX45C/Sp6V+NtiN6JQNS1Viay7cWkazmRkrQrdFj2eshDe96SIQNIoMxqhzBOg==", - "dev": true + "dev": true, + "license": "(MIT OR Apache-2.0)", + "engines": { + "node": "*" + } }, - "remove-trailing-separator": { + "node_modules/remove-trailing-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", - "dev": true + "dev": true, + "license": "ISC" }, - "remove-trailing-spaces": { + "node_modules/remove-trailing-spaces": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/remove-trailing-spaces/-/remove-trailing-spaces-1.0.8.tgz", "integrity": "sha512-O3vsMYfWighyFbTd8hk8VaSj9UAGENxAtX+//ugIst2RMk5e03h6RoIS+0ylsFxY1gvmPuAY/PO4It+gPEeySA==", - "dev": true + "dev": true, + "license": "MIT" }, - "request-progress": { + "node_modules/request-progress": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz", "integrity": "sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "throttleit": "^1.0.0" } }, - "require-directory": { + "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } }, - "require-from-string": { + "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } }, - "require-main-filename": { + "node_modules/require-main-filename": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", "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 + "dev": true, + "license": "ISC" }, - "requires-port": { + "node_modules/requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true + "dev": true, + "license": "MIT" }, - "resolve": { + "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "requires": { + "license": "MIT", + "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "resolve-from": { + "node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - }, - "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 + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "response-iterator": { + "node_modules/response-iterator": { "version": "0.2.6", "resolved": "https://registry.npmjs.org/response-iterator/-/response-iterator-0.2.6.tgz", - "integrity": "sha512-pVzEEzrsg23Sh053rmDUvLSkGXluZio0qu8VT6ukrYuvtjVfCbDZH9d6PGXb8HZfzdNZt8feXv/jvUzlhRgLnw==" + "integrity": "sha512-pVzEEzrsg23Sh053rmDUvLSkGXluZio0qu8VT6ukrYuvtjVfCbDZH9d6PGXb8HZfzdNZt8feXv/jvUzlhRgLnw==", + "license": "MIT", + "engines": { + "node": ">=0.8" + } }, - "restore-cursor": { + "node_modules/restore-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" } }, - "reusify": { + "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } }, - "rfdc": { + "node_modules/rfdc": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", - "dev": true + "dev": true, + "license": "MIT" }, - "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", - "integrity": "sha512-vD8HJ5raRcWOyymsR6Z3o6+RzfEPCnVLMFJ6vRslO1jt4LO6dUo5Qnpg7y4RkZFM2DMe3WUirkI5c16onjrc6A==", - "requires": { - "@rollup/rollup-android-arm-eabi": "4.22.4", - "@rollup/rollup-android-arm64": "4.22.4", - "@rollup/rollup-darwin-arm64": "4.22.4", - "@rollup/rollup-darwin-x64": "4.22.4", - "@rollup/rollup-linux-arm-gnueabihf": "4.22.4", - "@rollup/rollup-linux-arm-musleabihf": "4.22.4", - "@rollup/rollup-linux-arm64-gnu": "4.22.4", - "@rollup/rollup-linux-arm64-musl": "4.22.4", - "@rollup/rollup-linux-powerpc64le-gnu": "4.22.4", - "@rollup/rollup-linux-riscv64-gnu": "4.22.4", - "@rollup/rollup-linux-s390x-gnu": "4.22.4", - "@rollup/rollup-linux-x64-gnu": "4.22.4", - "@rollup/rollup-linux-x64-musl": "4.22.4", - "@rollup/rollup-win32-arm64-msvc": "4.22.4", - "@rollup/rollup-win32-ia32-msvc": "4.22.4", - "@rollup/rollup-win32-x64-msvc": "4.22.4", - "@types/estree": "1.0.5", + "node_modules/rollup": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.0.tgz", + "integrity": "sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==", + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.6" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.24.0", + "@rollup/rollup-android-arm64": "4.24.0", + "@rollup/rollup-darwin-arm64": "4.24.0", + "@rollup/rollup-darwin-x64": "4.24.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.24.0", + "@rollup/rollup-linux-arm-musleabihf": "4.24.0", + "@rollup/rollup-linux-arm64-gnu": "4.24.0", + "@rollup/rollup-linux-arm64-musl": "4.24.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.24.0", + "@rollup/rollup-linux-riscv64-gnu": "4.24.0", + "@rollup/rollup-linux-s390x-gnu": "4.24.0", + "@rollup/rollup-linux-x64-gnu": "4.24.0", + "@rollup/rollup-linux-x64-musl": "4.24.0", + "@rollup/rollup-win32-arm64-msvc": "4.24.0", + "@rollup/rollup-win32-ia32-msvc": "4.24.0", + "@rollup/rollup-win32-x64-msvc": "4.24.0", "fsevents": "~2.3.2" } }, - "rrweb-cssom": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz", - "integrity": "sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==", - "dev": true + "node_modules/rrweb-cssom": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz", + "integrity": "sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==", + "dev": true, + "license": "MIT" }, - "run-async": { + "node_modules/run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } }, - "run-parallel": { + "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "requires": { + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { "queue-microtask": "^1.2.2" } }, - "rxjs": { + "node_modules/rxjs": { "version": "7.8.1", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", "dev": true, - "requires": { + "license": "Apache-2.0", + "dependencies": { "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": { + "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true - }, - "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" - } + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" }, - "safer-buffer": { + "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true + "dev": true, + "license": "MIT" }, - "saxes": { + "node_modules/saxes": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", "dev": true, - "requires": { + "license": "ISC", + "dependencies": { "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" } }, - "scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", - "requires": { + "node_modules/scheduler": { + "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" } }, - "scuid": { + "node_modules/scuid": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/scuid/-/scuid-1.1.0.tgz", "integrity": "sha512-MuCAyrGZcTLfQoH2XoBlQ8C6bzwN88XT/0slOGz0pn8+gIP85BOAfYa44ZXQUTOwRwPU0QvgU+V+OSajl/59Xg==", - "dev": true + "dev": true, + "license": "MIT" }, - "semver": { + "node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } }, - "sentence-case": { + "node_modules/sentence-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-3.0.4.tgz", "integrity": "sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3", "upper-case-first": "^2.0.2" } }, - "serialize-query-params": { + "node_modules/serialize-query-params": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/serialize-query-params/-/serialize-query-params-2.0.2.tgz", - "integrity": "sha512-1chMo1dST4pFA9RDXAtF0Rbjaut4is7bzFbI1Z26IuMub68pNCILku85aYmeFhvnY//BXUPUhoRMjYcsT93J/Q==" + "integrity": "sha512-1chMo1dST4pFA9RDXAtF0Rbjaut4is7bzFbI1Z26IuMub68pNCILku85aYmeFhvnY//BXUPUhoRMjYcsT93J/Q==", + "license": "ISC" }, - "set-blocking": { + "node_modules/set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "dev": true + "dev": true, + "license": "ISC" }, - "set-function-length": { + "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" } }, - "set-function-name": { + "node_modules/set-function-name": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" } }, - "set-value": { + "node_modules/set-value": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/set-value/-/set-value-4.1.0.tgz", "integrity": "sha512-zTEg4HL0RwVrqcWs3ztF+x1vkxfm0lP+MQQFPiMJTKVceBwEV0A569Ou8l9IYQG8jOZdMVI1hGsc0tmeD2o/Lw==", - "requires": { + "funding": [ + "https://github.com/sponsors/jonschlinkert", + "https://paypal.me/jonathanschlinkert", + "https://jonschlinkert.dev/sponsor" + ], + "license": "MIT", + "dependencies": { "is-plain-object": "^2.0.4", "is-primitive": "^3.0.1" + }, + "engines": { + "node": ">=11.0" } }, - "setimmediate": { + "node_modules/setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", - "dev": true + "dev": true, + "license": "MIT" }, - "sha1": { + "node_modules/sha1": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/sha1/-/sha1-1.1.1.tgz", "integrity": "sha512-dZBS6OrMjtgVkopB1Gmo4RQCDKiZsqcpAQpkV/aaj+FCrCg8r4I4qMkDPQjBgLIxlmu9k4nUbWq6ohXahOneYA==", - "requires": { + "license": "BSD-3-Clause", + "dependencies": { "charenc": ">= 0.0.1", "crypt": ">= 0.0.1" + }, + "engines": { + "node": "*" } }, - "shallow-equal": { + "node_modules/shallow-equal": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-3.1.0.tgz", - "integrity": "sha512-pfVOw8QZIXpMbhBWvzBISicvToTiM5WBF1EeAUZDDSb5Dt29yl4AYbyywbJFSEsRUMr7gJaxqCdr4L3tQf9wVg==" + "integrity": "sha512-pfVOw8QZIXpMbhBWvzBISicvToTiM5WBF1EeAUZDDSb5Dt29yl4AYbyywbJFSEsRUMr7gJaxqCdr4L3tQf9wVg==", + "license": "MIT" }, - "shebang-command": { + "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "requires": { + "license": "MIT", + "dependencies": { "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "shebang-regex": { + "node_modules/shebang-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "shell-quote": { + "node_modules/shell-quote": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", - "dev": true + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "side-channel": { + "node_modules/side-channel": { "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==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "call-bind": "^1.0.7", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.4", "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "siginfo": { + "node_modules/siginfo": { "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" }, - "signal-exit": { + "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true + "dev": true, + "license": "ISC" }, - "signedsource": { + "node_modules/signedsource": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/signedsource/-/signedsource-1.0.0.tgz", "integrity": "sha512-6+eerH9fEnNmi/hyM1DXcRK3pWdoMQtlkQ+ns0ntzunjKqp5i3sKCc80ym8Fib3iaYhdJUOPdhlJWj1tvge2Ww==", - "dev": true + "dev": true, + "license": "BSD-3-Clause" }, - "slash": { + "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "slice-ansi": { + "node_modules/slice-ansi": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "ansi-styles": "^4.0.0", "astral-regex": "^2.0.0", "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "snake-case": { + "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==", - "requires": { + "license": "MIT", + "dependencies": { "dot-case": "^3.0.4", "tslib": "^2.0.3" } }, - "source-map": { + "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==" + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } }, - "source-map-js": { + "node_modules/source-map-js": { "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==" + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } }, - "space-separated-tokens": { + "node_modules/space-separated-tokens": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", - "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==" + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } }, - "split-on-first": { + "node_modules/split-on-first": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-3.0.0.tgz", - "integrity": "sha512-qxQJTx2ryR0Dw0ITYyekNQWpz6f8dGd7vffGNflQQ3Iqj9NJ6qiZ7ELpZsJ/QBhIVAiDfXdag3+Gp8RvWa62AA==" + "integrity": "sha512-qxQJTx2ryR0Dw0ITYyekNQWpz6f8dGd7vffGNflQQ3Iqj9NJ6qiZ7ELpZsJ/QBhIVAiDfXdag3+Gp8RvWa62AA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "sponge-case": { + "node_modules/sponge-case": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/sponge-case/-/sponge-case-1.0.1.tgz", "integrity": "sha512-dblb9Et4DAtiZ5YSUZHLl4XhH4uK80GhAZrVXdN4O2P4gQ40Wa5UIOPUHlA/nFd2PLblBZWUioLMMAVrgpoYcA==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "tslib": "^2.0.3" } }, - "sshpk": { + "node_modules/sshpk": { "version": "1.18.0", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", "bcrypt-pbkdf": "^1.0.0", @@ -31965,247 +16194,261 @@ "jsbn": "~0.1.0", "safer-buffer": "^2.0.2", "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" } }, - "stackback": { + "node_modules/stackback": { "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" }, - "std-env": { + "node_modules/std-env": { "version": "3.7.0", "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz", "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==", - "dev": true + "dev": true, + "license": "MIT" }, - "stop-iteration-iterator": { + "node_modules/stop-iteration-iterator": { "version": "1.0.0", "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, - "requires": { + "license": "MIT", + "dependencies": { "internal-slot": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" } }, - "streamsearch": { + "node_modules/streamsearch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", - "dev": true + "dev": true, + "engines": { + "node": ">=10.0.0" + } }, - "string_decoder": { + "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "safe-buffer": "~5.2.0" } }, - "string-argv": { + "node_modules/string-argv": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.19" + } }, - "string-env-interpolation": { + "node_modules/string-env-interpolation": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/string-env-interpolation/-/string-env-interpolation-1.0.1.tgz", "integrity": "sha512-78lwMoCcn0nNu8LszbP1UA7g55OeE4v7rCeWnM5B453rnNr4aq+5it3FEYtZrSEiMvHZOZ9Jlqb0OD0M2VInqg==", - "dev": true + "dev": true, + "license": "MIT" }, - "string-width": { + "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "string-width-cjs": { - "version": "npm:string-width@4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "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" + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" } }, - "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" + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" } }, - "stringify-entities": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.3.tgz", - "integrity": "sha512-BP9nNHMhhfcMbiuQKCqMjhDP5yBCAxsPu4pHFFzJ6Alo9dZgY4VLDPutXqIjpRiMoKdp7Av85Gr73Q5uH9k7+g==", - "requires": { + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "license": "MIT", + "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "strip-ansi": { + "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "requires": { + "license": "MIT", + "dependencies": { "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "strip-ansi-cjs": { - "version": "npm:strip-ansi@6.0.1", + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "requires": { + "license": "MIT", + "dependencies": { "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "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": { + "node_modules/strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true - }, - "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 + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } }, - "strip-literal": { + "node_modules/strip-literal": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.1.0.tgz", "integrity": "sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "js-tokens": "^9.0.0" }, - "dependencies": { - "js-tokens": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.0.tgz", - "integrity": "sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==", - "dev": true - } + "funding": { + "url": "https://github.com/sponsors/antfu" } }, - "style-mod": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.0.tgz", - "integrity": "sha512-Ca5ib8HrFn+f+0n4N4ScTIA9iTOQ7MaGS1ylHcoVqW9J7w2w8PzN6g9gKmTYgGEBH8e120+RCmhpje6jC5uGWA==" + "node_modules/strip-literal/node_modules/js-tokens": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.0.tgz", + "integrity": "sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==", + "dev": true, + "license": "MIT" }, - "style-to-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.5.tgz", - "integrity": "sha512-rDRwHtoDD3UMMrmZ6BzOW0naTjMsVZLIjsGleSKS/0Oz+cgCfAPRspaqJuE8rDzpKha/nEvnM0IF4seEAZUTKQ==", - "requires": { - "inline-style-parser": "0.2.2" + "node_modules/style-mod": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz", + "integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==", + "license": "MIT" + }, + "node_modules/style-to-object": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.8.tgz", + "integrity": "sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g==", + "license": "MIT", + "dependencies": { + "inline-style-parser": "0.2.4" } }, - "style-value-types": { + "node_modules/style-value-types": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/style-value-types/-/style-value-types-5.0.0.tgz", "integrity": "sha512-08yq36Ikn4kx4YU6RD7jWEv27v4V+PUsOGa4n/as8Et3CuODMJQ00ENeAVXAeydX4Z2j1XHZF1K2sX4mGl18fA==", - "requires": { + "license": "MIT", + "dependencies": { "hey-listen": "^1.0.8", "tslib": "^2.1.0" } }, - "subscriptions-transport-ws": { + "node_modules/subscriptions-transport-ws": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/subscriptions-transport-ws/-/subscriptions-transport-ws-0.11.0.tgz", "integrity": "sha512-8D4C6DIH5tGiAIpp5I0wD/xRlNiZAPGHygzCe7VzyzUoxHtawzjNAY9SUTXU05/EY2NMY9/9GF0ycizkXr1CWQ==", - "requires": { + "deprecated": "The `subscriptions-transport-ws` package is no longer maintained. We recommend you use `graphql-ws` instead. For help migrating Apollo software to `graphql-ws`, see https://www.apollographql.com/docs/apollo-server/data/subscriptions/#switching-from-subscriptions-transport-ws For general help using `graphql-ws`, see https://github.com/enisdenjo/graphql-ws/blob/master/README.md", + "license": "MIT", + "dependencies": { "backo2": "^1.0.2", "eventemitter3": "^3.1.0", "iterall": "^1.2.1", "symbol-observable": "^1.0.4", "ws": "^5.2.0 || ^6.0.0 || ^7.0.0" }, - "dependencies": { - "eventemitter3": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", - "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==" - }, - "symbol-observable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", - "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" + "peerDependencies": { + "graphql": "^15.7.2 || ^16.0.0" + } + }, + "node_modules/subscriptions-transport-ws/node_modules/eventemitter3": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", + "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==", + "license": "MIT" + }, + "node_modules/subscriptions-transport-ws/node_modules/symbol-observable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/subscriptions-transport-ws/node_modules/ws": { + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", + "license": "MIT", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true }, - "ws": { - "version": "7.5.10", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", - "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", - "requires": {} + "utf-8-validate": { + "optional": true } } }, - "sucrase": { + "node_modules/sucrase": { "version": "3.35.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", - "requires": { + "license": "MIT", + "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", "glob": "^10.3.10", @@ -32214,80 +16457,104 @@ "pirates": "^4.0.1", "ts-interface-checker": "^0.1.9" }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/sucrase/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==", + "license": "MIT", "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==", - "requires": { - "balanced-match": "^1.0.0" - } - }, - "commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==" - }, - "foreground-child": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", - "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", - "requires": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" - } - }, - "glob": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", - "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", - "requires": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.3.5", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", - "path-scurry": "^1.10.1" - } - }, - "minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", - "requires": { - "brace-expansion": "^2.0.1" - } - }, - "signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==" - } + "balanced-match": "^1.0.0" + } + }, + "node_modules/sucrase/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/sucrase/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sucrase/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "supports-color": { + "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "supports-preserve-symlinks-flag": { + "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "svg-parser": { + "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" }, - "svgo": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.2.0.tgz", - "integrity": "sha512-4PP6CMW/V7l/GmKRKzsLR8xxjdHTV4IMvhTnpuHwwBazSIlw5W/5SmPjN8Dwyt7lKbSJrRDgp4t9ph0HgChFBQ==", - "requires": { + "node_modules/svgo": { + "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": "^5.1.0", @@ -32296,47 +16563,68 @@ "csso": "^5.0.5", "picocolors": "^1.0.0" }, - "dependencies": { - "commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==" - } + "bin": { + "svgo": "bin/svgo" + }, + "engines": { + "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" } }, - "swap-case": { + "node_modules/swap-case": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/swap-case/-/swap-case-2.0.2.tgz", "integrity": "sha512-kc6S2YS/2yXbtkSMunBtKdah4VFETZ8Oh6ONSmSd9bRxhqTrtARUCBUiWXH3xVPpvR7tz2CSnkuXVE42EcGnMw==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "tslib": "^2.0.3" } }, - "symbol-observable": { + "node_modules/symbol-observable": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", - "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==" + "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==", + "license": "MIT", + "engines": { + "node": ">=0.10" + } }, - "symbol-tree": { + "node_modules/symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", - "dev": true + "dev": true, + "license": "MIT" }, - "tailwind-merge": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.2.2.tgz", - "integrity": "sha512-tWANXsnmJzgw6mQ07nE3aCDkCK4QdT3ThPMCzawoYA2Pws7vSTCvz3Vrjg61jVUGfFZPJzxEP+NimbcW+EdaDw==", - "requires": { - "@babel/runtime": "^7.24.0" + "node_modules/tailwind-merge": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.5.4.tgz", + "integrity": "sha512-0q8cfZHMu9nuYP/b5Shb7Y7Sh1B7Nnl5GqNr1U+n2p6+mybvRtayrQ+0042Z5byvTA8ihjlP8Odo8/VnHbZu4Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" } }, - "tailwindcss": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.3.tgz", - "integrity": "sha512-U7sxQk/n397Bmx4JHbJx/iSOOv5G+II3f1kpLpY2QeUv5DcPdcTsYLlusZfq1NthHS1c1cZoyFmmkex1rzke0A==", - "requires": { + "node_modules/tailwindcss": { + "version": "3.4.14", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.14.tgz", + "integrity": "sha512-IcSvOcTRcUtQQ7ILQL5quRDg7Xs93PdJEk1ZLbhhvJc7uj/OAhYOnruEiwnGgBvUtaUAJ8/mhSw1o8L2jCiENA==", + "license": "MIT", + "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", "chokidar": "^3.5.3", @@ -32360,474 +16648,486 @@ "resolve": "^1.22.2", "sucrase": "^3.32.0" }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tailwindcss-animate": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz", + "integrity": "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==", + "license": "MIT", + "peerDependencies": { + "tailwindcss": ">=3.0.0 || insiders" + } + }, + "node_modules/tailwindcss/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "license": "ISC", "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==", - "requires": { - "is-glob": "^4.0.3" - } - } + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/tailwindcss/node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "license": "MIT", + "engines": { + "node": ">=10" } }, - "test-exclude": { + "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", "dev": true, - "requires": { + "license": "ISC", + "dependencies": { "@istanbuljs/schema": "^0.1.2", "glob": "^7.1.4", "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" } }, - "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": { + "node_modules/thenify": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "requires": { + "license": "MIT", + "dependencies": { "any-promise": "^1.0.0" } }, - "thenify-all": { + "node_modules/thenify-all": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "requires": { + "license": "MIT", + "dependencies": { "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" } }, - "throttleit": { + "node_modules/throttleit": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.1.tgz", "integrity": "sha512-vDZpf9Chs9mAdfY046mcPt8fg5QSZr37hEH4TXYBnDF+izxgrbRGUAAaBvIk/fJm9aOFCGFd1EsNg5AZCbnQCQ==", - "dev": true + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "through": { + "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true + "dev": true, + "license": "MIT" }, - "tiny-invariant": { + "node_modules/tiny-invariant": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", - "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==" + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", + "license": "MIT" }, - "tinybench": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.6.0.tgz", - "integrity": "sha512-N8hW3PG/3aOoZAN5V/NSAEDz0ZixDSSt5b/a05iqtpgfLWMSVuCo7w0k2vVvEjdrIoeGqZzweX2WlyioNIHchA==", - "dev": true + "node_modules/tinybench": { + "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" }, - "tinypool": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.3.tgz", - "integrity": "sha512-Ud7uepAklqRH1bvwy22ynrliC7Dljz7Tm8M/0RBUW+YRa4YHhZ6e4PpgE+fu1zr/WqB1kbeuVrdfeuyIBpy4tw==", - "dev": true + "node_modules/tinypool": { + "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" + } }, - "tinyspy": { + "node_modules/tinyspy": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.1.tgz", "integrity": "sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } }, - "title-case": { + "node_modules/title-case": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/title-case/-/title-case-3.0.3.tgz", "integrity": "sha512-e1zGYRvbffpcHIrnuqT0Dh+gEJtDaxDSoG4JAIpq4oDFyooziLBIiYQv0GBT4FUAnUop5uZ1hiIAj7oAF6sOCA==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "tslib": "^2.0.3" } }, - "tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "node_modules/tmp": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", "dev": true, - "requires": { - "rimraf": "^3.0.0" + "license": "MIT", + "engines": { + "node": ">=14.14" } }, - "to-fast-properties": { + "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==" + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "license": "MIT", + "engines": { + "node": ">=4" + } }, - "to-regex-range": { + "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "requires": { + "license": "MIT", + "dependencies": { "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" } }, - "toggle-selection": { + "node_modules/toggle-selection": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz", - "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==" + "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==", + "license": "MIT" }, - "tough-cookie": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", - "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", + "node_modules/tough-cookie": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", "dev": true, - "requires": { + "license": "BSD-3-Clause", + "dependencies": { "psl": "^1.1.33", "punycode": "^2.1.1", "universalify": "^0.2.0", "url-parse": "^1.5.3" }, - "dependencies": { - "punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true - }, - "universalify": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", - "dev": true - } + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie/node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" } }, - "tr46": { + "node_modules/tr46": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "punycode": "^2.3.1" }, - "dependencies": { - "punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true - } + "engines": { + "node": ">=18" } }, - "trim-lines": { + "node_modules/trim-lines": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", - "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==" - }, - "trough": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/trough/-/trough-2.1.0.tgz", - "integrity": "sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==" + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } }, - "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": {} + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } }, - "ts-interface-checker": { + "node_modules/ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==" + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "license": "Apache-2.0" }, - "ts-invariant": { + "node_modules/ts-invariant": { "version": "0.10.3", "resolved": "https://registry.npmjs.org/ts-invariant/-/ts-invariant-0.10.3.tgz", "integrity": "sha512-uivwYcQaxAucv1CzRp2n/QdYPo4ILf9VXgH19zEIjFx2EJufV16P0JtJVpYHy89DItG6Kwj2oIUjrcK5au+4tQ==", - "requires": { + "license": "MIT", + "dependencies": { "tslib": "^2.1.0" + }, + "engines": { + "node": ">=8" } }, - "ts-log": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/ts-log/-/ts-log-2.2.5.tgz", - "integrity": "sha512-PGcnJoTBnVGy6yYNFxWVNkdcAuAMstvutN9MgDJIV6L0oG8fB+ZNNy1T+wJzah8RPGor1mZuPQkVfXNDpy9eHA==", - "dev": true - }, - "ts-node": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", - "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "devOptional": true, - "requires": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "dependencies": { - "arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "devOptional": true - } - } + "node_modules/ts-log": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/ts-log/-/ts-log-2.2.7.tgz", + "integrity": "sha512-320x5Ggei84AxzlXp91QkIGSw5wgaLT6GeAH0KsqDmRZdVWW2OiSeVvElVoatk3f7nicwXlElXsoFkARiGE2yg==", + "dev": true, + "license": "MIT" }, - "ts-toolbelt": { + "node_modules/ts-toolbelt": { "version": "9.6.0", "resolved": "https://registry.npmjs.org/ts-toolbelt/-/ts-toolbelt-9.6.0.tgz", "integrity": "sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==", - "dev": true + "dev": true, + "license": "Apache-2.0" }, - "tsconfck": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.0.tgz", - "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" - } + "node_modules/tsconfck": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.4.tgz", + "integrity": "sha512-kdqWFGVJqe+KGYvlSO9NIaWn9jT1Ny4oKVzAJsKii5eoE9snzTJzL4+MMVOMn+fikWGFmKEylcXL710V/kIPJQ==", + "license": "MIT", + "bin": { + "tsconfck": "bin/tsconfck.js" + }, + "engines": { + "node": "^18 || >=20" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true } } }, - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + "node_modules/tslib": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.0.tgz", + "integrity": "sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA==", + "license": "0BSD" }, - "tunnel-agent": { + "node_modules/tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", "dev": true, - "requires": { + "license": "Apache-2.0", + "dependencies": { "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" } }, - "tweetnacl": { + "node_modules/tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", - "dev": true + "dev": true, + "license": "Unlicense" }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "node_modules/type-detect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", "dev": true, - "requires": { - "prelude-ls": "^1.2.1" + "license": "MIT", + "engines": { + "node": ">=4" } }, - "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==", - "dev": true - }, - "type-fest": { + "node_modules/type-fest": { "version": "0.21.3", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true - }, - "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" + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "types-ramda": { + "node_modules/types-ramda": { "version": "0.29.10", "resolved": "https://registry.npmjs.org/types-ramda/-/types-ramda-0.29.10.tgz", "integrity": "sha512-5PJiW/eiTPyXXBYGZOYGezMl6qj7keBiZheRwfjJZY26QPHsNrjfJnz0mru6oeqqoTHOni893Jfd6zyUXfQRWg==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "ts-toolbelt": "^9.6.0" } }, - "typescript": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", - "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", - "devOptional": true + "node_modules/typescript": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } }, - "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==", + "node_modules/ua-parser-js": { + "version": "1.0.39", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.39.tgz", + "integrity": "sha512-k24RCVWlEcjkdOxYmVJgeD/0a1TiSpqLg+ZalVGV9lsnr4yqu0w7tX/x2xX6G4zpkgQnRf89lxuZ1wsbjXM8lw==", "dev": true, - "requires": { - "@typescript-eslint/eslint-plugin": "7.16.0", - "@typescript-eslint/parser": "7.16.0", - "@typescript-eslint/utils": "7.16.0" + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + }, + { + "type": "github", + "url": "https://github.com/sponsors/faisalman" + } + ], + "license": "MIT", + "bin": { + "ua-parser-js": "script/cli.js" + }, + "engines": { + "node": "*" } }, - "ua-parser-js": { - "version": "1.0.37", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.37.tgz", - "integrity": "sha512-bhTyI94tZofjo+Dn8SN6Zv8nBDvyXTymAdM3LDI/0IboIUwTu1rEhW7v2TfiVsoYWgkQ4kOVqnI8APUFbIQIFQ==", - "dev": true - }, - "uc.micro": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", - "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==", - "dev": true - }, - "uglify-js": { - "version": "3.17.4", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", - "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==", + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "license": "MIT" + }, + "node_modules/ufo": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz", + "integrity": "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ==", "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" + "license": "MIT" + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" } }, - "unc-path-regex": { + "node_modules/unc-path-regex": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", "integrity": "sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } }, - "undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "devOptional": true + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "devOptional": true, + "license": "MIT" }, - "unicode-canonical-property-names-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", - "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==" + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", + "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", + "license": "MIT", + "engines": { + "node": ">=4" + } }, - "unicode-match-property-ecmascript": { + "node_modules/unicode-match-property-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", - "requires": { + "license": "MIT", + "dependencies": { "unicode-canonical-property-names-ecmascript": "^2.0.0", "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" } }, - "unicode-match-property-value-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", - "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==" + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", + "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", + "license": "MIT", + "engines": { + "node": ">=4" + } }, - "unicode-property-aliases-ecmascript": { + "node_modules/unicode-property-aliases-ecmascript": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", - "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==" + "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", + "license": "MIT", + "engines": { + "node": ">=4" + } }, - "unidiff": { + "node_modules/unidiff": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/unidiff/-/unidiff-1.0.4.tgz", "integrity": "sha512-ynU0vsAXw0ir8roa+xPCUHmnJ5goc5BTM2Kuc3IJd8UwgaeRs7VSD5+eeaQL+xp1JtB92hu/Zy/Lgy7RZcr1pQ==", - "requires": { - "diff": "^5.1.0" - }, + "license": "MIT", "dependencies": { - "diff": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", - "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==" - } + "diff": "^5.1.0" } }, - "unified": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.4.tgz", - "integrity": "sha512-apMPnyLjAX+ty4OrNap7yumyVAMlKx5IWU2wlzzUdYJO9A8f1p9m/gywF/GM2ZDFcjQPrx59Mc90KwmxsoklxQ==", - "requires": { + "node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "license": "MIT", + "dependencies": { "@types/unist": "^3.0.0", "bail": "^2.0.0", "devlop": "^1.0.0", @@ -32835,246 +17135,342 @@ "is-plain-obj": "^4.0.0", "trough": "^2.0.0", "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "unist-util-is": { + "node_modules/unist-util-is": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", - "requires": { + "license": "MIT", + "dependencies": { "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "unist-util-position": { + "node_modules/unist-util-position": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", - "requires": { + "license": "MIT", + "dependencies": { "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "unist-util-remove-position": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz", - "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==", - "requires": { - "@types/unist": "^3.0.0", - "unist-util-visit": "^5.0.0" - } - }, - "unist-util-stringify-position": { + "node_modules/unist-util-stringify-position": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", - "requires": { + "license": "MIT", + "dependencies": { "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "unist-util-visit": { + "node_modules/unist-util-visit": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", - "requires": { + "license": "MIT", + "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "unist-util-visit-parents": { + "node_modules/unist-util-visit-parents": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", - "requires": { + "license": "MIT", + "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "universalify": { + "node_modules/universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } }, - "unixify": { + "node_modules/unixify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unixify/-/unixify-1.0.0.tgz", "integrity": "sha512-6bc58dPYhCMHHuwxldQxO3RRNZ4eCogZ/st++0+fcC1nr0jiGUtAdBJ2qzmLQWSxbtz42pWt4QQMiZ9HvZf5cg==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "normalize-path": "^2.1.1" }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/unixify/node_modules/normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", + "dev": true, + "license": "MIT", "dependencies": { - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", - "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - } + "remove-trailing-separator": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "untildify": { + "node_modules/untildify": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "update-browserslist-db": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", - "requires": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" + "node_modules/update-browserslist-db": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", + "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" } }, - "upper-case": { + "node_modules/upper-case": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-2.0.2.tgz", "integrity": "sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "tslib": "^2.0.3" } }, - "upper-case-first": { + "node_modules/upper-case-first": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-2.0.2.tgz", "integrity": "sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "tslib": "^2.0.3" } }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "node_modules/uri-js-replace": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uri-js-replace/-/uri-js-replace-1.0.1.tgz", + "integrity": "sha512-W+C9NWNLFOoBI2QWDp4UT9pv65r2w5Cx+3sTYFvtMdDBxkKt1syCqsUdSFAChbEe1uK5TfS04wt/nGwmaeIQ0g==", "dev": true, - "requires": { - "punycode": "^2.1.0" - }, - "dependencies": { - "punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true - } - } + "license": "MIT" }, - "url-parse": { + "node_modules/url-parse": { "version": "1.5.10", "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "querystringify": "^2.1.1", "requires-port": "^1.0.0" } }, - "urlpattern-polyfill": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-8.0.2.tgz", - "integrity": "sha512-Qp95D4TPJl1kC9SKigDcqgyM2VDVO4RiJc2d4qe5GrYm+zbIQCWWKAFaJNQ4BhdFeDGwBmAxqJBwWSJDb9T3BQ==", - "dev": true + "node_modules/urlpattern-polyfill": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-10.0.0.tgz", + "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==", + "dev": true, + "license": "MIT" }, - "use-callback-ref": { + "node_modules/use-callback-ref": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.2.tgz", "integrity": "sha512-elOQwe6Q8gqZgDA8mrh44qRTQqpIHDcZ3hXTLjBe1i4ph8XpNJnO+aQf3NaG+lriLopI4HMx9VjQLfPQ6vhnoA==", - "requires": { + "license": "MIT", + "dependencies": { "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "use-query-params": { + "node_modules/use-query-params": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/use-query-params/-/use-query-params-2.2.1.tgz", "integrity": "sha512-i6alcyLB8w9i3ZK3caNftdb+UnbfBRNPDnc89CNQWkGRmDrm/gfydHvMBfVsQJRq3NoHOM2dt/ceBWG2397v1Q==", - "requires": { + "license": "ISC", + "dependencies": { "serialize-query-params": "^2.0.2" + }, + "peerDependencies": { + "@reach/router": "^1.2.1", + "react": ">=16.8.0", + "react-dom": ">=16.8.0", + "react-router-dom": ">=5" + }, + "peerDependenciesMeta": { + "@reach/router": { + "optional": true + }, + "react-router-dom": { + "optional": true + } } }, - "use-sidecar": { + "node_modules/use-sidecar": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz", "integrity": "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==", - "requires": { + "license": "MIT", + "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.9.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "util-deprecate": { + "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" }, - "uuid": { + "node_modules/uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true - }, - "v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "devOptional": true - }, - "v8-to-istanbul": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz", - "integrity": "sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==", "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" } }, - "value-or-promise": { + "node_modules/value-or-promise": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/value-or-promise/-/value-or-promise-1.0.12.tgz", "integrity": "sha512-Z6Uz+TYwEqE7ZN50gwn+1LCVo9ZVrpxRPOhOLnncYkY1ZzOYtrX8Fwf/rFktZ8R5mJms6EZf5TqNOMeZmnPq9Q==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } }, - "verror": { + "node_modules/verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", "dev": true, - "requires": { + "engines": [ + "node >=0.6.0" + ], + "license": "MIT", + "dependencies": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", "extsprintf": "^1.2.0" } }, - "vfile": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.1.tgz", - "integrity": "sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==", - "requires": { + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "license": "MIT", + "dependencies": { "@types/unist": "^3.0.0", - "unist-util-stringify-position": "^4.0.0", "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "vfile-message": { + "node_modules/vfile-message": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", - "requires": { + "license": "MIT", + "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "victory-vendor": { - "version": "36.9.1", - "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.1.tgz", - "integrity": "sha512-+pZIP+U3pEJdDCeFmsXwHzV7vNHQC/eIbHklfe2ZCZqayYRH7lQbHcVgsJ0XOOv27hWs4jH4MONgXxHMObTMSA==", - "requires": { + "node_modules/victory-vendor": { + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz", + "integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==", + "license": "MIT AND ISC", + "dependencies": { "@types/d3-array": "^3.0.3", "@types/d3-ease": "^3.0.0", "@types/d3-interpolate": "^3.0.1", @@ -33091,51 +17487,133 @@ "d3-timer": "^3.0.1" } }, - "vite": { - "version": "5.4.7", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.7.tgz", - "integrity": "sha512-5l2zxqMEPVENgvzTuBpHer2awaetimj2BGkhBPdnwKbPNOlHsODU+oiazEZzLK7KhAnOrO+XGYJYn4ZlUhDtDQ==", - "requires": { + "node_modules/vite": { + "version": "5.4.9", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.9.tgz", + "integrity": "sha512-20OVpJHh0PAM0oSOELa5GaZNWeDjcAvQjGXy2Uyr+Tp+/D2/Hdz6NLgpJLsarPTA2QJ6v8mX2P1ZfbsSKvdMkg==", + "license": "MIT", + "dependencies": { "esbuild": "^0.21.3", - "fsevents": "~2.3.3", "postcss": "^8.4.43", "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } } }, - "vite-node": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.4.0.tgz", - "integrity": "sha512-VZDAseqjrHgNd4Kh8icYHWzTKSCZMhia7GyHfhtzLW33fZlG9SwsB6CEhgyVOWkJfJ2pFLrp/Gj1FSfAiqH9Lw==", + "node_modules/vite-node": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.6.0.tgz", + "integrity": "sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "cac": "^6.7.14", "debug": "^4.3.4", "pathe": "^1.1.1", "picocolors": "^1.0.0", "vite": "^5.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" } }, - "vite-tsconfig-paths": { + "node_modules/vite-tsconfig-paths": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-4.3.2.tgz", "integrity": "sha512-0Vd/a6po6Q+86rPlntHye7F31zA2URZMbH8M3saAZ/xR9QoGN/L21bxEGfXdWmFdNkqPpRdxFT7nmNe12e9/uA==", - "requires": { + "license": "MIT", + "dependencies": { "debug": "^4.1.1", "globrex": "^0.1.2", "tsconfck": "^3.0.3" + }, + "peerDependencies": { + "vite": "*" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } } }, - "vitest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.4.0.tgz", - "integrity": "sha512-gujzn0g7fmwf83/WzrDTnncZt2UiXP41mHuFYFrdwaLRVQ6JYQEiME2IfEjU3vcFL3VKa75XhI3lFgn+hfVsQw==", - "dev": true, - "requires": { - "@vitest/expect": "1.4.0", - "@vitest/runner": "1.4.0", - "@vitest/snapshot": "1.4.0", - "@vitest/spy": "1.4.0", - "@vitest/utils": "1.4.0", + "node_modules/vite/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, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/vitest": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.6.0.tgz", + "integrity": "sha512-H5r/dN06swuFnzNFhq/dnz37bPXnq8xB2xB5JOVk8K09rUtoeNN+LHWkoQ0A/i3hvbUKKcCei9KpbxqHMLhLLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "1.6.0", + "@vitest/runner": "1.6.0", + "@vitest/snapshot": "1.6.0", + "@vitest/spy": "1.6.0", + "@vitest/utils": "1.6.0", "acorn-walk": "^8.3.2", "chai": "^4.3.10", "debug": "^4.3.4", @@ -33147,377 +17625,519 @@ "std-env": "^3.5.0", "strip-literal": "^2.0.0", "tinybench": "^2.5.1", - "tinypool": "^0.8.2", + "tinypool": "^0.8.3", "vite": "^5.0.0", - "vite-node": "1.4.0", + "vite-node": "1.6.0", "why-is-node-running": "^2.2.2" }, - "dependencies": { - "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, - "requires": { - "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" - } - }, - "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 - }, - "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 - }, - "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 - }, - "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 + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/node": "^18.0.0 || >=20.0.0", + "@vitest/browser": "1.6.0", + "@vitest/ui": "1.6.0", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true }, - "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, - "requires": { - "path-key": "^4.0.0" - } + "@types/node": { + "optional": true }, - "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, - "requires": { - "mimic-fn": "^4.0.0" - } + "@vitest/browser": { + "optional": true }, - "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 + "@vitest/ui": { + "optional": true }, - "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 + "happy-dom": { + "optional": true }, - "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 + "jsdom": { + "optional": true } } }, - "vscode-languageserver-types": { - "version": "3.17.5", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", - "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==" + "node_modules/vitest/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": { + "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" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/vitest/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" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vitest/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/vitest/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, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vitest/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/vitest/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": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vitest/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" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vitest/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/vitest/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, - "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==", + "node_modules/vitest/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, - "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" + "license": "MIT", + "engines": { + "node": ">=12" }, - "dependencies": { - "semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", - "dev": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "w3c-keyname": { + "node_modules/vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", + "license": "MIT" + }, + "node_modules/w3c-keyname": { "version": "2.2.8", "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", - "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==" + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", + "license": "MIT" }, - "w3c-xmlserializer": { + "node_modules/w3c-xmlserializer": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" } }, - "warning": { + "node_modules/warning": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", - "requires": { + "license": "MIT", + "dependencies": { "loose-envify": "^1.0.0" } }, - "wcwidth": { + "node_modules/wcwidth": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "defaults": "^1.0.3" } }, - "web-streams-polyfill": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", - "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", - "integrity": "sha512-FE+a4PPkOmBbgNDIyRmcHhgXn+2ClRl3JzJdDu/P4+B8y81LqKe6RAsI9b3lAOHe1T1BMkSjsRHTYRikImZnVA==", - "dev": true, - "requires": { - "@peculiar/asn1-schema": "^2.3.8", - "@peculiar/json-schema": "^1.1.12", - "asn1js": "^3.0.1", - "pvtsutils": "^1.3.5", - "tslib": "^2.6.2" - } - }, - "webidl-conversions": { + "node_modules/webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", - "dev": true + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } }, - "whatwg-encoding": { + "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==", "dev": true, - "requires": { + "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==", + "dev": true, + "license": "MIT", "dependencies": { - "iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - } - } + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "whatwg-mimetype": { + "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==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } }, - "whatwg-url": { + "node_modules/whatwg-url": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.0.0.tgz", "integrity": "sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "tr46": "^5.0.0", "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" } }, - "which": { + "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "requires": { + "license": "ISC", + "dependencies": { "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" } }, - "which-boxed-primitive": { + "node_modules/which-boxed-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "is-bigint": "^1.0.1", "is-boolean-object": "^1.1.0", "is-number-object": "^1.0.4", "is-string": "^1.0.5", "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "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", - "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", "dev": true, - "requires": { - "is-map": "^2.0.1", - "is-set": "^2.0.1", - "is-weakmap": "^2.0.1", - "is-weakset": "^2.0.1" + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "which-module": { + "node_modules/which-module": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", - "dev": true + "dev": true, + "license": "ISC" }, - "which-typed-array": { + "node_modules/which-typed-array": { "version": "1.1.15", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.7", "for-each": "^0.3.3", "gopd": "^1.0.1", "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "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==", + "node_modules/why-is-node-running": { + "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": { + "license": "MIT", + "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" } }, - "wordwrap": { + "node_modules/wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==" + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "license": "MIT" }, - "wrap-ansi": { + "node_modules/wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" } }, - "wrap-ansi-cjs": { - "version": "npm:wrap-ansi@7.0.0", + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "requires": { + "license": "MIT", + "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" } }, - "wrappy": { + "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "dev": true, + "license": "ISC" }, - "ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "dev": true, - "requires": {} + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } }, - "xml-name-validator": { + "node_modules/xml-name-validator": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", - "dev": true + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } }, - "xmlchars": { + "node_modules/xmlchars": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", - "dev": true + "dev": true, + "license": "MIT" }, - "y18n": { + "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } }, - "yallist": { + "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "license": "ISC" }, - "yaml": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.0.tgz", - "integrity": "sha512-2wWLbGbYDiSqqIKoPjar3MPgB94ErzCtrNE1FdqGuaO0pi2JGjmE8aW8TDZwzU7vuxcGRdL/4gPQwQ7hD5AMSw==" + "node_modules/yaml": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.0.tgz", + "integrity": "sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } }, - "yaml-ast-parser": { + "node_modules/yaml-ast-parser": { "version": "0.0.43", "resolved": "https://registry.npmjs.org/yaml-ast-parser/-/yaml-ast-parser-0.0.43.tgz", "integrity": "sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==", - "dev": true + "dev": true, + "license": "Apache-2.0" }, - "yargs": { + "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", @@ -33526,54 +18146,68 @@ "y18n": "^5.0.5", "yargs-parser": "^21.1.1" }, - "dependencies": { - "yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true - } + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" } }, - "yauzl": { + "node_modules/yauzl": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" } }, - "yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "devOptional": true - }, - "yocto-queue": { + "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "zen-observable": { + "node_modules/zen-observable": { "version": "0.8.15", "resolved": "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz", - "integrity": "sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==" + "integrity": "sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==", + "license": "MIT" }, - "zen-observable-ts": { + "node_modules/zen-observable-ts": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-1.2.5.tgz", "integrity": "sha512-QZWQekv6iB72Naeake9hS1KxHlotfRpe+WGNbNx5/ta+R3DNjVO2bswf63gXlWDcs+EMd7XY8HfVQyP1X6T4Zg==", - "requires": { + "license": "MIT", + "dependencies": { "zen-observable": "0.8.15" } }, - "zwitch": { + "node_modules/zwitch": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", - "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==" + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } } } } diff --git a/frontend/app/package.json b/frontend/app/package.json index 9ad1a9f3fc..749d833a50 100644 --- a/frontend/app/package.json +++ b/frontend/app/package.json @@ -23,30 +23,31 @@ "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", - "@codemirror/commands": "^6.3.3", - "@codemirror/lang-markdown": "^6.2.4", + "@codemirror/commands": "^6.7.0", + "@codemirror/lang-markdown": "^6.3.0", + "@codemirror/language": "^6.10.3", "@codemirror/state": "^6.4.1", "@codemirror/theme-one-dark": "^6.1.2", - "@codemirror/view": "^6.26.1", - "@graphiql/plugin-explorer": "^1.0.4", + "@codemirror/view": "^6.34.1", + "@graphiql/plugin-explorer": "^3.2.2", + "@graphiql/toolkit": "^0.11.0", "@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-accordion": "^1.2.1", "@radix-ui/react-dropdown-menu": "^2.0.6", "@radix-ui/react-label": "^2.0.2", "@radix-ui/react-popover": "^1.0.7", "@radix-ui/react-progress": "^1.0.3", + "@radix-ui/react-scroll-area": "^1.2.0", "@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-tooltip": "^1.0.7", "@svgr/rollup": "^8.1.0", @@ -58,12 +59,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", + "graphiql": "^3.7.1", "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", @@ -79,6 +81,7 @@ "react-markdown": "^9.0.1", "react-paginate": "^8.2.0", "react-popper": "^2.3.0", + "react-resizable-panels": "^2.1.5", "react-router-dom": "^6.22.3", "react-simple-code-editor": "^0.13.1", "react-toastify": "^9.1.3", @@ -88,16 +91,17 @@ "sha1": "^1.1.1", "subscriptions-transport-ws": "^0.11.0", "tailwind-merge": "^2.2.2", + "tailwindcss-animate": "^1.0.7", "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 +112,15 @@ "@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", - "husky": "^8.0.3", + "cypress": "^13.15.0", "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": { @@ -141,20 +130,13 @@ "react-dom": "^18.2.0" }, "cm6-graphql": { + "@codemirror/language": "^6.2.1", "@codemirror/state": "^6.4.1", "@codemirror/view": "^6.26.1" } }, "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..acc362f66d 100644 --- a/frontend/app/src/components/account-menu.tsx +++ b/frontend/app/src/components/account-menu.tsx @@ -1,111 +1,218 @@ +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_DISCORD_URL, + 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 = () => ( + <> + + + + Infrahub documentation + + + + + + GraphQL Sandbox + + + + + + + Swagger documentation + + + + + + + + + GitHub Repository + + + + + + + Join our Discord server + + + +); + +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..e7c494ac80 100644 --- a/frontend/app/src/components/branch-selector.tsx +++ b/frontend/app/src/components/branch-selector.tsx @@ -1,132 +1,217 @@ -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 { useAtomValue, useSetAtom } from "jotai"; +import { 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, CommandInput, CommandItem, CommandList } from "@/components/ui/command"; +import graphqlClient from "@/graphql/graphqlClientApollo"; +import { useAuth } from "@/hooks/useAuth"; +import { constructPath } from "@/utils/fetch"; +import { useCommandState } from "cmdk"; +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 && ( - - )} - - ); +type DisplayForm = { + open: boolean; + defaultBranchName?: string; +}; 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({ open: false }); + + useEffect(() => { + if (isOpen) graphqlClient.refetchQueries({ include: ["GetBranches"] }); + }, [isOpen]); - const valueLabel = ( -
- {getBranchIcon(branch, true)} + return ( + { + setDisplayForm({ open: false }); + setIsOpen(open); + }} + > + + + -

{branch?.name}

-
+ + {displayForm.open ? ( + setDisplayForm({ open: false })} + onSuccess={() => { + setDisplayForm({ open: false }); + setIsOpen(false); + }} + defaultBranchName={displayForm.defaultBranchName} + data-testid="branch-create-form" + /> + ) : ( + + )} + + ); +} - const branchesOptions: SelectOption[] = branchesToSelectOptions(branches); +function BranchSelect({ + setPopoverOpen, + setFormOpen, +}: { + setPopoverOpen: (open: boolean) => void; + setFormOpen: (displayForm: DisplayForm) => 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); - } else { - setBranchInQueryString(branch.name); - } + const handleBranchChange = (branch: Branch) => { + setBranchInQueryString(branch.is_default ? undefined : branch.name); + setCurrentBranch(branch); + setPopoverOpen(false); }; - const renderOption = ({ option, active, selected }: any) => ( -
-
{getBranchIcon(option, active)}
- -
-

{option.name}

- {selected ? ( - - - - ) : null} + return ( + <> + +
+ + + +
+ + + setFormOpen({ open: true, defaultBranchName })} + /> + + {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 = () => { - const permission = usePermission(); - const [open, setOpen] = React.useState(false); +export const BranchFormTriggerButton = ({ + setOpen, +}: { + setOpen: (displayForm: DisplayForm) => void; +}) => { + const { isAuthenticated } = useAuth(); + + const handleClick = (e: React.MouseEvent) => { + e.stopPropagation(); + setOpen({ open: true }); + }; + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === "Enter") { + e.stopPropagation(); + setOpen({ open: true }); + } + }; return ( - - - - - - + + + + ); +}; - -
Create a branch
-
- setOpen(false)} onSuccess={() => setOpen(false)} /> - - +const BranchNotFound = ({ onSelect }: { onSelect: (branchName: string) => void }) => { + const filteredCount = useCommandState((state) => state.filtered.count); + const search = useCommandState((state) => state.search); + + if (filteredCount !== 0) return null; + + return ( + onSelect(search)} + className="text-neutral-600 truncate gap-1" + > + Create branch {search} + ); }; diff --git a/frontend/app/src/components/buttons/button-primitive.tsx b/frontend/app/src/components/buttons/button-primitive.tsx index 537df4a76a..b224a49c58 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} @@ -59,11 +60,12 @@ export const Button = forwardRef( interface ButtonWithTooltipProps extends ButtonProps { tooltipContent?: TooltipProps["content"]; tooltipEnabled?: TooltipProps["enabled"]; + side?: TooltipProps["side"]; } export const ButtonWithTooltip = forwardRef( - ({ tooltipContent, tooltipEnabled, ...props }, ref) => ( - + ({ tooltipContent, tooltipEnabled, side, ...props }, ref) => ( + 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} + > ` - ${className?.includes("p-") ? "" : "p-2"} - inline-flex items-center gap-x-1.5 rounded-full - text-sm font-semibold - focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 - shadow-sm ring-1 ring-inset ring-gray-300 -`; - -const getClassName = (type: BUTTON_TYPES) => { - switch (type) { - case BUTTON_TYPES.VALIDATE: { - return ` - bg-green-500 text-gray-50 - hover:bg-green-400 - disabled:cursor-not-allowed disabled:bg-green-400 disabled:text-gray-100 disabled:border-slate-200 disabled:shadow-none - `; - } - case BUTTON_TYPES.CANCEL: { - return ` - bg-red-600 text-gray-50 - hover:bg-red-400 - disabled:cursor-not-allowed disabled:bg-red-400 disabled:text-gray-100 disabled:border-slate-200 disabled:shadow-none - `; - } - case BUTTON_TYPES.WARNING: { - return ` - bg-yellow-400 text-gray-800 - hover:bg-yellow-300 - disabled:cursor-not-allowed disabled:bg-yellow-200 disabled:text-gray-600 disabled:border-slate-200 disabled:shadow-none - `; - } - case BUTTON_TYPES.DEFAULT: { - return ` - bg-gray-100 text-gray-900 - hover:bg-gray-200 - disabled:cursor-not-allowed disabled:bg-slate-50 disabled:text-slate-500 disabled:border-slate-200 disabled:shadow-none - `; - } - default: { - return "disabled:cursor-not-allowed disabled:bg-slate-50 disabled:text-slate-500 disabled:border-slate-200 disabled:shadow-none"; - } - } -}; - -export const RoundedButton = forwardRef((props: any, ref) => { - const { type, className, onClick, ...propsToPass } = props; - - const customClassName = getClassName(type); - - return ( - - ); -}); diff --git a/frontend/app/src/components/buttons/select-button.tsx b/frontend/app/src/components/buttons/select-button.tsx deleted file mode 100644 index b98d09094a..0000000000 --- a/frontend/app/src/components/buttons/select-button.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import { classNames } from "@/utils/common"; -import { Listbox, Transition } from "@headlessui/react"; -import { Icon } from "@iconify-icon/react"; -import { Fragment } from "react"; -import { BUTTON_TYPES, Button } from "./button"; - -// type SelectButtonProps = {}; - -export const SelectButton = (props: any) => { - const { label, value, valueLabel, onChange, options, renderOption } = props; - - return ( - - {({ open }) => ( - <> - {label} -
-
- -
- {valueLabel} -
- - -
-
- - - - {options.map((option: any) => ( - - classNames( - active ? "text-custom-white bg-custom-blue-800" : "text-gray-900", - "cursor-pointer select-none p-4 text-sm" - ) - } - value={option}> - {(attributes) => - renderOption({ - option, - ...attributes, - }) - } - - ))} - - -
- - )} -
- ); -}; 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..750b47c2e8 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 { ReactElement, forwardRef } 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 - Sign in + to={constructPath("/login")} + state={{ from: location }} + > + Login {" "} 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..d57705deca 100644 --- a/frontend/app/src/components/conversations/thread.tsx +++ b/frontend/app/src/components/conversations/thread.tsx @@ -2,12 +2,16 @@ 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"; import { createObject } from "@/graphql/mutations/objects/createObject"; import { updateObjectWithId } from "@/graphql/mutations/objects/updateObjectWithId"; import { useAuth } from "@/hooks/useAuth"; +import useQuery from "@/hooks/useQuery"; +import { getObjectPermissionsQuery } from "@/screens/permission/queries/getObjectPermissions"; +import { getPermission } from "@/screens/permission/utils"; import { currentBranchAtom } from "@/state/atoms/branches.atom"; import { datetimeAtom } from "@/state/atoms/time.atom"; import { classNames } from "@/utils/common"; @@ -21,7 +25,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; @@ -48,6 +51,13 @@ export const Thread = (props: tThread) => { const [confirmModal, setConfirmModal] = useState(false); const [markAsResolved, setMarkAsResolved] = useState(false); + const { loading, data } = useQuery( + gql(getObjectPermissionsQuery(PROPOSED_CHANGES_THREAD_COMMENT_OBJECT)) + ); + + const permission = + data && getPermission(data?.[PROPOSED_CHANGES_THREAD_COMMENT_OBJECT]?.permissions?.edges); + const handleSubmit = async ({ comment }: { comment: string }) => { try { setIsLoading(true); @@ -177,7 +187,8 @@ export const Thread = (props: tThread) => { + data-cy="thread" + > {displayContext && getThreadTitle(thread)} {sortedComments.map((comment: any, index: number) => ( @@ -200,7 +211,10 @@ export const Thread = (props: tThread) => {
{MarkAsResolved} -
diff --git a/frontend/app/src/components/display/accordion.tsx b/frontend/app/src/components/display/accordion.tsx index 5d2d401568..41321f5521 100644 --- a/frontend/app/src/components/display/accordion.tsx +++ b/frontend/app/src/components/display/accordion.tsx @@ -1,11 +1,12 @@ +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; children?: any; className?: string; + titleClassName?: string; iconClassName?: string; defaultOpen?: boolean; style?: CSSProperties; @@ -19,6 +20,7 @@ export default function Accordion({ className, hideChevron, iconClassName, + titleClassName, ...props }: AccordionProps) { const [isOpen, setIsOpen] = useState(); @@ -27,22 +29,20 @@ export default function Accordion({ return (
-
-
setIsOpen(!open)}> - - {open ? : } - +
setIsOpen(!open)}> + + {open ? : } + - {title} -
+ {title}
+ {open && children}
); diff --git a/frontend/app/src/components/display/avatar.tsx b/frontend/app/src/components/display/avatar.tsx index f73c13ca84..8a612bc46c 100644 --- a/frontend/app/src/components/display/avatar.tsx +++ b/frontend/app/src/components/display/avatar.tsx @@ -1,15 +1,14 @@ 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"; +import { forwardRef } from "react"; -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 +19,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 +30,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; +export const Avatar = forwardRef((props: tAvatar, ref) => { + const { name, text, variant, size, className, isLoading, ...otherProps } = props; if (isLoading) { return ( @@ -46,8 +47,14 @@ export const Avatar = (props: tAvatar) => { } return ( -
- {initials(name)} -
+ ); -}; +}); diff --git a/frontend/app/src/components/display/background.tsx b/frontend/app/src/components/display/background.tsx deleted file mode 100644 index 4719641636..0000000000 --- a/frontend/app/src/components/display/background.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { classNames } from "@/utils/common"; -import { MouseEventHandler } from "react"; - -type tBackground = { - onClick?: MouseEventHandler; - className?: string; -}; - -export const Background = ({ onClick, className = "", ...propsToPass }: tBackground) => { - return ( -
- ); -}; 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..d0bc8d1159 100644 --- a/frontend/app/src/components/display/properties-popover.tsx +++ b/frontend/app/src/components/display/properties-popover.tsx @@ -1,6 +1,6 @@ import { ButtonWithTooltip } from "@/components/buttons/button-primitive"; -import { usePermission } from "@/hooks/usePermission"; import ObjectItemMetaEdit from "@/screens/object-item-meta-edit/object-item-meta-edit"; +import { Permission } from "@/screens/permission/types"; import { metaEditFieldDetailsState } from "@/state/atoms/showMetaEdit.atom copy"; import { Icon } from "@iconify-icon/react"; import { useAtom } from "jotai/index"; @@ -16,6 +16,7 @@ interface PropertiesEditTriggerProps { data: any; schema: any; hideHeader?: boolean; + permission: Permission; } const PropertiesPopover = ({ @@ -26,8 +27,8 @@ const PropertiesPopover = ({ data, schema, hideHeader, + permission, }: PropertiesEditTriggerProps) => { - const permission = usePermission(); const [showMetaEditModal, setShowMetaEditModal] = useState(false); const [metaEditFieldDetails, setMetaEditFieldDetails] = useAtom(metaEditFieldDetailsState); @@ -56,10 +57,11 @@ const PropertiesPopover = ({ }); setShowMetaEditModal(true); }} - disabled={!permission.write.allow} - tooltipEnabled={!permission.write.allow} - tooltipContent={permission.write.message ?? undefined} - data-testid="properties-edit-button"> + disabled={!permission.update.isAllowed} + tooltipEnabled={!permission.update.isAllowed} + tooltipContent={permission.update.message ?? undefined} + data-testid="properties-edit-button" + >
@@ -77,7 +79,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..48b5cd517c 100644 --- a/frontend/app/src/components/display/slide-over.tsx +++ b/frontend/app/src/components/display/slide-over.tsx @@ -1,16 +1,17 @@ -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 { open: boolean; setOpen: React.Dispatch>; + onClose?: () => void; children: React.ReactNode; title: string | React.ReactNode; offset?: number; @@ -22,8 +23,7 @@ interface SlideOverContextProps { export const SlideOverContext = React.createContext({}); -export default function SlideOver(props: Props) { - const { open, setOpen, title, offset = 0 } = props; +export default function SlideOver({ open, setOpen, onClose, title, offset = 0, children }: Props) { const initialFocusRef = useRef(null); const [preventClose, setPreventClose] = useState(false); @@ -43,7 +43,15 @@ export default function SlideOver(props: Props) { return ( - + { + setOpen(value); + if (onClose) onClose(); + }} + initialFocus={initialFocusRef} + > + leaveTo="opacity-0" + >
+ leaveTo="translate-x-full" + > + data-testid="side-panel-container" + >
{title}
- {props.children} + {children}
@@ -103,7 +114,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..1e70337049 100644 --- a/frontend/app/src/components/editor/code-editor.tsx +++ b/frontend/app/src/components/editor/code-editor.tsx @@ -5,7 +5,7 @@ import Prism from "prismjs"; import "prismjs/components/prism-json"; // need this import "prismjs/themes/prism.css"; //Example style, you can use another -import { BUTTON_TYPES, Button } from "@/components/buttons/button"; +import { Button } from "@/components/buttons/button-primitive"; import { ALERT_TYPES, Alert } from "@/components/ui/alert"; import { classNames } from "@/utils/common"; import { useState } from "react"; @@ -34,8 +34,10 @@ export const CodeEditor = (props: any) => { {enableCopy && ( 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..7aac322ccd --- /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 { 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..4a28b12168 --- /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 { 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..e8df443a82 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 { 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..7c635d855c 100644 --- a/frontend/app/src/components/form/branch-create-form.tsx +++ b/frontend/app/src/components/form/branch-create-form.tsx @@ -1,16 +1,15 @@ +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"; type BranchFormData = { name: string; @@ -21,9 +20,10 @@ type BranchFormData = { type BranchCreateFormProps = { onCancel?: () => void; onSuccess?: (branch: Branch) => void; + defaultBranchName?: string; }; -const BranchCreateForm = ({ onCancel, onSuccess }: BranchCreateFormProps) => { +const BranchCreateForm = ({ defaultBranchName, onCancel, onSuccess }: BranchCreateFormProps) => { const [branches, setBranches] = useAtom(branchesState); const [, setBranchInQueryString] = useQueryParam(QSP.BRANCH, StringParam); const [createBranch] = useMutation(BRANCH_CREATE); @@ -56,10 +56,15 @@ const BranchCreateForm = ({ onCancel, onSuccess }: BranchCreateFormProps) => { sync_with_git: !!data.sync_with_git.value, }; await handleSubmit(branchData); - }}> + }} + > { fields: Array; 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..f4b7e9c7ae 100644 --- a/frontend/app/src/components/form/fields/common.tsx +++ b/frontend/app/src/components/form/fields/common.tsx @@ -1,18 +1,17 @@ -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, + RelationshipValueFromPool, } 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 { Link } from "react-router-dom"; export const InputUniqueTips = ({ className }: { className: string }) => ( @@ -51,7 +50,7 @@ export const LabelFormField = ({ )} {fieldData?.source?.type === "pool" && ( - + )}
); @@ -66,12 +65,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 +55,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..0984ff7803 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 @@ -1,8 +1,9 @@ import { ButtonProps, ButtonWithTooltip } from "@/components/buttons/button-primitive"; import SlideOver, { SlideOverTitle } from "@/components/display/slide-over"; -import { usePermission } from "@/hooks/usePermission"; import ObjectItemEditComponent from "@/screens/object-item-edit/object-item-edit-paginated"; +import { Permission } from "@/screens/permission/types"; import { IModelSchema } from "@/state/atoms/schema.atom"; + import { Icon } from "@iconify-icon/react"; import { useState } from "react"; @@ -10,15 +11,16 @@ interface ObjectEditSlideOverTriggerProps extends ButtonProps { data: any; schema: IModelSchema; onUpdateComplete?: () => void; + permission: Permission; } const ObjectEditSlideOverTrigger = ({ data, schema, onUpdateComplete, + permission, ...props }: ObjectEditSlideOverTriggerProps) => { - const permission = usePermission(); const [isEditDrawerOpen, setIsEditDrawerOpen] = useState(false); return ( @@ -28,11 +30,12 @@ const ObjectEditSlideOverTrigger = ({ variant="outline" size="icon" onClick={() => setIsEditDrawerOpen(true)} - disabled={!permission.write.allow} - tooltipEnabled={!permission.write.allow} - tooltipContent={permission.write.message ?? undefined} + disabled={!permission.create.isAllowed} + tooltipEnabled={!permission.create.isAllowed} + tooltipContent={permission.create.message ?? undefined} data-testid="edit-button" - {...props}> + {...props} + > @@ -46,7 +49,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..e3a5fff850 100644 --- a/frontend/app/src/components/form/object-form.tsx +++ b/frontend/app/src/components/form/object-form.tsx @@ -1,14 +1,28 @@ -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, + ACCOUNT_ROLE_OBJECT, + GLOBAL_PERMISSION_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 { AccountRoleForm } from "@/screens/role-management/account-role-form"; +import { GlobalPermissionForm } from "@/screens/role-management/global-permissions-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 +38,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 +54,6 @@ const ObjectForm = ({ kind, isFilterForm, currentProfiles, ...props }: ObjectFor ); } - if (isFilterForm) { - return ; - } - if ([REPOSITORY_KIND, READONLY_REPOSITORY_KIND].includes(kind)) { return ( }> @@ -57,24 +66,35 @@ const ObjectForm = ({ kind, isFilterForm, currentProfiles, ...props }: ObjectFor return ; } + if (kind === ACCOUNT_OBJECT) { + return ; + } + + if (kind === ACCOUNT_GROUP_OBJECT) { + return ; + } + + if (kind === ACCOUNT_ROLE_OBJECT) { + return ; + } + + if (kind === GLOBAL_PERMISSION_OBJECT) { + return ; + } + + if (kind === OBJECT_PERMISSION_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..d631e6c353 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"; @@ -29,7 +30,7 @@ export type PoolSource = { id: string; }; -export type AttributeValueFormPool = { +export type AttributeValueFromPool = { source: PoolSource; value: { from_pool: { id: string } }; }; @@ -51,15 +52,15 @@ export type AttributeValueFromUser = export type FormAttributeValue = | AttributeValueFromUser | AttributeValueFromProfile - | EmptyFieldValue - | AttributeValueFormPool; + | AttributeValueFromPool + | EmptyFieldValue; -export type RelationshipValueFormPool = { +export type RelationshipValueFromPool = { source: PoolSource; value: { id: string } | { from_pool: { id: string } }; }; -export type RelationshipValueFormUser = { +export type RelationshipValueFromUser = { source: { type: SourceType; }; @@ -67,8 +68,8 @@ export type RelationshipValueFormUser = { }; export type FormRelationshipValue = - | RelationshipValueFormUser - | RelationshipValueFormPool + | RelationshipValueFromUser + | RelationshipValueFromPool | EmptyFieldValue; export type FormFieldValue = FormAttributeValue | FormRelationshipValue; @@ -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,8 +115,9 @@ export type DynamicRelationshipFieldProps = Omit peer?: string; parent?: string; options?: SelectOption[]; - relationship: components["schemas"]["RelationshipSchema-Output"]; + relationship: RelationshipSchema; schema: IModelSchema; + peerField?: string; }; export type DynamicFieldProps = @@ -131,7 +129,7 @@ export type DynamicFieldProps = export const isFormFieldValueFromPool = ( fieldData: FormFieldValue -): fieldData is RelationshipValueFormPool => fieldData.source?.type === "pool"; +): fieldData is RelationshipValueFromPool => fieldData.source?.type === "pool"; export type NumberPoolData = { id: string; diff --git a/frontend/app/src/components/form/utils/getFieldDefaultValue.ts b/frontend/app/src/components/form/utils/getFieldDefaultValue.ts index 87399efc5b..2781c381d3 100644 --- a/frontend/app/src/components/form/utils/getFieldDefaultValue.ts +++ b/frontend/app/src/components/form/utils/getFieldDefaultValue.ts @@ -1,13 +1,13 @@ -import { FieldSchema, AttributeType } from "@/utils/getObjectItemDisplayValue"; import { ProfileData } from "@/components/form/object-form"; import { - AttributeValueFormPool, + AttributeValueFromPool, AttributeValueFromProfile, 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; @@ -42,6 +42,7 @@ export const getCurrentFieldValue = ( if (!objectData) return null; const currentField = objectData[fieldName]; + if (!currentField) return null; if (currentField.is_default || currentField.is_from_profile) { @@ -92,7 +93,7 @@ const getDefaultValueFromProfiles = ( const getDefaultValueFromPool = ( fieldName: string, objectData?: Record -): AttributeValueFormPool | null => { +): AttributeValueFromPool | null => { if (!objectData) return null; const currentField = objectData[fieldName]; diff --git a/frontend/app/src/components/form/utils/getFormFieldsFromSchema.ts b/frontend/app/src/components/form/utils/getFormFieldsFromSchema.ts index c2b7836ad7..3600ad12de 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,27 +9,27 @@ 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 { IModelSchema, genericsState, 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; + schema: IModelSchema; profiles?: Array; 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 => { @@ -77,6 +68,7 @@ export const getFormFieldsFromSchema = ({ auth, owner: currentFieldValue?.owner, isProtected: !!currentFieldValue?.is_protected, + permissions: { update: currentFieldValue?.permissions?.update_value }, isReadOnly: attribute.read_only, }), type: attribute.kind as Exclude, @@ -124,8 +116,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 +133,7 @@ export const getFormFieldsFromSchema = ({ field: attribute, schema: schema, unique: attribute.unique, - items: getOptionsFromAttribute(attribute, basicFomFieldProps.defaultValue), + items: attribute.enum, }; return enumField; @@ -168,40 +160,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/isFieldDisabled.ts b/frontend/app/src/components/form/utils/isFieldDisabled.ts index 43e9aab4d3..2e14c464d6 100644 --- a/frontend/app/src/components/form/utils/isFieldDisabled.ts +++ b/frontend/app/src/components/form/utils/isFieldDisabled.ts @@ -1,11 +1,15 @@ import { LineageOwner } from "@/generated/graphql"; import { AuthContextType } from "@/hooks/useAuth"; +import { PermissionDecisionData } from "@/screens/permission/types"; +import { store } from "@/state"; +import { currentBranchAtom } from "@/state/atoms/branches.atom"; export type IsFieldDisabledParams = { owner?: LineageOwner | null; auth?: AuthContextType; isProtected?: boolean; isReadOnly?: boolean; + permissions?: { update?: PermissionDecisionData | null }; }; export const isFieldDisabled = ({ @@ -13,12 +17,27 @@ export const isFieldDisabled = ({ auth, isProtected, isReadOnly, + permissions, }: IsFieldDisabledParams) => { - if (isReadOnly) return true; + const currentBranch = store.get(currentBranchAtom); - // Field is available if there is no owner and if is_protected is not set to true - if (!isProtected || !owner || auth?.permissions?.isAdmin) return false; + switch (permissions?.update) { + case "ALLOW": + return false; + case "ALLOW_DEFAULT": + return !currentBranch?.is_default; + case "ALLOW_OTHER": + return !!currentBranch?.is_default; + case "DENY": + return true; + default: { + if (isReadOnly) return true; - // Field is available only if is_protected is set to true and if the owner is the user - return owner?.id !== auth?.user?.id; + // Field is available if there is no owner and if is_protected is not set to true + if (!isProtected || !owner) return false; + + // Field is available only if is_protected is set to true and if the owner is the user + return owner?.id !== auth?.user?.id; + } + } }; diff --git a/frontend/app/src/components/form/utils/mutations/getCreateMutationFromFormData.ts b/frontend/app/src/components/form/utils/mutations/getCreateMutationFromFormData.ts index 327e6f8d06..227e43e929 100644 --- a/frontend/app/src/components/form/utils/mutations/getCreateMutationFromFormData.ts +++ b/frontend/app/src/components/form/utils/mutations/getCreateMutationFromFormData.ts @@ -46,9 +46,10 @@ export const getCreateMutationFromFormDataOnly = ( if (data.source?.type === "user") { const fieldValue = data.value === "" ? null : data.value; + return { ...acc, - [name]: { value: fieldValue }, + [name]: Array.isArray(fieldValue) ? fieldValue : { value: fieldValue }, }; } diff --git a/frontend/app/src/components/form/utils/updateFormFieldValue.ts b/frontend/app/src/components/form/utils/updateFormFieldValue.ts index 59f73c5156..62c140cc19 100644 --- a/frontend/app/src/components/form/utils/updateFormFieldValue.ts +++ b/frontend/app/src/components/form/utils/updateFormFieldValue.ts @@ -1,15 +1,15 @@ +import { PoolValue } from "@/components/form/pool-selector"; import { - AttributeValueFormPool, + AttributeValueFromPool, FormAttributeValue, FormFieldValue, FormRelationshipValue, - RelationshipValueFormPool, + RelationshipValueFromPool, } from "@/components/form/type"; import { isDeepEqual } from "remeda"; -import { PoolValue } from "@/components/form/pool-selector"; export const updateFormFieldValue = ( - newValue: Exclude["value"], + newValue: Exclude["value"], defaultValue?: FormFieldValue ): FormFieldValue => { if (defaultValue && isDeepEqual(newValue, defaultValue.value as typeof newValue)) { 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..5eb16a2d24 --- /dev/null +++ b/frontend/app/src/components/inputs/dropdown.tsx @@ -0,0 +1,287 @@ +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; + badge?: 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.badge && ( + + {item.badge} + + )} +
+

{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} + + {selectItem?.badge && {selectItem?.badge}} +
+
+ + + + 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..d7002f7928 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; @@ -80,6 +80,7 @@ export type SelectProps = { isUnique?: boolean; isInherited?: boolean; placeholder?: string; + peerField?: string; }; export const Select = forwardRef((props, ref) => { @@ -102,6 +103,7 @@ export const Select = forwardRef((props, ref) => { schema, placeholder, preventEmpty, + peerField, // Field used to build option label // eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars isOptional, // Avoid proving useless props // eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars @@ -154,7 +156,7 @@ export const Select = forwardRef((props, ref) => { // Query to fetch options only if a peer is defined // TODO: Find another solution for queries while loading schema const optionsQueryString = peer - ? getDropdownOptions({ kind: peer, parentFilter }) + ? getDropdownOptions({ kind: peer, parentFilter, peerField }) : "query { ok }"; const poolsQueryString = poolPeer ? getDropdownOptions({ kind: poolPeer }) : "query { ok }"; @@ -171,7 +173,7 @@ export const Select = forwardRef((props, ref) => { const loading = optionsLoading || poolsLoading; const data = hasBeenOpened ? optionsData : poolsData; - const labelQueryString = peer ? getObjectDisplayLabel({ kind: peer }) : "query { ok }"; + const labelQueryString = peer ? getObjectDisplayLabel({ kind: peer, peerField }) : "query { ok }"; const labelQuery = gql` ${labelQueryString} @@ -185,6 +187,7 @@ export const Select = forwardRef((props, ref) => { const optionsList = getOptionsFromRelationship({ options: optionsResult, schemas: schemaList, + peerField, }); const addOption: SelectOption = { @@ -613,7 +616,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 +633,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 +652,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 +680,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 +742,8 @@ export const Select = forwardRef((props, ref) => { } open={open} setOpen={setOpen} - offset={1}> + offset={1} + > {renderContentForEnum()} @@ -793,7 +802,10 @@ export const Select = forwardRef((props, ref) => { const id = selectedOption?.id ?? value?.id ?? value; const { data } = await fetchLabel({ variables: { ids: [id] } }); - const label = data[peer]?.edges[0]?.node?.display_label; + const label = peerField + ? (data[peer]?.edges[0]?.node?.[peerField]?.value ?? + data[peer]?.edges[0]?.node?.[peerField]) + : data[peer]?.edges[0]?.node?.display_label; const newSelectedOption = { ...selectedOption, @@ -818,7 +830,9 @@ export const Select = forwardRef((props, ref) => { const { data } = await fetchLabel({ variables: { ids } }); const newSelectedOptions = data[peer]?.edges.map((edge) => ({ - name: edge.node.display_label, + name: peerField + ? (edge.node?.[peerField]?.value ?? edge.node?.[peerField]) + : edge.node.display_label, id: edge.node.id, })); @@ -867,15 +881,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 +928,8 @@ export const Select = forwardRef((props, ref) => { + onClick={handleFocusPools} + > @@ -923,18 +941,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 +964,8 @@ export const Select = forwardRef((props, ref) => { className={classNames( "block truncate italic text-xs", selected ? "font-semibold" : "" - )}> + )} + > {option.description} )} @@ -962,12 +984,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..2d91edeb4d 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 { 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/notifications.tsx b/frontend/app/src/components/notifications.tsx index cf3dc6deb1..954a3fd2f0 100644 --- a/frontend/app/src/components/notifications.tsx +++ b/frontend/app/src/components/notifications.tsx @@ -12,7 +12,6 @@ export const Notifications = (props: any) => { `; const { data } = useSubscription(query); - console.log("data: ", data); return
OK
; }; diff --git a/frontend/app/src/components/search/search-actions.tsx b/frontend/app/src/components/search/search-actions.tsx index 702318dbea..ee99d22e92 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 { MenuItem } from "@/screens/layout/menu-navigation/types"; +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"; @@ -17,8 +17,8 @@ export const SearchActions = ({ query }: SearchProps) => { const menuItems = useAtomValue(menuFlatAtom); const queryLowerCased = query.toLowerCase(); - const resultsMenu = menuItems.filter(({ title }) => - title.toLowerCase().includes(queryLowerCased) + const resultsMenu = menuItems.filter(({ label }) => + label.toLowerCase().includes(queryLowerCased) ); const resultsSchema = models.filter( ({ kind, label, description }) => @@ -55,7 +55,7 @@ const ActionOnMenu = ({ menuItem }: ActionOnMenuProps) => { Menu - {menuItem.title} + {menuItem.label} ); }; diff --git a/frontend/app/src/components/search/search-anywhere.tsx b/frontend/app/src/components/search/search-anywhere.tsx index 9743a1ecb6..f69f2a367d 100644 --- a/frontend/app/src/components/search/search-anywhere.tsx +++ b/frontend/app/src/components/search/search-anywhere.tsx @@ -1,57 +1,47 @@ +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 { CollapsedButton } from "@/screens/layout/menu-navigation/components/collapsed-button"; import { classNames } from "@/utils/common"; -import { Combobox, Dialog, Transition } from "@headlessui/react"; +import { Combobox, Dialog } from "@headlessui/react"; import { Icon } from "@iconify-icon/react"; -import { - ChangeEventHandler, - Fragment, - MouseEventHandler, - ReactNode, - forwardRef, - useEffect, - useState, -} from "react"; +import { 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 ( -
-
- } + + @@ -47,12 +42,14 @@ const BranchesItems = () => {
    + data-testid="branches-items" + > {branches.map((branch) => (
  • navigate(constructPath(`/branches/${branch.name}`))}> + className="col-span-1 rounded-lg border cursor-pointer bg-gray-50 hover:bg-gray-100" + onClick={() => navigate(constructPath(`/branches/${branch.name}`))} + >
    @@ -103,7 +100,7 @@ const BranchesItems = () => {
  • ))}
- +
); }; diff --git a/frontend/app/src/screens/branches/diff.tsx b/frontend/app/src/screens/branches/diff.tsx index 22f422072d..0ad6ad100b 100644 --- a/frontend/app/src/screens/branches/diff.tsx +++ b/frontend/app/src/screens/branches/diff.tsx @@ -1,12 +1,12 @@ +import DatetimeField from "@/components/form/fields/datetime.field"; import { Tabs } from "@/components/tabs"; +import { Form, FormSubmit } from "@/components/ui/form"; +import { DIFF_TABS } from "@/config/constants"; import { QSP } from "@/config/qsp"; import { DateTimeParam, StringParam, useQueryParam } from "use-query-params"; import { ArtifactsDiff } from "../diff/artifact-diff/artifacts-diff"; -import { NodeDiff } from "../diff/node-diff"; import { FilesDiff } from "../diff/file-diff/files-diff"; -import { Form, FormSubmit } from "@/components/ui/form"; -import DatetimeField from "@/components/form/fields/datetime.field"; -import { DIFF_TABS } from "@/config/constants"; +import { NodeDiff } from "../diff/node-diff"; const tabs = [ { diff --git a/frontend/app/src/screens/diff/artifact-diff/artifact-content-diff.tsx b/frontend/app/src/screens/diff/artifact-diff/artifact-content-diff.tsx index 9d3be72cb0..382a2b954c 100644 --- a/frontend/app/src/screens/diff/artifact-diff/artifact-content-diff.tsx +++ b/frontend/app/src/screens/diff/artifact-diff/artifact-content-diff.tsx @@ -325,7 +325,8 @@ export const ArtifactContentDiff = (props: any) => { [changeKey]: change?.comments?.map((comment: any, index: number) => (
+ className="bg-custom-white p-4 border border-custom-blue-500 rounded-md m-2" + > {comment.message}
)), @@ -347,7 +348,7 @@ export const ArtifactContentDiff = (props: any) => { itemNew?.storage_id ); - if (thread || !auth?.permissions?.write || !proposedChangeId) { + if (thread || !auth?.isAuthenticated || !proposedChangeId) { // Do not display the add button if there is already a thread return wrapInAnchor(renderDefault()); } @@ -359,7 +360,8 @@ export const ArtifactContentDiff = (props: any) => { {inHoverState && ( )} @@ -413,7 +415,8 @@ export const ArtifactContentDiff = (props: any) => { diffType={fileContent.type} renderGutter={renderGutter} widgets={getWidgets(fileContent.hunks)} - optimizeSelection> + optimizeSelection + > {(hunks) => hunks.map((hunk) => )}
diff --git a/frontend/app/src/screens/diff/artifact-diff/artifact-repo-diff.tsx b/frontend/app/src/screens/diff/artifact-diff/artifact-repo-diff.tsx index 5dacc62772..0eb0408d10 100644 --- a/frontend/app/src/screens/diff/artifact-diff/artifact-repo-diff.tsx +++ b/frontend/app/src/screens/diff/artifact-diff/artifact-repo-diff.tsx @@ -3,8 +3,8 @@ import { Badge } from "@/components/display/badge"; import { getArtifactDetails } from "@/graphql/queries/getArtifacts"; import useQuery from "@/hooks/useQuery"; import ErrorScreen from "@/screens/errors/error-screen"; -import LoadingScreen from "@/screens/loading-screen/loading-screen"; import NoDataFound from "@/screens/errors/no-data-found"; +import LoadingScreen from "@/screens/loading-screen/loading-screen"; import { schemaState } from "@/state/atoms/schema.atom"; import { gql } from "@apollo/client"; import { useAtom } from "jotai"; diff --git a/frontend/app/src/screens/diff/checks/check.tsx b/frontend/app/src/screens/diff/checks/check.tsx index 6bd4ac524a..25f92a94e9 100644 --- a/frontend/app/src/screens/diff/checks/check.tsx +++ b/frontend/app/src/screens/diff/checks/check.tsx @@ -150,7 +150,8 @@ export const Check = ({ id }: tCheckProps) => { className={classNames( "flex flex-col rounded-md p-2 bg-gray-50 border border-l-4", getCheckBorderColor(severity?.value) - )}> + )} + >
diff --git a/frontend/app/src/screens/diff/checks/checks-summary.tsx b/frontend/app/src/screens/diff/checks/checks-summary.tsx index 7e505d3da7..40648ec6fa 100644 --- a/frontend/app/src/screens/diff/checks/checks-summary.tsx +++ b/frontend/app/src/screens/diff/checks/checks-summary.tsx @@ -1,3 +1,4 @@ +import { Button } from "@/components/buttons/button-primitive"; import { Retry } from "@/components/buttons/retry"; import { PieChart } from "@/components/display/pie-chart"; import { ALERT_TYPES, Alert } from "@/components/ui/alert"; @@ -5,7 +6,6 @@ import { CHECKS_LABEL, PROPOSED_CHANGES_VALIDATOR_OBJECT, VALIDATIONS_ENUM_MAP, - VALIDATION_STATES, } from "@/config/constants"; import graphqlClient from "@/graphql/graphqlClientApollo"; import { runCheck } from "@/graphql/mutations/diff/runCheck"; @@ -14,7 +14,9 @@ import LoadingScreen from "@/screens/loading-screen/loading-screen"; import { genericsState } from "@/state/atoms/schema.atom"; import { schemaKindLabelState } from "@/state/atoms/schemaKindLabel.atom"; import { getValidatorsStats } from "@/utils/checks"; +import { classNames } from "@/utils/common"; import { gql } from "@apollo/client"; +import { Icon } from "@iconify-icon/react"; import { useAtomValue } from "jotai"; import { useParams } from "react-router-dom"; import { toast } from "react-toastify"; @@ -31,7 +33,7 @@ export const ChecksSummary = (props: tChecksSummaryProps) => { const { proposedChangeId } = useParams(); const schemaKindLabel = useAtomValue(schemaKindLabelState); const schemaList = useAtomValue(genericsState); - const auth = useAuth(); + const { isAuthenticated } = useAuth(); const schemaData = schemaList.find((s) => s.kind === PROPOSED_CHANGES_VALIDATOR_OBJECT); @@ -43,10 +45,6 @@ export const ChecksSummary = (props: tChecksSummaryProps) => { return { ...acc, [kind]: getValidatorsStats(relatedValidators) }; }, {}); - const validatorsInProgress = validators.filter( - (validator: any) => validator?.state?.value === VALIDATION_STATES.IN_PROGRESS - ); - const handleRetry = async (validator: string) => { const runParams = { id: proposedChangeId, @@ -85,13 +83,15 @@ export const ChecksSummary = (props: tChecksSummaryProps) => {
- Retry all: - - handleRetry("all")} - isLoading={isLoading || !!validatorsInProgress.length} - isDisabled={!auth?.permissions?.write} - /> + disabled={!isAuthenticated} + variant="ghost" + className="gap-1 hover:bg-neutral-200" + > + Retry all + +
diff --git a/frontend/app/src/screens/diff/checks/checks.tsx b/frontend/app/src/screens/diff/checks/checks.tsx index e793f6c42c..e814c4bfcf 100644 --- a/frontend/app/src/screens/diff/checks/checks.tsx +++ b/frontend/app/src/screens/diff/checks/checks.tsx @@ -26,7 +26,7 @@ export const Checks = forwardRef((props, ref) => { } return ( -
+
diff --git a/frontend/app/src/screens/diff/checks/conflict.tsx b/frontend/app/src/screens/diff/checks/conflict.tsx index 4969b7d469..0c0da0dfee 100644 --- a/frontend/app/src/screens/diff/checks/conflict.tsx +++ b/frontend/app/src/screens/diff/checks/conflict.tsx @@ -149,7 +149,8 @@ export const Conflict = (props: any) => { return (
+ className={classNames("flex-1 grid grid-cols-2 gap-2 p-2 rounded-md", className)} + >
{branch} diff --git a/frontend/app/src/screens/diff/checks/validator-checks-counter.tsx b/frontend/app/src/screens/diff/checks/validator-checks-counter.tsx deleted file mode 100644 index 00782dd8d0..0000000000 --- a/frontend/app/src/screens/diff/checks/validator-checks-counter.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { getChecksStats } from "@/utils/checks"; - -type tValidatorChecksCounterProps = { - checks: any[]; -}; - -export const ValidatorChecksCounter = (props: tValidatorChecksCounterProps) => { - const { checks } = props; - - const checksStats = getChecksStats(checks); - - const isEmpty = !Object.values(checksStats).filter(Boolean).length; - - if (isEmpty) { - return ( -
-
0
-
- ); - } - - return
ok
; -}; diff --git a/frontend/app/src/screens/diff/checks/validator-checks-progress.tsx b/frontend/app/src/screens/diff/checks/validator-checks-progress.tsx deleted file mode 100644 index f8a0853ddc..0000000000 --- a/frontend/app/src/screens/diff/checks/validator-checks-progress.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import { getChecksStats } from "@/utils/checks"; - -type tValidatorChecksProgressProps = { - checks: any[]; -}; - -const getCheckBar = (type: string, amount: number, total: number, index: number) => { - const precentage = Math.floor((amount / total) * 100); - - switch (type) { - case "total": { - return null; - } - case "success": { - return ( -
- {amount} -
- ); - } - case "info": { - return ( -
- {amount} -
- ); - } - case "warning": { - return ( -
- {amount} -
- ); - } - case "error": { - return ( -
- {amount} -
- ); - } - case "critical": { - return ( -
- {amount} -
- ); - } - default: { - return ( -
- {amount} -
- ); - } - } -}; - -export const ValidatorChecksProgress = (props: tValidatorChecksProgressProps) => { - const { checks } = props; - - const checksStats = getChecksStats(checks); - - const isEmpty = !Object.values(checksStats).filter(Boolean).length; - - if (isEmpty) { - return ( -
-
0
-
- ); - } - - return ( -
- {Object.entries(checksStats).map(([type, amount], index) => - getCheckBar(type, amount, checksStats.total, index) - )} -
- ); -}; diff --git a/frontend/app/src/screens/diff/diff-badge.tsx b/frontend/app/src/screens/diff/diff-badge.tsx index 9b6d890329..8b45446b0d 100644 --- a/frontend/app/src/screens/diff/diff-badge.tsx +++ b/frontend/app/src/screens/diff/diff-badge.tsx @@ -33,7 +33,8 @@ export const BadgeUnchanged = ({ hasConflicts && "border-none p-0 pl-1 gap-1", className )} - {...props}> + {...props} + > {(children || children === 0) && {children}} {hasConflicts && } @@ -103,7 +104,8 @@ const CloseBadge = ({ className }: CloseBadgeProps) => { className={classNames( "flex justify-center items-center absolute border-2 border-white -top-2 -right-2 rounded-full", className - )}> + )} + >
); diff --git a/frontend/app/src/screens/diff/diff-tree.tsx b/frontend/app/src/screens/diff/diff-tree.tsx index 9ce8d1c36e..837c94ea6d 100644 --- a/frontend/app/src/screens/diff/diff-tree.tsx +++ b/frontend/app/src/screens/diff/diff-tree.tsx @@ -1,13 +1,13 @@ +import { Tooltip } from "@/components/ui/tooltip"; import { Tree, TreeItemProps, TreeProps } from "@/components/ui/tree"; -import { useEffect, useState } from "react"; -import { addItemsToTree, EMPTY_TREE } from "@/screens/ipam/ipam-tree/utils"; -import { DiffBadge } from "@/screens/diff/node-diff/utils"; +import { useSchema } from "@/hooks/useSchema"; import { DiffNode } from "@/screens/diff/node-diff/types"; -import { useLocation, useNavigate } from "react-router-dom"; +import { DiffBadge } from "@/screens/diff/node-diff/utils"; import { TREE_ROOT_ID } from "@/screens/ipam/constants"; -import { useSchema } from "@/hooks/useSchema"; +import { EMPTY_TREE, addItemsToTree } from "@/screens/ipam/ipam-tree/utils"; import { Icon } from "@iconify-icon/react"; -import { Tooltip } from "@/components/ui/tooltip"; +import { useEffect, useState } from "react"; +import { useLocation, useNavigate } from "react-router-dom"; interface DiffTreeProps extends Omit { nodes: Array; @@ -65,7 +65,8 @@ const DiffTreeItem = ({ element }: TreeItemProps) => { href={"#" + diffNode?.uuid} tabIndex={-1} className="flex items-center gap-2 text-gray-800 overflow-hidden" - data-testid="hierarchical-tree-item"> + data-testid="hierarchical-tree-item" + > { }; export const formatDiffNodesToDiffTree = (nodes: Array) => { - return nodes.reduce((acc, node) => { - const newNode = { - id: node.uuid, - name: node.label, - parent: TREE_ROOT_ID as string, - children: acc.filter(({ parent }) => parent === node.uuid).map(({ id }) => id), - metadata: { - kind: node.kind, // for icon on tree item - uuid: node.uuid, // for url - status: node.status, // for icon color - containsConflicts: node.contains_conflict, // for icon conflicts - }, - }; - - if (node.parent) { - const { uuid: parentUUID, relationship_name: parentRelationshipName } = node.parent; - - const parentNodeId = parentUUID + parentRelationshipName; - const newNodeWithParent = { - ...newNode, - parent: parentNodeId, + return nodes.reduce( + (acc, node) => { + const newNode = { + id: node.uuid, + name: node.label, + parent: TREE_ROOT_ID as string, + children: acc.filter(({ parent }) => parent === node.uuid).map(({ id }) => id), + metadata: { + kind: node.kind, // for icon on tree item + uuid: node.uuid, // for url + status: node.status, // for icon color + containsConflicts: node.contains_conflict, // for icon conflicts + }, }; - const existingParentOfNewNode = acc.find(({ id }) => id === parentNodeId); - if (existingParentOfNewNode) { + if (node.parent) { + const { uuid: parentUUID, relationship_name: parentRelationshipName } = node.parent; + + const parentNodeId = parentUUID + parentRelationshipName; + const newNodeWithParent = { + ...newNode, + parent: parentNodeId, + }; + + const existingParentOfNewNode = acc.find(({ id }) => id === parentNodeId); + if (existingParentOfNewNode) { + return acc + .map((accNode) => { + if (accNode.id === parentNodeId) { + return { + ...accNode, + children: [...new Set(accNode.children.concat(newNodeWithParent.id))], + }; + } + + return accNode; + }) + .concat(newNodeWithParent); + } + + const newParentNode = { + id: parentNodeId, + name: parentRelationshipName ?? "", + parent: parentUUID, + children: [newNode.id], + metadata: { + kind: node.parent.kind, // for icon on tree item + }, + }; + return acc .map((accNode) => { - if (accNode.id === parentNodeId) { + if (accNode.id === parentUUID) { return { ...accNode, - children: [...new Set(accNode.children.concat(newNodeWithParent.id))], + children: [...new Set(accNode.children.concat(newParentNode.id))], }; } return accNode; }) - .concat(newNodeWithParent); + .concat(newParentNode, newNodeWithParent); } - const newParentNode = { - id: parentNodeId, - name: parentRelationshipName ?? "", - parent: parentUUID, - children: [newNode.id], - metadata: { - kind: node.parent.kind, // for icon on tree item - }, - }; - - return acc - .map((accNode) => { - if (accNode.id === parentUUID) { - return { - ...accNode, - children: [...new Set(accNode.children.concat(newParentNode.id))], - }; - } - - return accNode; - }) - .concat(newParentNode, newNodeWithParent); - } - - return [...acc, newNode]; - }, [] as TreeProps["data"]); + return [...acc, newNode]; + }, + [] as TreeProps["data"] + ); }; export const generateRootCategoryNodeForDiffTree = ( diffTreeNodes: TreeProps["data"] ): TreeProps["data"] => { - return diffTreeNodes.reduce((acc, node) => { - const nodeKind = node.metadata?.kind as string | undefined; - if (node.parent !== TREE_ROOT_ID || !nodeKind) return [...acc, node]; - - const nodeUpdated = { - ...node, - parent: nodeKind, - }; - - const existingRootCategoryNode = acc.find(({ id }) => id === nodeKind); - - if (existingRootCategoryNode) { - const rootCategoryNodeUpdated = { - ...existingRootCategoryNode, - children: [...existingRootCategoryNode.children, node.id], + return diffTreeNodes.reduce( + (acc, node) => { + const nodeKind = node.metadata?.kind as string | undefined; + if (node.parent !== TREE_ROOT_ID || !nodeKind) return [...acc, node]; + + const nodeUpdated = { + ...node, + parent: nodeKind, }; - return acc - .map((item) => (item.id === existingRootCategoryNode.id ? rootCategoryNodeUpdated : item)) - .concat(nodeUpdated); - } else { - const newRootCategoryNode = { - id: nodeKind, - name: nodeKind, - parent: TREE_ROOT_ID, - children: [node.id], - isBranch: true, - metadata: { - kind: nodeKind, - }, - }; + const existingRootCategoryNode = acc.find(({ id }) => id === nodeKind); + + if (existingRootCategoryNode) { + const rootCategoryNodeUpdated = { + ...existingRootCategoryNode, + children: [...existingRootCategoryNode.children, node.id], + }; - return [...acc, newRootCategoryNode, nodeUpdated]; - } - }, [] as TreeProps["data"]); + return acc + .map((item) => (item.id === existingRootCategoryNode.id ? rootCategoryNodeUpdated : item)) + .concat(nodeUpdated); + } else { + const newRootCategoryNode = { + id: nodeKind, + name: nodeKind, + parent: TREE_ROOT_ID, + children: [node.id], + isBranch: true, + metadata: { + kind: nodeKind, + }, + }; + + return [...acc, newRootCategoryNode, nodeUpdated]; + } + }, + [] as TreeProps["data"] + ); }; diff --git a/frontend/app/src/screens/diff/file-diff/file-content-diff.tsx b/frontend/app/src/screens/diff/file-diff/file-content-diff.tsx index 17b0039ded..073b43322b 100644 --- a/frontend/app/src/screens/diff/file-diff/file-content-diff.tsx +++ b/frontend/app/src/screens/diff/file-diff/file-content-diff.tsx @@ -350,7 +350,8 @@ export const FileContentDiff = (props: any) => { [changeKey]: change?.comments?.map((comment: any, index: number) => (
+ className="bg-custom-white p-4 border border-custom-blue-500 rounded-md m-2" + > {comment.message}
)), @@ -367,7 +368,7 @@ export const FileContentDiff = (props: any) => { const thread = findThreadByChange(threads, change, commitFrom, commitTo); - if (thread || !auth?.permissions?.write || !proposedChangeId) { + if (thread || !auth?.isAuthenticated || !proposedChangeId) { // Do not display the add button if there is already a thread return wrapInAnchor(renderDefault()); } @@ -379,7 +380,8 @@ export const FileContentDiff = (props: any) => { {inHoverState && ( )} @@ -430,7 +432,8 @@ export const FileContentDiff = (props: any) => { diffType={fileContent.type} renderGutter={renderGutter} widgets={getWidgets(fileContent.hunks)} - optimizeSelection> + optimizeSelection + > {(hunks) => hunks.map((hunk) => )}
diff --git a/frontend/app/src/screens/diff/node-diff/conflict.tsx b/frontend/app/src/screens/diff/node-diff/conflict.tsx index eb6d944c3f..ed379bf317 100644 --- a/frontend/app/src/screens/diff/node-diff/conflict.tsx +++ b/frontend/app/src/screens/diff/node-diff/conflict.tsx @@ -3,7 +3,7 @@ import { ALERT_TYPES, Alert } from "@/components/ui/alert"; import { Badge } from "@/components/ui/badge"; import graphqlClient from "@/graphql/graphqlClientApollo"; import { resolveConflict } from "@/graphql/mutations/diff/resolveConflict"; -import { usePermission } from "@/hooks/usePermission"; +import { useAuth } from "@/hooks/useAuth"; import LoadingScreen from "@/screens/loading-screen/loading-screen"; import { currentBranchAtom } from "@/state/atoms/branches.atom"; import { proposedChangedState } from "@/state/atoms/proposedChanges.atom"; @@ -17,10 +17,11 @@ import { toast } from "react-toastify"; export const Conflict = ({ conflict }: any) => { const currentBranch = useAtomValue(currentBranchAtom); const date = useAtomValue(datetimeAtom); - const permission = usePermission(); const [isLoading, setIsLoading] = useState(false); const proposedChangesDetails = useAtomValue(proposedChangedState); + const { isAuthenticated } = useAuth(); + const handleAccept = async (conflictValue: string) => { try { setIsLoading(true); @@ -66,7 +67,7 @@ export const Conflict = ({ conflict }: any) => {
handleAccept("BASE_BRANCH")} /> @@ -74,7 +75,8 @@ export const Conflict = ({ conflict }: any) => { htmlFor={"base"} className={ conflict.selected_branch === "BASE_BRANCH" ? "cursor-default" : "cursor-pointer" - }> + } + > {proposedChangesDetails.destination_branch?.value} @@ -85,7 +87,7 @@ export const Conflict = ({ conflict }: any) => {
handleAccept("DIFF_BRANCH")} /> @@ -93,7 +95,8 @@ export const Conflict = ({ conflict }: any) => { htmlFor={"diff"} className={ conflict.selected_branch === "DIFF_BRANCH" ? "cursor-default" : "cursor-pointer" - }> + } + > {proposedChangesDetails.source_branch?.value} diff --git a/frontend/app/src/screens/diff/node-diff/index.tsx b/frontend/app/src/screens/diff/node-diff/index.tsx index 4f12290304..5966fa4bf9 100644 --- a/frontend/app/src/screens/diff/node-diff/index.tsx +++ b/frontend/app/src/screens/diff/node-diff/index.tsx @@ -1,35 +1,34 @@ +import { Button, ButtonProps } from "@/components/buttons/button-primitive"; +import { DateDisplay } from "@/components/display/date-display"; +import { ALERT_TYPES, Alert } from "@/components/ui/alert"; +import { Badge } from "@/components/ui/badge"; +import { Tooltip } from "@/components/ui/tooltip"; import { PROPOSED_CHANGES_OBJECT_THREAD_OBJECT } from "@/config/constants"; +import { QSP } from "@/config/qsp"; +import graphqlClient from "@/graphql/graphqlClientApollo"; +import { rebaseBranch } from "@/graphql/mutations/branches/rebaseBranch"; +import { DIFF_UPDATE } from "@/graphql/mutations/proposed-changes/diff/diff-update"; +import { getProposedChangesDiffTree } from "@/graphql/queries/proposed-changes/getProposedChangesDiffTree"; +import { useAuth } from "@/hooks/useAuth"; import useQuery from "@/hooks/useQuery"; +import DiffTree from "@/screens/diff/diff-tree"; +import { DIFF_STATUS, DiffNode as DiffNodeType } from "@/screens/diff/node-diff/types"; +import { DiffBadge } from "@/screens/diff/node-diff/utils"; +import ErrorScreen from "@/screens/errors/error-screen"; import LoadingScreen from "@/screens/loading-screen/loading-screen"; import { proposedChangedState } from "@/state/atoms/proposedChanges.atom"; import { schemaState } from "@/state/atoms/schema.atom"; +import { datetimeAtom } from "@/state/atoms/time.atom"; +import { classNames, objectToString } from "@/utils/common"; +import { formatFullDate, formatRelativeTimeFromNow } from "@/utils/date"; +import { NetworkStatus, gql, useMutation } from "@apollo/client"; +import { Icon } from "@iconify-icon/react"; import { useAtomValue } from "jotai"; import { createContext, useState } from "react"; +import { toast } from "react-toastify"; +import { StringParam, useQueryParam } from "use-query-params"; import { DiffFilter, ProposedChangeDiffFilter } from "../../proposed-changes/diff-filter"; -import { getProposedChangesDiffTree } from "@/graphql/queries/proposed-changes/getProposedChangesDiffTree"; import { DiffNode } from "./node"; -import { StringParam, useQueryParam } from "use-query-params"; -import { QSP } from "@/config/qsp"; -import { Card } from "@/components/ui/card"; -import DiffTree from "@/screens/diff/diff-tree"; -import { Button, ButtonProps } from "@/components/buttons/button-primitive"; -import { rebaseBranch } from "@/graphql/mutations/branches/rebaseBranch"; -import { classNames, objectToString } from "@/utils/common"; -import graphqlClient from "@/graphql/graphqlClientApollo"; -import { datetimeAtom } from "@/state/atoms/time.atom"; -import { gql, NetworkStatus, useMutation } from "@apollo/client"; -import { toast } from "react-toastify"; -import { Alert, ALERT_TYPES } from "@/components/ui/alert"; -import { DIFF_UPDATE } from "@/graphql/mutations/proposed-changes/diff/diff-update"; -import { useAuth } from "@/hooks/useAuth"; -import { DateDisplay } from "@/components/display/date-display"; -import { Icon } from "@iconify-icon/react"; -import { DIFF_STATUS, DiffNode as DiffNodeType } from "@/screens/diff/node-diff/types"; -import { formatFullDate, formatRelativeTimeFromNow } from "@/utils/date"; -import { Tooltip } from "@/components/ui/tooltip"; -import { DiffBadge } from "@/screens/diff/node-diff/utils"; -import { Badge } from "@/components/ui/badge"; -import ErrorScreen from "@/screens/errors/error-screen"; export const DiffContext = createContext({}); @@ -55,7 +54,7 @@ const buildFilters = (filters: DiffFilter, qsp?: String | null) => { }; export const NodeDiff = ({ branchName, filters }: NodeDiffProps) => { - const auth = useAuth(); + const { isAuthenticated } = useAuth(); const [qspStatus] = useQueryParam(QSP.STATUS, StringParam); const date = useAtomValue(datetimeAtom); const proposedChangesDetails = useAtomValue(proposedChangedState); @@ -162,7 +161,7 @@ export const NodeDiff = ({ branchName, filters }: NodeDiffProps) => {
@@ -192,7 +191,7 @@ export const NodeDiff = ({ branchName, filters }: NodeDiffProps) => {
@@ -209,7 +208,7 @@ export const NodeDiff = ({ branchName, filters }: NodeDiffProps) => { return (
-
+
@@ -225,7 +224,8 @@ export const NodeDiff = ({ branchName, filters }: NodeDiffProps) => { size="sm" variant="primary" onClick={handleRefresh} - disabled={!auth?.permissions?.write || isLoadingUpdate}> + disabled={!isAuthenticated || isLoadingUpdate} + > Refresh diff
@@ -234,18 +234,19 @@ export const NodeDiff = ({ branchName, filters }: NodeDiffProps) => { size="sm" variant="primary-outline" onClick={handleRebase} - disabled={isLoadingUpdate}> + disabled={isLoadingUpdate} + > Rebase
- - - +
+ +
-
+
{nodes.length === 0 && qspStatus && (
diff --git a/frontend/app/src/screens/diff/node-diff/node-attribute.tsx b/frontend/app/src/screens/diff/node-diff/node-attribute.tsx index 2f646e4891..fe16b79bf6 100644 --- a/frontend/app/src/screens/diff/node-diff/node-attribute.tsx +++ b/frontend/app/src/screens/diff/node-diff/node-attribute.tsx @@ -1,10 +1,10 @@ -import { useParams } from "react-router-dom"; -import { DiffRow } from "@/screens/diff/node-diff/utils"; -import { DiffAttribute, DiffStatus } from "@/screens/diff/node-diff/types"; -import { DiffThread } from "@/screens/diff/node-diff/thread"; import { DiffNodeProperty } from "@/screens/diff/node-diff/node-property"; -import { Conflict } from "./conflict"; +import { DiffThread } from "@/screens/diff/node-diff/thread"; +import { DiffAttribute, DiffStatus } from "@/screens/diff/node-diff/types"; +import { DiffRow } from "@/screens/diff/node-diff/utils"; +import { useParams } from "react-router-dom"; import { BadgeConflict } from "../diff-badge"; +import { Conflict } from "./conflict"; type DiffNodeAttributeProps = { attribute: DiffAttribute; @@ -38,7 +38,8 @@ export const DiffNodeAttribute = ({
} left={previousValue} - right={newValue}> + right={newValue} + >
{attribute.conflict && } diff --git a/frontend/app/src/screens/diff/node-diff/node-property.tsx b/frontend/app/src/screens/diff/node-diff/node-property.tsx index f10b7b9f49..b81181307c 100644 --- a/frontend/app/src/screens/diff/node-diff/node-property.tsx +++ b/frontend/app/src/screens/diff/node-diff/node-property.tsx @@ -1,12 +1,12 @@ import { Badge } from "@/components/ui/badge"; -import { useParams } from "react-router-dom"; -import { DiffThread } from "./thread"; -import { Icon } from "@iconify-icon/react"; -import { Conflict } from "./conflict"; -import { DiffRow, formatPropertyName, formatValue } from "@/screens/diff/node-diff/utils"; import { BadgeConflict } from "@/screens/diff/diff-badge"; import { DiffProperty, DiffStatus } from "@/screens/diff/node-diff/types"; +import { DiffRow, formatPropertyName, formatValue } from "@/screens/diff/node-diff/utils"; import { classNames } from "@/utils/common"; +import { Icon } from "@iconify-icon/react"; +import { useParams } from "react-router-dom"; +import { Conflict } from "./conflict"; +import { DiffThread } from "./thread"; type DiffNodePropertyProps = { className?: string; @@ -77,7 +77,8 @@ export const DiffNodeProperty = ({ status, property, className }: DiffNodeProper left={getPreviousValue(property)} leftClassName="font-normal" rightClassName="font-normal" - right={getNewValue(property)}> + right={getNewValue(property)} + > {property.conflict && } ); diff --git a/frontend/app/src/screens/diff/node-diff/node-relationship-element.tsx b/frontend/app/src/screens/diff/node-diff/node-relationship-element.tsx index 62f2b1eb45..6e8cbc5f75 100644 --- a/frontend/app/src/screens/diff/node-diff/node-relationship-element.tsx +++ b/frontend/app/src/screens/diff/node-diff/node-relationship-element.tsx @@ -1,9 +1,9 @@ -import { DiffNodeProperty } from "./node-property"; -import { DiffRelationshipElement, DiffStatus } from "@/screens/diff/node-diff/types"; -import { DiffBadge, DiffRow } from "@/screens/diff/node-diff/utils"; import { BadgeConflict } from "@/screens/diff/diff-badge"; import { DiffThread } from "@/screens/diff/node-diff/thread"; +import { DiffRelationshipElement, DiffStatus } from "@/screens/diff/node-diff/types"; +import { DiffBadge, DiffRow } from "@/screens/diff/node-diff/utils"; import { useParams } from "react-router-dom"; +import { DiffNodeProperty } from "./node-property"; type DiffNodeElementProps = { element: DiffRelationshipElement; @@ -28,7 +28,8 @@ export const DiffNodeRelationshipElement = ({ element, status }: DiffNodeElement
} right={element.status === "ADDED" && element.peer_label} - left={element.status === "REMOVED" && element.peer_label}> + left={element.status === "REMOVED" && element.peer_label} + >
{element.properties .filter((property) => property.status !== "UNCHANGED") diff --git a/frontend/app/src/screens/diff/node-diff/node-relationship.tsx b/frontend/app/src/screens/diff/node-diff/node-relationship.tsx index 2e65edb536..7b6b983603 100644 --- a/frontend/app/src/screens/diff/node-diff/node-relationship.tsx +++ b/frontend/app/src/screens/diff/node-diff/node-relationship.tsx @@ -1,10 +1,10 @@ -import { DiffNodeRelationshipElement } from "./node-relationship-element"; -import { useParams } from "react-router-dom"; +import { Badge } from "@/components/ui/badge"; import { DiffThread } from "@/screens/diff/node-diff/thread"; -import { DiffRow } from "@/screens/diff/node-diff/utils"; import { DiffRelationship, DiffStatus } from "@/screens/diff/node-diff/types"; -import { Badge } from "@/components/ui/badge"; +import { DiffRow } from "@/screens/diff/node-diff/utils"; import { Icon } from "@iconify-icon/react"; +import { useParams } from "react-router-dom"; +import { DiffNodeRelationshipElement } from "./node-relationship-element"; type DiffNodeRelationshipProps = { relationship: DiffRelationship; @@ -51,7 +51,8 @@ export const DiffNodeRelationship = ({ status, relationship }: DiffNodeRelations )}
- }> + } + >
{relationship.elements.map((element, index: number) => ( diff --git a/frontend/app/src/screens/diff/node-diff/node.tsx b/frontend/app/src/screens/diff/node-diff/node.tsx index 7c737e5780..a95d6c899a 100644 --- a/frontend/app/src/screens/diff/node-diff/node.tsx +++ b/frontend/app/src/screens/diff/node-diff/node.tsx @@ -1,18 +1,18 @@ import Accordion from "@/components/display/accordion"; -import { Card } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; -import { DiffNodeRelationship } from "./node-relationship"; -import { DiffNodeAttribute } from "./node-attribute"; -import { DiffThread } from "./thread"; -import { useLocation, useParams } from "react-router-dom"; +import { Card } from "@/components/ui/card"; +import type { DiffNode as DiffNodeType, PropertyType } from "@/screens/diff/node-diff/types"; import { DiffBadge } from "@/screens/diff/node-diff/utils"; -import { useAtomValue } from "jotai"; import { schemaKindNameState } from "@/state/atoms/schemaKindName.atom"; -import type { DiffNode as DiffNodeType, PropertyType } from "@/screens/diff/node-diff/types"; import { classNames } from "@/utils/common"; -import { useEffect, useRef } from "react"; import { Icon } from "@iconify-icon/react"; +import { useAtomValue } from "jotai"; +import { useEffect, useRef } from "react"; +import { useLocation, useParams } from "react-router-dom"; +import { DiffNodeAttribute } from "./node-attribute"; import { getNewValue, getPreviousValue } from "./node-property"; +import { DiffNodeRelationship } from "./node-relationship"; +import { DiffThread } from "./thread"; type DiffNodeProps = { node: DiffNodeType; @@ -38,7 +38,8 @@ export const DiffNode = ({ sourceBranch, destinationBranch, node }: DiffNodeProp + className={classNames(isSelectedOnNavigation && "ring-2 ring-custom-blue-500")} + > {(!!node.attributes?.length || !!node.relationships?.length) && ( }
} - className="bg-gray-100 border rounded-md"> + className="bg-gray-100 border rounded-md" + >
diff --git a/frontend/app/src/screens/diff/node-diff/thread.tsx b/frontend/app/src/screens/diff/node-diff/thread.tsx index 48b6cf0091..3e2f28a914 100644 --- a/frontend/app/src/screens/diff/node-diff/thread.tsx +++ b/frontend/app/src/screens/diff/node-diff/thread.tsx @@ -3,7 +3,6 @@ import SlideOver from "@/components/display/slide-over"; import { Tooltip } from "@/components/ui/tooltip"; import { PROPOSED_CHANGES_OBJECT_THREAD_OBJECT } from "@/config/constants"; import { getProposedChangesObjectThreads } from "@/graphql/queries/proposed-changes/getProposedChangesObjectThreads"; -import { useAuth } from "@/hooks/useAuth"; import useQuery from "@/hooks/useQuery"; import { schemaState } from "@/state/atoms/schema.atom"; import { getThreadLabel, getThreadTitle } from "@/utils/diff"; @@ -12,8 +11,9 @@ import { useAtom } from "jotai"; import { useContext, useState } from "react"; import { useParams } from "react-router-dom"; -import { Icon } from "@iconify-icon/react"; import { Button } from "@/components/buttons/button-primitive"; +import { getPermission } from "@/screens/permission/utils"; +import { Icon } from "@iconify-icon/react"; import { DiffContext } from "."; import { DiffComments } from "./comments"; @@ -24,7 +24,6 @@ type tDiffThread = { export const DiffThread = ({ path }: tDiffThread) => { const { proposedChangeId } = useParams(); const [schemaList] = useAtom(schemaState); - const auth = useAuth(); const { node, currentBranch } = useContext(DiffContext); const [showThread, setShowThread] = useState(false); @@ -46,7 +45,9 @@ export const DiffThread = ({ path }: tDiffThread) => { const { loading, error, data, refetch } = useQuery(query, { skip: !schemaData }); - const thread = data ? data[PROPOSED_CHANGES_OBJECT_THREAD_OBJECT]?.edges[0]?.node : {}; + const thread = data ? data[schemaData.kind]?.edges[0]?.node : {}; + + const permission = data && getPermission(data?.[schemaData.kind]?.permissions?.edges); if (loading || error) { return null; @@ -64,14 +65,15 @@ export const DiffThread = ({ path }: tDiffThread) => { {thread?.comments?.count ? ( @@ -80,7 +82,7 @@ export const DiffThread = ({ path }: tDiffThread) => {
diff --git a/frontend/app/src/screens/diff/node-diff/utils.tsx b/frontend/app/src/screens/diff/node-diff/utils.tsx index 5e867c60c4..87616fb54c 100644 --- a/frontend/app/src/screens/diff/node-diff/utils.tsx +++ b/frontend/app/src/screens/diff/node-diff/utils.tsx @@ -1,3 +1,8 @@ +import Accordion from "@/components/display/accordion"; +import { DiffProperty, DiffStatus } from "@/screens/diff/node-diff/types"; +import { classNames, warnUnexpectedType } from "@/utils/common"; +import { capitalizeFirstLetter } from "@/utils/string"; +import { ReactNode } from "react"; import { BadgeAdded, BadgeConflict, @@ -7,11 +12,6 @@ import { BadgeUpdated, DiffBadgeProps, } from "../diff-badge"; -import { ReactNode } from "react"; -import { DiffProperty, DiffStatus } from "@/screens/diff/node-diff/types"; -import { classNames, warnUnexpectedType } from "@/utils/common"; -import Accordion from "@/components/display/accordion"; -import { capitalizeFirstLetter } from "@/utils/string"; export const diffBadges: { [key: string]: BadgeType } = { ADDED: BadgeAdded, @@ -88,12 +88,14 @@ export const DiffRow = ({ status === "REMOVED" && "bg-red-100 text-red-900", status === "UPDATED" && "bg-blue-100 text-blue-900", rightClassName - )}> + )} + > {right}
- }> + } + > {children}
diff --git a/frontend/app/src/screens/edit-form-hook/dynamic-control-types.ts b/frontend/app/src/screens/edit-form-hook/dynamic-control-types.ts index 1932a132ee..3a1220ca7a 100644 --- a/frontend/app/src/screens/edit-form-hook/dynamic-control-types.ts +++ b/frontend/app/src/screens/edit-form-hook/dynamic-control-types.ts @@ -1,8 +1,8 @@ import { SelectOption } from "@/components/inputs/select"; +import { SCHEMA_ATTRIBUTE_KIND } from "@/config/constants"; import { components } from "@/infraops"; import { RegisterOptions } from "react-hook-form"; import { FormFieldError } from "./form"; -import { SCHEMA_ATTRIBUTE_KIND } from "@/config/constants"; // Interface for every field in a create/edit form export interface DynamicFieldData { diff --git a/frontend/app/src/screens/errors/error-fallback.tsx b/frontend/app/src/screens/errors/error-fallback.tsx index ccef134ee2..216f32afb3 100644 --- a/frontend/app/src/screens/errors/error-fallback.tsx +++ b/frontend/app/src/screens/errors/error-fallback.tsx @@ -88,7 +88,8 @@ function ErrorFallback({ error }: ErrorFallbackProps) { className="underline" href="https://discord.gg/opsmill" target="_blank" - rel="noreferrer"> + rel="noreferrer" + > Discord {" or "} @@ -96,7 +97,8 @@ function ErrorFallback({ error }: ErrorFallbackProps) { className="underline" href="https://github.com/opsmill/infrahub/issues/new/choose" target="_blank" - rel="noreferrer"> + rel="noreferrer" + > GitHub

diff --git a/frontend/app/src/screens/errors/unauthorized-screen.tsx b/frontend/app/src/screens/errors/unauthorized-screen.tsx new file mode 100644 index 0000000000..eddd97227f --- /dev/null +++ b/frontend/app/src/screens/errors/unauthorized-screen.tsx @@ -0,0 +1,32 @@ +import Accordion from "@/components/display/accordion"; +import { classNames } from "@/utils/common"; +import { Icon } from "@iconify-icon/react"; +import { ReactElement } from "react"; + +type tUnauthorized = { + className?: string; + message?: string; + icon?: ReactElement; +}; + +const DEFAULT_MESSAGE = "Sorry, you are not authorized to access this view."; + +export default function UnauthorizedScreen({ className, message, icon }: tUnauthorized) { + return ( +
+ {icon || ( + + )} + + +
{message ?? DEFAULT_MESSAGE}
+
+
+ ); +} diff --git a/frontend/app/src/screens/graphql/details/graphql-query-details-card.tsx b/frontend/app/src/screens/graphql/details/graphql-query-details-card.tsx index c7ac7b2936..366c4d0537 100644 --- a/frontend/app/src/screens/graphql/details/graphql-query-details-card.tsx +++ b/frontend/app/src/screens/graphql/details/graphql-query-details-card.tsx @@ -3,11 +3,12 @@ import PropertiesPopover from "@/components/display/properties-popover"; import ObjectEditSlideOverTrigger from "@/components/form/object-edit-slide-over-trigger"; import { Property, PropertyList } from "@/components/table/property-list"; import { Badge } from "@/components/ui/badge"; -import { CardWithBorder } from "@/components/ui/card"; +import { Card, CardWithBorder } from "@/components/ui/card"; import { Link } from "@/components/ui/link"; import { Tooltip } from "@/components/ui/tooltip"; import { RELATIONSHIP_VIEW_BLACKLIST } from "@/config/constants"; import { CoreGraphQlQuery } from "@/generated/graphql"; +import { Permission } from "@/screens/permission/types"; import { iNodeSchema } from "@/state/atoms/schema.atom"; import { constructPath } from "@/utils/fetch"; import { AttributeType, ObjectAttributeValue } from "@/utils/getObjectItemDisplayValue"; @@ -18,35 +19,66 @@ type GraphqlQueryDetailsCardProps = { data: CoreGraphQlQuery; schema: iNodeSchema; refetch: () => Promise; + permission: Permission; }; -const GraphqlQueryDetailsCard = ({ data, schema, refetch }: GraphqlQueryDetailsCardProps) => { +const GraphqlQueryDetailsCard = ({ + data, + schema, + refetch, + permission, +}: GraphqlQueryDetailsCardProps) => { return ( - - - - - + + + + + ); }; -const GraphqlQueryDetailsTitle = ({ data, schema, refetch }: GraphqlQueryDetailsCardProps) => { +const GraphqlQueryDetailsTitle = ({ + data, + schema, + refetch, + permission, +}: GraphqlQueryDetailsCardProps) => { return ( <> - + {schema.namespace} {schema.name} - {data.display_label} - + ); }; -const GraphqlQueryPropertyList = ({ data, schema, refetch }: GraphqlQueryDetailsCardProps) => { +const GraphqlQueryPropertyList = ({ + data, + schema, + refetch, + permission, +}: GraphqlQueryDetailsCardProps) => { const properties: Property[] = [ { name: "ID", @@ -89,6 +121,7 @@ const GraphqlQueryPropertyList = ({ data, schema, refetch }: GraphqlQueryDetails refetch={refetch} data={data} schema={schema} + permission={permission} />
@@ -119,6 +152,7 @@ const GraphqlQueryPropertyList = ({ data, schema, refetch }: GraphqlQueryDetails refetch={refetch} data={data} schema={schema} + permission={permission} />
)), @@ -147,6 +181,7 @@ const GraphqlQueryPropertyList = ({ data, schema, refetch }: GraphqlQueryDetails refetch={refetch} data={data} schema={schema} + permission={permission} />
diff --git a/frontend/app/src/screens/graphql/details/graphql-query-viewer-card.tsx b/frontend/app/src/screens/graphql/details/graphql-query-viewer-card.tsx index 190ce2e6a8..a8b5fc5b55 100644 --- a/frontend/app/src/screens/graphql/details/graphql-query-viewer-card.tsx +++ b/frontend/app/src/screens/graphql/details/graphql-query-viewer-card.tsx @@ -1,15 +1,15 @@ import { Button } from "@/components/buttons/button-primitive"; import { CopyToClipboard } from "@/components/buttons/copy-to-clipboard"; import { GraphqlViewer } from "@/components/editor/graphql/graphql-viewer"; -import { CardWithBorder } from "@/components/ui/card"; +import { Card, CardWithBorder } from "@/components/ui/card"; import { constructPath } from "@/utils/fetch"; import { Icon } from "@iconify-icon/react"; import { Link } from "react-router-dom"; const GraphqlQueryViewerCard = ({ query }: { query: string }) => { return ( - - + +

Query

@@ -22,7 +22,7 @@ const GraphqlQueryViewerCard = ({ query }: { query: string }) => {
-
+ ); }; diff --git a/frontend/app/src/screens/groups/add-group-form.tsx b/frontend/app/src/screens/groups/add-group-form.tsx index 6c3787b4e7..2220b38403 100644 --- a/frontend/app/src/screens/groups/add-group-form.tsx +++ b/frontend/app/src/screens/groups/add-group-form.tsx @@ -1,11 +1,11 @@ import DynamicForm, { DynamicFormProps } from "@/components/form/dynamic-form"; -import { iNodeSchema } from "@/state/atoms/schema.atom"; -import NoDataFound from "@/screens/errors/no-data-found"; +import { ALERT_TYPES, Alert } from "@/components/ui/alert"; +import { ADD_RELATIONSHIP } from "@/graphql/mutations/relationships/addRelationship"; import { useMutation } from "@/hooks/useQuery"; -import { toast } from "react-toastify"; -import { Alert, ALERT_TYPES } from "@/components/ui/alert"; +import NoDataFound from "@/screens/errors/no-data-found"; +import { iNodeSchema } from "@/state/atoms/schema.atom"; import { pluralize } from "@/utils/string"; -import { ADD_RELATIONSHIP } from "@/graphql/mutations/relationships/addRelationship"; +import { toast } from "react-toastify"; interface AddGroupFormProps extends Omit { objectId: string; diff --git a/frontend/app/src/screens/groups/add-group-trigger-button.tsx b/frontend/app/src/screens/groups/add-group-trigger-button.tsx index bef66ff2a9..d29df23ab6 100644 --- a/frontend/app/src/screens/groups/add-group-trigger-button.tsx +++ b/frontend/app/src/screens/groups/add-group-trigger-button.tsx @@ -1,24 +1,25 @@ import { ButtonWithTooltip } from "@/components/buttons/button-primitive"; -import { usePermission } from "@/hooks/usePermission"; -import { useState } from "react"; import SlideOver, { SlideOverTitle } from "@/components/display/slide-over"; -import { Icon } from "@iconify-icon/react"; -import AddGroupForm from "@/screens/groups/add-group-form"; -import { iNodeSchema } from "@/state/atoms/schema.atom"; import graphqlClient from "@/graphql/graphqlClientApollo"; import { useObjectDetails } from "@/hooks/useObjectDetails"; +import AddGroupForm from "@/screens/groups/add-group-form"; +import { iNodeSchema } from "@/state/atoms/schema.atom"; +import { Icon } from "@iconify-icon/react"; +import { useState } from "react"; +import { Permission } from "../permission/types"; type AddGroupTriggerButtonProps = { schema: iNodeSchema; objectId: string; + permission: Permission; }; export default function AddGroupTriggerButton({ schema, objectId, + permission, ...props }: AddGroupTriggerButtonProps) { - const permission = usePermission(); const [isAddGroupFormOpen, setIsAddGroupFormOpen] = useState(false); const { data } = useObjectDetails(schema, objectId); @@ -30,11 +31,12 @@ export default function AddGroupTriggerButton({ setIsAddGroupFormOpen(true)} className="p-2" - disabled={!permission.write.allow} - tooltipContent={permission.write.message ?? "Add groups"} + disabled={!permission.update.isAllowed} + tooltipContent={permission.update.message ?? "Add groups"} tooltipEnabled data-testid="open-group-form-button" - {...props}> + {...props} + > @@ -49,7 +51,8 @@ export default function AddGroupTriggerButton({ /> } open={isAddGroupFormOpen} - setOpen={setIsAddGroupFormOpen}> + setOpen={setIsAddGroupFormOpen} + > { + const [filters, setFilters] = useFilters(); + const isInitialMount = useRef(true); + + const hasAutoGeneratedFiltered = filters.some( + (filter) => + filter.name === HIDE_AUTO_GENERATED_FILTER.name && + filter.value === HIDE_AUTO_GENERATED_FILTER.value + ); + + useEffect(() => { + if (isInitialMount.current) { + isInitialMount.current = false; + if (filters.length === 0) { + setFilters([HIDE_AUTO_GENERATED_FILTER]); + } + } + }, [filters, setFilters]); + + const handleClick = () => { + if (hasAutoGeneratedFiltered) { + setFilters(filters.filter((filter) => filter.name !== HIDE_AUTO_GENERATED_FILTER.name)); + } else { + setFilters([...filters, HIDE_AUTO_GENERATED_FILTER]); + } + }; + + return ( + + ); +}; diff --git a/frontend/app/src/screens/groups/groups-manager-trigger-button.tsx b/frontend/app/src/screens/groups/groups-manager-trigger-button.tsx index 11f64f3e07..c96fd74855 100644 --- a/frontend/app/src/screens/groups/groups-manager-trigger-button.tsx +++ b/frontend/app/src/screens/groups/groups-manager-trigger-button.tsx @@ -1,19 +1,18 @@ import { ButtonProps, ButtonWithTooltip } from "@/components/buttons/button-primitive"; -import { usePermission } from "@/hooks/usePermission"; -import { useState } from "react"; -import { Icon } from "@iconify-icon/react"; import SlideOver, { SlideOverTitle } from "@/components/display/slide-over"; -import { GroupsManager, GroupsManagerProps } from "@/screens/groups/groups-manager"; import { useObjectDetails } from "@/hooks/useObjectDetails"; +import { GroupsManager, GroupsManagerProps } from "@/screens/groups/groups-manager"; +import { Icon } from "@iconify-icon/react"; +import { useState } from "react"; type GroupsManagerTriggerProps = ButtonProps & GroupsManagerProps; export const GroupsManagerTriggerButton = ({ schema, + permission, objectId, ...props }: GroupsManagerTriggerProps) => { - const permission = usePermission(); const [isManageGroupsDrawerOpen, setIsManageGroupsDrawerOpen] = useState(false); const { data } = useObjectDetails(schema, objectId); @@ -23,14 +22,15 @@ export const GroupsManagerTriggerButton = ({ return ( <> setIsManageGroupsDrawerOpen(true)} variant="outline" size="square" data-testid="manage-groups" - {...props}> + {...props} + > {props.children ?? @@ -44,7 +44,8 @@ export const GroupsManagerTriggerButton = ({ title="Manage groups" subtitle="Add and unassign groups" /> - }> + } + > diff --git a/frontend/app/src/screens/groups/groups-manager.tsx b/frontend/app/src/screens/groups/groups-manager.tsx index c3d02ee37a..8dca66ebb0 100644 --- a/frontend/app/src/screens/groups/groups-manager.tsx +++ b/frontend/app/src/screens/groups/groups-manager.tsx @@ -1,18 +1,19 @@ +import { Button } from "@/components/buttons/button-primitive"; import { SearchInput } from "@/components/ui/search-input"; -import { useState } from "react"; -import { IModelSchema } from "@/state/atoms/schema.atom"; -import useQuery from "@/hooks/useQuery"; -import { gql } from "@apollo/client"; import { getGroupsQuery } from "@/graphql/queries/groups/getGroups"; -import LoadingScreen from "@/screens/loading-screen/loading-screen"; -import NoDataFound from "@/screens/errors/no-data-found"; +import useQuery from "@/hooks/useQuery"; import ErrorScreen from "@/screens/errors/error-screen"; -import ObjectGroupsList from "@/screens/groups/object-groups-list"; +import NoDataFound from "@/screens/errors/no-data-found"; import AddGroupTriggerButton from "@/screens/groups/add-group-trigger-button"; -import { classNames } from "@/utils/common"; +import ObjectGroupsList from "@/screens/groups/object-groups-list"; import { GroupDataFromAPI } from "@/screens/groups/types"; -import { Button } from "@/components/buttons/button-primitive"; +import LoadingScreen from "@/screens/loading-screen/loading-screen"; +import { getPermission } from "@/screens/permission/utils"; +import { IModelSchema } from "@/state/atoms/schema.atom"; +import { classNames } from "@/utils/common"; +import { gql } from "@apollo/client"; import { Icon } from "@iconify-icon/react"; +import { useState } from "react"; export type GroupsManagerProps = { className?: string; @@ -44,6 +45,8 @@ export const GroupsManager = ({ className, schema, objectId }: GroupsManagerProp const currentObjectData = data[schema.kind!]?.edges[0]?.node; + const permission = getPermission(data[schema.kind!]?.permissions?.edges); + if (!currentObjectData) { return ( - +
{hasAutoGeneratedGroups && ( @@ -97,7 +100,8 @@ export const GroupsManager = ({ className, schema, objectId }: GroupsManagerProp onClick={() => setDisplayAutoGenerated((v) => !v)} className="ml-auto text-custom-blue-700 shrink-0" variant="ghost" - size="sm"> + size="sm" + > auto-generated diff --git a/frontend/app/src/screens/groups/object-groups-list.tsx b/frontend/app/src/screens/groups/object-groups-list.tsx index a7f10c5a28..edb1ba60eb 100644 --- a/frontend/app/src/screens/groups/object-groups-list.tsx +++ b/frontend/app/src/screens/groups/object-groups-list.tsx @@ -1,20 +1,20 @@ +import { Button } from "@/components/buttons/button-primitive"; import ItemGroup from "@/components/layouts/item-group"; +import ModalDelete from "@/components/modals/modal-delete"; import { Badge } from "@/components/ui/badge"; import { Tooltip } from "@/components/ui/tooltip"; -import { Button } from "@/components/buttons/button-primitive"; -import { Icon } from "@iconify-icon/react"; -import { Link } from "react-router-dom"; -import { getObjectDetailsUrl2 } from "@/utils/objects"; import { QSP } from "@/config/qsp"; -import ModalDelete from "@/components/modals/modal-delete"; -import { useState } from "react"; -import { useMutation } from "@/hooks/useQuery"; import graphqlClient from "@/graphql/graphqlClientApollo"; -import { pluralize } from "@/utils/string"; -import { useAtomValue } from "jotai"; -import { schemaState } from "@/state/atoms/schema.atom"; import { REMOVE_RELATIONSHIP } from "@/graphql/mutations/relationships/removeRelationship"; +import { useMutation } from "@/hooks/useQuery"; import { GroupDataFromAPI } from "@/screens/groups/types"; +import { schemaState } from "@/state/atoms/schema.atom"; +import { getObjectDetailsUrl2 } from "@/utils/objects"; +import { pluralize } from "@/utils/string"; +import { Icon } from "@iconify-icon/react"; +import { useAtomValue } from "jotai"; +import { useState } from "react"; +import { Link } from "react-router-dom"; type ObjectGroupsListProps = { className?: string; @@ -50,7 +50,8 @@ const ObjectGroupItem = ({ objectId, group }: ObjectGroupProps) => {
+ className="font-semibold hover:underline truncate block" + > {group.display_label} @@ -59,7 +60,8 @@ const ObjectGroupItem = ({ objectId, group }: ObjectGroupProps) => { to={getObjectDetailsUrl2(group.__typename, group.id, [ { name: QSP.TAB, value: "members" }, ])} - className="text-sm font-light hover:underline"> + className="text-sm font-light hover:underline" + > {pluralize(group.members.count, "member")} @@ -93,7 +95,8 @@ const RemoveGroupButton = ({ objectId, group }: ObjectGroupProps) => { size="icon" className="flex-shrink-0 hover:bg-gray-200" onClick={() => setShowDeleteModal(true)} - data-testid="leave-group-button"> + data-testid="leave-group-button" + > diff --git a/frontend/app/src/screens/ipam/common/ip-details-card.tsx b/frontend/app/src/screens/ipam/common/ip-details-card.tsx index 0ce31406da..6dffaf1922 100644 --- a/frontend/app/src/screens/ipam/common/ip-details-card.tsx +++ b/frontend/app/src/screens/ipam/common/ip-details-card.tsx @@ -5,6 +5,7 @@ import { Badge } from "@/components/ui/badge"; import { CardWithBorder } from "@/components/ui/card"; import { Link } from "@/components/ui/link"; import { IP_SUMMARY_RELATIONSHIPS_BLACKLIST } from "@/screens/ipam/constants"; +import { Permission } from "@/screens/permission/types"; import { IModelSchema } from "@/state/atoms/schema.atom"; import { constructPath } from "@/utils/fetch"; import { AttributeType, ObjectAttributeValue } from "@/utils/getObjectItemDisplayValue"; @@ -14,9 +15,10 @@ type tIpDetailsCard = { schema: IModelSchema; data: { id: string; display_label: string } & Record; refetch: () => void; + permission: Permission; }; -export function IpDetailsCard({ schema, data, refetch }: tIpDetailsCard) { +export function IpDetailsCard({ schema, data, refetch, permission }: tIpDetailsCard) { const properties: Property[] = [ { name: "ID", value: data.id }, ...(schema.attributes ?? []).map((schemaAttribute) => { @@ -48,7 +50,8 @@ export function IpDetailsCard({ schema, data, refetch }: tIpDetailsCard) { + )} + > {relationshipData?.display_label} ), @@ -62,7 +65,12 @@ export function IpDetailsCard({ schema, data, refetch }: tIpDetailsCard) {
{schema.namespace} {schema.label} summary
- + diff --git a/frontend/app/src/screens/ipam/ip-addresses/ip-address-summary.tsx b/frontend/app/src/screens/ipam/ip-addresses/ip-address-summary.tsx index 233e747491..c80a12d173 100644 --- a/frontend/app/src/screens/ipam/ip-addresses/ip-address-summary.tsx +++ b/frontend/app/src/screens/ipam/ip-addresses/ip-address-summary.tsx @@ -3,11 +3,13 @@ import { GET_IP_ADDRESS_KIND } from "@/graphql/queries/ipam/ip-address"; import { getObjectDetailsPaginated } from "@/graphql/queries/objects/getObjectDetails"; import useQuery from "@/hooks/useQuery"; import ErrorScreen from "@/screens/errors/error-screen"; +import UnauthorizedScreen from "@/screens/errors/unauthorized-screen"; import { IpDetailsCard } from "@/screens/ipam/common/ip-details-card"; import { constructPathForIpam } from "@/screens/ipam/common/utils"; import { IPAM_ROUTE, IP_ADDRESS_GENERIC } from "@/screens/ipam/constants"; import { IpamSummarySkeleton } from "@/screens/ipam/prefixes/ipam-summary-skeleton"; import LoadingScreen from "@/screens/loading-screen/loading-screen"; +import { getPermission } from "@/screens/permission/utils"; import { genericsState, schemaState } from "@/state/atoms/schema.atom"; import { getSchemaObjectColumns } from "@/utils/getSchemaObjectColumns"; import { gql } from "@apollo/client"; @@ -66,6 +68,7 @@ const IpAddressSummaryContent = ({ ipAddressId, ipAddressKind }: IpAddressSummar objectid: ipAddressId, kind: ipAddressKind, columns, + hasPermission: true, }) ); @@ -76,15 +79,26 @@ const IpAddressSummaryContent = ({ ipAddressId, ipAddressKind }: IpAddressSummar if (loading || !data || !ipAddressSchema) return ; + const permission = getPermission(data[ipAddressKind]?.permissions?.edges); + if (error) { return ; } + if (!permission.view.isAllowed) { + return ; + } + const ipAddressData = data[ipAddressKind].edges[0].node; return (
- +
); }; diff --git a/frontend/app/src/screens/ipam/ip-addresses/ipam-ip-address-list.tsx b/frontend/app/src/screens/ipam/ip-addresses/ipam-ip-address-list.tsx index 3767b6be74..fb1a7c3124 100644 --- a/frontend/app/src/screens/ipam/ip-addresses/ipam-ip-address-list.tsx +++ b/frontend/app/src/screens/ipam/ip-addresses/ipam-ip-address-list.tsx @@ -22,6 +22,7 @@ import { } from "@/screens/ipam/constants"; import LoadingScreen from "@/screens/loading-screen/loading-screen"; import ObjectItemEditComponent from "@/screens/object-item-edit/object-item-edit-paginated"; +import { getPermission } from "@/screens/permission/utils"; import { currentBranchAtom } from "@/state/atoms/branches.atom"; import { datetimeAtom } from "@/state/atoms/time.atom"; import { stringifyWithoutQuotes } from "@/utils/string"; @@ -59,6 +60,8 @@ const IpamIPAddressesList = forwardRef((props, ref) => { skip: !defaultIpNamespace, }); + const permission = getPermission(data?.[IP_ADDRESS_GENERIC]?.permissions?.edges); + const { data: getPrefixKindData } = useQuery(GET_PREFIX_KIND, { variables: { ids: [prefix] }, skip: !prefix || !defaultIpNamespace, @@ -151,7 +154,8 @@ const IpamIPAddressesList = forwardRef((props, ref) => { + ])} + > {prefixData.display_label} @@ -164,7 +168,13 @@ const IpamIPAddressesList = forwardRef((props, ref) => { {(loading || !defaultIpNamespace) && } {data && ( - +
)} {relatedRowToDelete && ( @@ -202,7 +212,8 @@ const IpamIPAddressesList = forwardRef((props, ref) => { {relatedObjectToEdit?.__typename} @@ -210,7 +221,8 @@ const IpamIPAddressesList = forwardRef((props, ref) => { } open={!!relatedObjectToEdit} - setOpen={() => setRelatedObjectToEdit(undefined)}> + setOpen={() => setRelatedObjectToEdit(undefined)} + > { setRelatedObjectToEdit(undefined); diff --git a/frontend/app/src/screens/ipam/ip-namespace-selector.tsx b/frontend/app/src/screens/ipam/ip-namespace-selector.tsx index b896235403..ba6c977cf7 100644 --- a/frontend/app/src/screens/ipam/ip-namespace-selector.tsx +++ b/frontend/app/src/screens/ipam/ip-namespace-selector.tsx @@ -12,12 +12,16 @@ import { constructPathForIpam } from "./common/utils"; import { IPAM_QSP, IPAM_ROUTE, IPAM_TABS, NAMESPACE_GENERIC } from "./constants"; export default function IpNamespaceSelector() { - const { loading, data } = useQuery(GET_IP_NAMESPACES); + const { loading, data, error } = useQuery(GET_IP_NAMESPACES); if (loading) { return ; } + if (error) { + return null; + } + const namespaces = data?.[NAMESPACE_GENERIC]?.edges.map((edge: any) => edge.node) ?? []; const defaultNamespace = namespaces.find((result: any) => result.default?.value === true); diff --git a/frontend/app/src/screens/ipam/ipam-router.tsx b/frontend/app/src/screens/ipam/ipam-router.tsx index 9e2e563dc5..08af7cae57 100644 --- a/frontend/app/src/screens/ipam/ipam-router.tsx +++ b/frontend/app/src/screens/ipam/ipam-router.tsx @@ -1,18 +1,22 @@ import { ButtonWithTooltip } from "@/components/buttons/button-with-tooltip"; import SlideOver from "@/components/display/slide-over"; +import ObjectForm from "@/components/form/object-form"; import { Tabs } from "@/components/tabs"; -import { Card } from "@/components/ui/card"; import { DEFAULT_BRANCH_NAME } from "@/config/constants"; -import { usePermission } from "@/hooks/usePermission"; -import ObjectForm from "@/components/form/object-form"; +import useQuery from "@/hooks/useQuery"; +import { getPermission } from "@/screens/permission/utils"; import { currentBranchAtom } from "@/state/atoms/branches.atom"; import { genericsState, schemaState } from "@/state/atoms/schema.atom"; import { constructPath } from "@/utils/fetch"; +import { gql } from "@apollo/client"; import { Icon } from "@iconify-icon/react"; import { useAtomValue, useSetAtom } from "jotai"; import { useRef, useState } from "react"; import { useNavigate, useParams } from "react-router-dom"; import { StringParam, useQueryParam } from "use-query-params"; +import ErrorScreen from "../errors/error-screen"; +import UnauthorizedScreen from "../errors/unauthorized-screen"; +import { getObjectPermissionsQuery } from "../permission/queries/getObjectPermissions"; import { defaultIpNamespaceAtom } from "./common/namespace.state"; import { IPAM_QSP, @@ -34,7 +38,6 @@ function IpamRouter() { const [qspTab] = useQueryParam(IPAM_QSP.TAB, StringParam); const navigate = useNavigate(); const { prefix } = useParams(); - const permission = usePermission(); const branch = useAtomValue(currentBranchAtom); const schemaList = useAtomValue(schemaState); const genericList = useAtomValue(genericsState); @@ -42,13 +45,16 @@ function IpamRouter() { const defaultIpNamespace = useAtomValue(defaultIpNamespaceAtom); const reloadIpamTree = useSetAtom(reloadIpamTreeAtom); const refetchRef = useRef(null); + const [showCreateDrawer, setShowCreateDrawer] = useState(false); const objectname = qspTab ? tabToKind[qspTab] : IP_PREFIX_GENERIC; const schema = schemaList.find((s) => s.kind === objectname); const generic = genericList.find((s) => s.kind === objectname); const schemaData = schema || generic; - const [showCreateDrawer, setShowCreateDrawer] = useState(false); + const { loading, data, error } = useQuery(gql(getObjectPermissionsQuery(objectname))); + + const permission = data && getPermission(data?.[objectname]?.permissions?.edges); const tabs = [ { @@ -117,24 +123,37 @@ function IpamRouter() { } }; + if (error) { + if (error.networkError?.statusCode === 403) { + const { message } = error.networkError?.result?.errors?.[0] ?? {}; + + return ; + } + + return ; + } + const rightitems = ( setShowCreateDrawer(true)} className="mr-4" - data-testid="create-object-button"> + data-testid="create-object-button" + > Add {schemaData?.label} ); return ( - + <> -
{renderContent()}
+
+ {renderContent()} +
-
+ ); } diff --git a/frontend/app/src/screens/ipam/ipam-tree/ipam-tree.state.ts b/frontend/app/src/screens/ipam/ipam-tree/ipam-tree.state.ts index 14ce6a4716..baf04a70e4 100644 --- a/frontend/app/src/screens/ipam/ipam-tree/ipam-tree.state.ts +++ b/frontend/app/src/screens/ipam/ipam-tree/ipam-tree.state.ts @@ -1,8 +1,8 @@ import { TreeProps } from "@/components/ui/tree"; import graphqlClient from "@/graphql/graphqlClientApollo"; import { - GET_PREFIX_ANCESTORS, GET_PREFIXES_ONLY, + GET_PREFIX_ANCESTORS, GET_TOP_LEVEL_PREFIXES, } from "@/graphql/queries/ipam/prefixes"; import { IP_PREFIX_GENERIC, TREE_ROOT_ID } from "@/screens/ipam/constants"; @@ -13,8 +13,8 @@ import * as R from "ramda"; import { AncestorsData, EMPTY_TREE, - formatIPPrefixResponseForTreeView, PrefixData, + formatIPPrefixResponseForTreeView, updateTreeData, } from "./utils"; diff --git a/frontend/app/src/screens/ipam/ipam-tree/ipam-tree.tsx b/frontend/app/src/screens/ipam/ipam-tree/ipam-tree.tsx index 25263e10df..e5d80eb490 100644 --- a/frontend/app/src/screens/ipam/ipam-tree/ipam-tree.tsx +++ b/frontend/app/src/screens/ipam/ipam-tree/ipam-tree.tsx @@ -6,23 +6,23 @@ import { useEffect, useState } from "react"; import { ITreeViewOnLoadDataProps, NodeId } from "react-accessible-treeview"; import { Link, useNavigate, useParams } from "react-router-dom"; +import { Badge } from "@/components/ui/badge"; +import { SearchInput, SearchInputProps } from "@/components/ui/search-input"; import { GET_PREFIXES_ONLY } from "@/graphql/queries/ipam/prefixes"; import { defaultIpNamespaceAtom } from "@/screens/ipam/common/namespace.state"; import { constructPathForIpam } from "@/screens/ipam/common/utils"; import { IPAM_QSP, IPAM_ROUTE, TREE_ROOT_ID } from "@/screens/ipam/constants"; import { genericsState, schemaState } from "@/state/atoms/schema.atom"; +import { debounce } from "@/utils/common"; import { StringParam, useQueryParam } from "use-query-params"; import { ipamTreeAtom, reloadIpamTreeAtom } from "./ipam-tree.state"; import { + EMPTY_TREE, PrefixData, formatIPPrefixResponseForTreeView, getTreeItemAncestors, updateTreeData, - EMPTY_TREE, } from "./utils"; -import { Badge } from "@/components/ui/badge"; -import { SearchInput, SearchInputProps } from "@/components/ui/search-input"; -import { debounce } from "@/utils/common"; export default function IpamTree({ className }: { className?: string }) { const { prefix } = useParams(); @@ -136,7 +136,8 @@ const IpamTreeItem = ({ element }: TreeItemProps) => { to={url} tabIndex={-1} className="flex items-center gap-2 w-full" - data-testid="ipam-tree-item"> + data-testid="ipam-tree-item" + > {schema?.icon ? :
} {element.name} {!!element.metadata?.descendantsCount && ( diff --git a/frontend/app/src/screens/ipam/ipam-tree/utils.ts b/frontend/app/src/screens/ipam/ipam-tree/utils.ts index 85f656fa19..23e0044512 100644 --- a/frontend/app/src/screens/ipam/ipam-tree/utils.ts +++ b/frontend/app/src/screens/ipam/ipam-tree/utils.ts @@ -1,5 +1,5 @@ import { TreeItemProps, TreeProps } from "@/components/ui/tree"; -import { TREE_ROOT_ID, IP_PREFIX_GENERIC } from "@/screens/ipam/constants"; +import { IP_PREFIX_GENERIC, TREE_ROOT_ID } from "@/screens/ipam/constants"; export type PrefixNode = { id: string; diff --git a/frontend/app/src/screens/ipam/prefixes/ipam-prefix-details.tsx b/frontend/app/src/screens/ipam/prefixes/ipam-prefix-details.tsx index 7a666aee9e..344caa5939 100644 --- a/frontend/app/src/screens/ipam/prefixes/ipam-prefix-details.tsx +++ b/frontend/app/src/screens/ipam/prefixes/ipam-prefix-details.tsx @@ -1,3 +1,4 @@ +import { ColorDisplay } from "@/components/display/color-display"; import SlideOver from "@/components/display/slide-over"; import ModalDelete from "@/components/modals/modal-delete"; import ProgressBarChart from "@/components/stats/progress-bar-chart"; @@ -17,6 +18,7 @@ import { IPAM_QSP, IPAM_ROUTE, IP_PREFIX_GENERIC } from "@/screens/ipam/constant import { reloadIpamTreeAtom } from "@/screens/ipam/ipam-tree/ipam-tree.state"; import LoadingScreen from "@/screens/loading-screen/loading-screen"; import ObjectItemEditComponent from "@/screens/object-item-edit/object-item-edit-paginated"; +import { getPermission } from "@/screens/permission/utils"; import { currentBranchAtom } from "@/state/atoms/branches.atom"; import { datetimeAtom } from "@/state/atoms/time.atom"; import { stringifyWithoutQuotes } from "@/utils/string"; @@ -27,7 +29,6 @@ import { forwardRef, useImperativeHandle, useState } from "react"; import { useParams } from "react-router-dom"; import { toast } from "react-toastify"; import { StringParam, useQueryParam } from "use-query-params"; -import { ColorDisplay } from "@/components/display/color-display"; const IpamIPPrefixDetails = forwardRef((props, ref) => { const { prefix } = useParams(); @@ -46,6 +47,8 @@ const IpamIPPrefixDetails = forwardRef((props, ref) => { useImperativeHandle(ref, () => ({ refetch })); + const permission = getPermission(data?.[IP_PREFIX_GENERIC]?.permissions?.edges); + if (!prefix) { return
Select a Prefix in the Tree to the left to see details
; } @@ -169,7 +172,13 @@ const IpamIPPrefixDetails = forwardRef((props, ref) => {
{data && ( -
+
)} {relatedRowToDelete && ( @@ -209,7 +218,8 @@ const IpamIPPrefixDetails = forwardRef((props, ref) => { {relatedObjectToEdit?.__typename} @@ -219,7 +229,8 @@ const IpamIPPrefixDetails = forwardRef((props, ref) => { } open={!!relatedObjectToEdit} - setOpen={() => setRelatedObjectToEdit(undefined)}> + setOpen={() => setRelatedObjectToEdit(undefined)} + > { setRelatedObjectToEdit(undefined); diff --git a/frontend/app/src/screens/ipam/prefixes/ipam-prefixes-summary-details.tsx b/frontend/app/src/screens/ipam/prefixes/ipam-prefixes-summary-details.tsx index 4a6c2c341c..742a97677f 100644 --- a/frontend/app/src/screens/ipam/prefixes/ipam-prefixes-summary-details.tsx +++ b/frontend/app/src/screens/ipam/prefixes/ipam-prefixes-summary-details.tsx @@ -7,6 +7,7 @@ import NoDataFound from "@/screens/errors/no-data-found"; import { IpDetailsCard } from "@/screens/ipam/common/ip-details-card"; import { constructPathForIpam } from "@/screens/ipam/common/utils"; import { IPAM_QSP, IPAM_ROUTE, IP_PREFIX_GENERIC } from "@/screens/ipam/constants"; +import { getPermission } from "@/screens/permission/utils"; import { genericsState, schemaState } from "@/state/atoms/schema.atom"; import { getSchemaObjectColumns } from "@/utils/getSchemaObjectColumns"; import { gql } from "@apollo/client"; @@ -73,6 +74,7 @@ const PrefixSummaryContent = ({ prefixId, prefixKind }: PrefixSummaryContentProp kind: prefixKind, columns, filters, + hasPermissions: true, }) ); @@ -85,13 +87,20 @@ const PrefixSummaryContent = ({ prefixId, prefixKind }: PrefixSummaryContentProp const prefixData = data[prefixKind]?.edges?.length && data[prefixKind]?.edges[0].node; + const permission = getPermission(data[prefixKind]?.permissions?.edges); + if (!prefixData) { return ; } return (
- +
); }; diff --git a/frontend/app/src/screens/ipam/prefixes/ipam-prefixes-summary-list.tsx b/frontend/app/src/screens/ipam/prefixes/ipam-prefixes-summary-list.tsx index e1e5eafb16..a46910e7f2 100644 --- a/frontend/app/src/screens/ipam/prefixes/ipam-prefixes-summary-list.tsx +++ b/frontend/app/src/screens/ipam/prefixes/ipam-prefixes-summary-list.tsx @@ -16,6 +16,7 @@ import { IPAM_QSP, IPAM_ROUTE, IP_PREFIX_GENERIC } from "@/screens/ipam/constant import { reloadIpamTreeAtom } from "@/screens/ipam/ipam-tree/ipam-tree.state"; import LoadingScreen from "@/screens/loading-screen/loading-screen"; import ObjectItemEditComponent from "@/screens/object-item-edit/object-item-edit-paginated"; +import { getPermission } from "@/screens/permission/utils"; import { currentBranchAtom } from "@/state/atoms/branches.atom"; import { datetimeAtom } from "@/state/atoms/time.atom"; import { stringifyWithoutQuotes } from "@/utils/string"; @@ -45,6 +46,8 @@ const IpamIPPrefixesSummaryList = forwardRef((props, ref) => { useImperativeHandle(ref, () => ({ refetch })); + const permission = getPermission(data?.[IP_PREFIX_GENERIC]?.permissions?.edges); + const columns = [ { name: "prefix", label: "Prefix" }, { name: "description", label: "Description" }, @@ -145,7 +148,13 @@ const IpamIPPrefixesSummaryList = forwardRef((props, ref) => { {(loading || !defaultIpNamespace) && } {data && ( -
+
)} {relatedRowToDelete && ( @@ -185,7 +194,8 @@ const IpamIPPrefixesSummaryList = forwardRef((props, ref) => { {relatedObjectToEdit?.__typename} @@ -195,7 +205,8 @@ const IpamIPPrefixesSummaryList = forwardRef((props, ref) => { } open={!!relatedObjectToEdit} - setOpen={() => setRelatedObjectToEdit(undefined)}> + setOpen={() => setRelatedObjectToEdit(undefined)} + > { setRelatedObjectToEdit(undefined); diff --git a/frontend/app/src/screens/layout/app-version.tsx b/frontend/app/src/screens/layout/app-version.tsx new file mode 100644 index 0000000000..d52cf25d21 --- /dev/null +++ b/frontend/app/src/screens/layout/app-version.tsx @@ -0,0 +1,19 @@ +import { Skeleton } from "@/components/skeleton"; +import { CONFIG } from "@/config/config"; +import { components } from "@/infraops"; +import { fetchUrl } from "@/utils/fetch"; +import { useEffect, useState } from "react"; + +export const AppVersion = () => { + const [info, setInfo] = useState(null); + + useEffect(() => { + fetchUrl(CONFIG.INFO_URL).then((result) => setInfo(result)); + }, []); + + return ( +
+ Infrahub - v{info ? info.version : } +
+ ); +}; diff --git a/frontend/app/src/screens/layout/breadcrumb-navigation/breadcrumb-navigation.tsx b/frontend/app/src/screens/layout/breadcrumb-navigation/breadcrumb-navigation.tsx new file mode 100644 index 0000000000..d8c671c685 --- /dev/null +++ b/frontend/app/src/screens/layout/breadcrumb-navigation/breadcrumb-navigation.tsx @@ -0,0 +1,34 @@ +import { Breadcrumb, BreadcrumbSeparator } from "@/components/ui/breadcrumb"; +import { + BreadcrumbDynamicElement, + BreadcrumbDynamicElementProps, +} from "@/screens/layout/breadcrumb-navigation/items/breadcrumb-dynamic-element"; +import { breadcrumbActiveStyle } from "@/screens/layout/breadcrumb-navigation/style"; +import { classNames } from "@/utils/common"; +import React from "react"; +import { UIMatch, useMatches } from "react-router-dom"; + +export default function BreadcrumbNavigation() { + const matches = useMatches() as UIMatch< + unknown, + { breadcrumb?: (match: UIMatch) => BreadcrumbDynamicElementProps } + >[]; + + const crumbs = matches + .map((match) => match.handle?.breadcrumb?.(match)) + .filter((match) => !!match); + + return ( + + {crumbs.map((crumb, index) => ( + + + + + ))} + + ); +} diff --git a/frontend/app/src/screens/layout/breadcrumb-navigation/items/breadcrumb-branch-selector.tsx b/frontend/app/src/screens/layout/breadcrumb-navigation/items/breadcrumb-branch-selector.tsx new file mode 100644 index 0000000000..391b4cca22 --- /dev/null +++ b/frontend/app/src/screens/layout/breadcrumb-navigation/items/breadcrumb-branch-selector.tsx @@ -0,0 +1,57 @@ +import { Combobox, ComboboxContent, ComboboxList, ComboboxTrigger } from "@/components/ui/combobox"; +import { CommandEmpty, CommandItem } from "@/components/ui/command"; +import graphqlClient from "@/graphql/graphqlClientApollo"; +import { breadcrumbItemStyle } from "@/screens/layout/breadcrumb-navigation/style"; +import { branchesState } from "@/state/atoms/branches.atom"; +import { classNames } from "@/utils/common"; +import { constructPath } from "@/utils/fetch"; +import { useAtomValue } from "jotai"; +import { useEffect, useState } from "react"; +import { Link, useNavigate } from "react-router-dom"; + +export default function BreadcrumbBranchSelector({ + value, + className, + ...props +}: { + value: string; + className?: string; +}) { + const branches = useAtomValue(branchesState); + const navigate = useNavigate(); + const [isOpen, setIsOpen] = useState(false); + + useEffect(() => { + if (isOpen) graphqlClient.refetchQueries({ include: ["GetBranches"] }); + }, [isOpen]); + + return ( + + + {value} + + + + + No branch found. + {branches.map((branch) => { + const branchUrl = constructPath(`/branches/${branch.name}`); + return ( + { + setIsOpen(false); + navigate(branchUrl); + }} + asChild + > + {branch.name} + + ); + })} + + + + ); +} diff --git a/frontend/app/src/screens/layout/breadcrumb-navigation/items/breadcrumb-dynamic-element.tsx b/frontend/app/src/screens/layout/breadcrumb-navigation/items/breadcrumb-dynamic-element.tsx new file mode 100644 index 0000000000..ddabdd1775 --- /dev/null +++ b/frontend/app/src/screens/layout/breadcrumb-navigation/items/breadcrumb-dynamic-element.tsx @@ -0,0 +1,33 @@ +import BreadcrumbBranchSelector from "@/screens/layout/breadcrumb-navigation/items/breadcrumb-branch-selector"; +import { BreadcrumbLink } from "@/screens/layout/breadcrumb-navigation/items/breadcrumb-link"; +import BreadcrumbObjectSelector from "@/screens/layout/breadcrumb-navigation/items/breadcrumb-object-selector"; +import BreadcrumbSchemaSelector from "@/screens/layout/breadcrumb-navigation/items/breadcrumb-schema-selector"; +import { BreadcrumbItem } from "@/screens/layout/breadcrumb-navigation/type"; +import { warnUnexpectedType } from "@/utils/common"; + +export type BreadcrumbDynamicElementProps = BreadcrumbItem & { + isLast?: boolean; + className?: string; +}; + +export const BreadcrumbDynamicElement = ({ ...props }: BreadcrumbDynamicElementProps) => { + if (props.type === "link") { + return {props.label}; + } + + if (props.type === "select") { + const { value, kind, ...otherProps } = props; + if (kind === "schema") { + return ; + } + + return ; + } + + if (props.type === "branch") { + return ; + } + + warnUnexpectedType(props); + return null; +}; diff --git a/frontend/app/src/screens/layout/breadcrumb-navigation/items/breadcrumb-link.tsx b/frontend/app/src/screens/layout/breadcrumb-navigation/items/breadcrumb-link.tsx new file mode 100644 index 0000000000..40ba89074d --- /dev/null +++ b/frontend/app/src/screens/layout/breadcrumb-navigation/items/breadcrumb-link.tsx @@ -0,0 +1,16 @@ +import { breadcrumbItemStyle } from "@/screens/layout/breadcrumb-navigation/style"; +import { classNames } from "@/utils/common"; +import { Slot } from "@radix-ui/react-slot"; +import React from "react"; +import { Link, LinkProps } from "react-router-dom"; + +export const BreadcrumbLink = React.forwardRef< + HTMLAnchorElement, + LinkProps & { + asChild?: boolean; + } +>(({ asChild, className, ...props }, ref) => { + const Comp = asChild ? Slot : Link; + + return ; +}); diff --git a/frontend/app/src/screens/layout/breadcrumb-navigation/items/breadcrumb-loading.tsx b/frontend/app/src/screens/layout/breadcrumb-navigation/items/breadcrumb-loading.tsx new file mode 100644 index 0000000000..213efe3d6a --- /dev/null +++ b/frontend/app/src/screens/layout/breadcrumb-navigation/items/breadcrumb-loading.tsx @@ -0,0 +1,5 @@ +import { Spinner } from "@/components/ui/spinner"; + +export default function BreadcrumbLoading() { + return ; +} diff --git a/frontend/app/src/screens/layout/breadcrumb-navigation/items/breadcrumb-object-selector.tsx b/frontend/app/src/screens/layout/breadcrumb-navigation/items/breadcrumb-object-selector.tsx new file mode 100644 index 0000000000..4efc36628a --- /dev/null +++ b/frontend/app/src/screens/layout/breadcrumb-navigation/items/breadcrumb-object-selector.tsx @@ -0,0 +1,55 @@ +import { useObjectDetails } from "@/hooks/useObjectDetails"; +import { useSchema } from "@/hooks/useSchema"; +import { BreadcrumbLink } from "@/screens/layout/breadcrumb-navigation/items/breadcrumb-link"; +import BreadcrumbLoading from "@/screens/layout/breadcrumb-navigation/items/breadcrumb-loading"; +import { IModelSchema } from "@/state/atoms/schema.atom"; +import { getObjectDetailsUrl2 } from "@/utils/objects"; +import { NetworkStatus } from "@apollo/client"; + +export default function BreadcrumbObjectSelector({ + kind, + id, + ...props +}: { + kind: string; + id: string; + className?: string; +}) { + const { schema } = useSchema(kind); + + if (!schema) return ; + + return ; +} + +const ObjectSelector = ({ + schema, + id, + ...props +}: { + schema: IModelSchema; + id: string; + className?: string; +}) => { + const { data, error, networkStatus } = useObjectDetails(schema, id); + + if (networkStatus === NetworkStatus.loading) return ; + + if (error) return null; + + const objectList = data?.[schema.kind!].edges.map((edge: any) => edge.node); + if (!objectList || objectList.length === 0) return null; + + const currentObject = objectList.find((node: any) => node.id === id); + + if (!currentObject) return null; + + return ( + + {currentObject.display_label} + + ); +}; diff --git a/frontend/app/src/screens/layout/breadcrumb-navigation/items/breadcrumb-schema-selector.tsx b/frontend/app/src/screens/layout/breadcrumb-navigation/items/breadcrumb-schema-selector.tsx new file mode 100644 index 0000000000..5ba83e2207 --- /dev/null +++ b/frontend/app/src/screens/layout/breadcrumb-navigation/items/breadcrumb-schema-selector.tsx @@ -0,0 +1,67 @@ +import { BreadcrumbSeparator } from "@/components/ui/breadcrumb"; +import { PROFILE_KIND } from "@/config/constants"; +import { useSchema } from "@/hooks/useSchema"; +import { BreadcrumbLink } from "@/screens/layout/breadcrumb-navigation/items/breadcrumb-link"; +import BreadcrumbLoading from "@/screens/layout/breadcrumb-navigation/items/breadcrumb-loading"; +import { breadcrumbActiveStyle } from "@/screens/layout/breadcrumb-navigation/style"; +import { classNames } from "@/utils/common"; +import { getObjectDetailsUrl2 } from "@/utils/objects"; + +interface BreadcrumbSchemaSelectorProps { + kind: string; + isLast?: boolean; + className?: string; +} +export default function BreadcrumbSchemaSelector({ + isLast, + kind, + ...props +}: BreadcrumbSchemaSelectorProps) { + const { schema, isProfile, isNode } = useSchema(kind); + + if (!schema) { + return ; + } + + if (isProfile) { + return ( + <> + + + + {schema.label} + + + ); + } + + if (isNode && schema.hierarchy) { + return ( + <> + + + + {schema.label} + + + ); + } + + return ( + + {schema.label} + + ); +} diff --git a/frontend/app/src/screens/layout/breadcrumb-navigation/style.ts b/frontend/app/src/screens/layout/breadcrumb-navigation/style.ts new file mode 100644 index 0000000000..36705789a7 --- /dev/null +++ b/frontend/app/src/screens/layout/breadcrumb-navigation/style.ts @@ -0,0 +1,5 @@ +export const breadcrumbItemStyle = + "w-auto h-auto border-none hover:bg-gray-100 py-1 px-2 rounded-md"; + +export const breadcrumbActiveStyle = + "bg-indigo-50 text-indigo-700 hover:bg-indigo-100 [&>iconify-icon]:text-indigo-700"; diff --git a/frontend/app/src/screens/layout/breadcrumb-navigation/type.ts b/frontend/app/src/screens/layout/breadcrumb-navigation/type.ts new file mode 100644 index 0000000000..e76b6cac32 --- /dev/null +++ b/frontend/app/src/screens/layout/breadcrumb-navigation/type.ts @@ -0,0 +1,4 @@ +export type BreadcrumbItem = + | { type: "select"; value: string; kind: string } + | { type: "link"; label: string; to: string } + | { type: "branch"; value: string }; diff --git a/frontend/app/src/screens/layout/content.tsx b/frontend/app/src/screens/layout/content.tsx index 808f4d3dab..b6b67bd43b 100644 --- a/frontend/app/src/screens/layout/content.tsx +++ b/frontend/app/src/screens/layout/content.tsx @@ -1,4 +1,6 @@ import { Retry } from "@/components/buttons/retry"; +import { Badge } from "@/components/ui/badge"; +import { Card, CardProps } from "@/components/ui/card"; import { classNames } from "@/utils/common"; import { HTMLAttributes, ReactNode } from "react"; @@ -30,7 +32,8 @@ export const ContentTitle = ({ "min-h-[4rem] bg-custom-white flex items-center px-4 border-b", className )} - {...props}> + {...props} + >
{title && (
@@ -38,16 +41,65 @@ export const ContentTitle = ({ {reload && }
)} - {description &&

{description}

} + {description &&
{description}
}
{children} ); }; +export const ContentCard = ({ className, ...props }: CardProps) => { + return ; +}; + +export type ContentCardTitleProps = { + title?: ReactNode; + subtitle?: ReactNode; + description?: ReactNode; + end?: ReactNode; + badgeContent?: ReactNode; + reload?: () => void; + isReloadLoading?: boolean; + className?: string; +}; + +export const ContentCardTitle = ({ + badgeContent, + description, + isReloadLoading, + reload, + title, + className, + end, + ...props +}: ContentCardTitleProps) => { + return ( +
+
+ {title && ( +
+

{title}

+ {!!badgeContent && {badgeContent}} + {reload && } +
+ )} + {description &&
{description}
} +
+ {end} +
+ ); +}; + +export const ContentCardContent = ({ className, ...props }: HTMLAttributes) => { + return
; +}; + export const Content = Object.assign(ContentRoot, { Title: ContentTitle, Root: ContentRoot, + Card: ContentCard, + CardTitle: ContentCardTitle, + CardContent: ContentCardContent, }); export default Content; diff --git a/frontend/app/src/screens/layout/footer.tsx b/frontend/app/src/screens/layout/footer.tsx deleted file mode 100644 index 830683d027..0000000000 --- a/frontend/app/src/screens/layout/footer.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import { Button } from "@/components/buttons/button-primitive"; -import { Tooltip } from "@/components/ui/tooltip"; -import { - CONFIG, - INFRAHUB_API_SERVER_URL, - INFRAHUB_DOC_LOCAL, - INFRAHUB_GITHUB_URL, -} from "@/config/config"; -import { components } from "@/infraops"; -import { constructPath, fetchUrl } from "@/utils/fetch"; -import { Icon } from "@iconify-icon/react"; -import { useEffect, useState } from "react"; -import { Link } from "react-router-dom"; - -const AppVersionInfo = () => { - const [info, setInfo] = useState(null); - - const fetchInfo = async () => { - const result: components["schemas"]["InfoAPI"] = await fetchUrl(CONFIG.INFO_URL); - - setInfo(result); - }; - - useEffect(() => { - fetchInfo(); - }, []); - - if (!info) return null; - return
version {info.version}
; -}; - -export const Footer = () => { - return ( -
-
- - - - - - - - - - - - - - - - - - - - - - - -
- - -
- ); -}; diff --git a/frontend/app/src/screens/layout/header.tsx b/frontend/app/src/screens/layout/header.tsx index 2b02e1f5f8..75b1771de9 100644 --- a/frontend/app/src/screens/layout/header.tsx +++ b/frontend/app/src/screens/layout/header.tsx @@ -1,17 +1,22 @@ -import { AccountMenu } from "@/components/account-menu"; -import { SearchAnywhere } from "@/components/search/search-anywhere"; +import BranchSelector from "@/components/branch-selector"; import { TimeFrameSelector } from "@/components/time-selector"; +import InfrahubLogo from "@/images/infrahub-logo.svg"; +import BreadcrumbNavigation from "@/screens/layout/breadcrumb-navigation/breadcrumb-navigation"; +import { constructPath } from "@/utils/fetch"; +import { Link } from "react-router-dom"; export default function Header() { return ( -
- +
+ + Infrahub logo + -
- -
+ - + + +
); } diff --git a/frontend/app/src/screens/layout/layout.tsx b/frontend/app/src/screens/layout/layout.tsx index 644def4081..8848964f82 100644 --- a/frontend/app/src/screens/layout/layout.tsx +++ b/frontend/app/src/screens/layout/layout.tsx @@ -1,18 +1,18 @@ import { QSP } from "@/config/qsp"; import { SchemaContext, withSchemaContext } from "@/decorators/withSchemaContext"; import { Branch } from "@/generated/graphql"; -import graphqlClient from "@/graphql/graphqlClientApollo"; import GET_BRANCHES from "@/graphql/queries/branches/getBranches"; +import Sidebar from "@/screens/layout/sidebar"; import LoadingScreen from "@/screens/loading-screen/loading-screen"; import { branchesState, currentBranchAtom } from "@/state/atoms/branches.atom"; import { findSelectedBranch } from "@/utils/branches"; +import { NetworkStatus, useQuery } from "@apollo/client"; import { useSetAtom } from "jotai"; import { useAtomValue } from "jotai/index"; -import { useContext, useEffect, useState } from "react"; +import { useContext, useEffect } from "react"; +import { Outlet } from "react-router-dom"; import { StringParam, useQueryParam } from "use-query-params"; import Header from "./header"; -import { Sidebar } from "./sidebar"; -import { Outlet } from "react-router-dom"; function Layout() { const branches = useAtomValue(branchesState); @@ -20,17 +20,16 @@ function Layout() { const { checkSchemaUpdate } = useContext(SchemaContext); const setBranches = useSetAtom(branchesState); const setCurrentBranch = useSetAtom(currentBranchAtom); - const [isLoadingBranches, setIsLoadingBranches] = useState(true); - - const fetchBranches = async () => { - try { - const { data }: any = await graphqlClient.query({ - query: GET_BRANCHES, - context: { branch: branchInQueryString }, - }); - - return data.Branch ?? []; - } catch (err: any) { + const { networkStatus } = useQuery(GET_BRANCHES, { + notifyOnNetworkStatusChange: true, + onCompleted: (data) => { + const branches: Branch[] = data.Branch ?? []; + const selectedBranch = findSelectedBranch(branches, branchInQueryString); + + setBranches(branches); + setCurrentBranch(selectedBranch); + }, + onError: (err) => { console.error("err.message: ", err.message); if (err?.message?.includes("Received status code 401")) { @@ -38,34 +37,15 @@ function Layout() { } console.error("Error while fetching branches: ", err); - - return []; - } - }; - - /** - * Set branches in state atom - */ - const setBranchesInState = async () => { - const branches: Branch[] = await fetchBranches(); - - const selectedBranch = findSelectedBranch(branches, branchInQueryString); - - setBranches(branches); - setCurrentBranch(selectedBranch); - setIsLoadingBranches(false); - }; - - useEffect(() => { - setBranchesInState(); - }, []); + }, + }); useEffect(() => { if (branches.length === 0) return; checkSchemaUpdate(); }, [branches.length, branchInQueryString]); - if (isLoadingBranches) { + if (networkStatus === NetworkStatus.loading) { return (
@@ -74,13 +54,15 @@ function Layout() { } return ( -
- +
+
-
-
+
+ - +
+ +
); diff --git a/frontend/app/src/screens/layout/menu-navigation/components/collapsed-button.tsx b/frontend/app/src/screens/layout/menu-navigation/components/collapsed-button.tsx new file mode 100644 index 0000000000..3359fa3a89 --- /dev/null +++ b/frontend/app/src/screens/layout/menu-navigation/components/collapsed-button.tsx @@ -0,0 +1,27 @@ +import { ButtonProps, ButtonWithTooltip } from "@/components/buttons/button-primitive"; +import { classNames } from "@/utils/common"; +import { Icon } from "@iconify-icon/react"; +import { forwardRef } from "react"; + +export interface CollapsedButton extends ButtonProps { + tooltipContent: string; + icon: string; +} + +export const CollapsedButton = forwardRef( + ({ className, icon, ...props }, ref) => { + return ( + + + + ); + } +); diff --git a/frontend/app/src/screens/layout/menu-navigation/components/menu-section-internal.tsx b/frontend/app/src/screens/layout/menu-navigation/components/menu-section-internal.tsx new file mode 100644 index 0000000000..b613adf53a --- /dev/null +++ b/frontend/app/src/screens/layout/menu-navigation/components/menu-section-internal.tsx @@ -0,0 +1,110 @@ +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuSub, + DropdownMenuSubContent, + DropdownMenuSubTrigger, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { CollapsedButton } from "@/screens/layout/menu-navigation/components/collapsed-button"; +import { menuNavigationItemStyle } from "@/screens/layout/menu-navigation/styles"; +import type { MenuItem } from "@/screens/layout/menu-navigation/types"; +import { classNames } from "@/utils/common"; +import { constructPath } from "@/utils/fetch"; +import { Icon } from "@iconify-icon/react"; +import React from "react"; +import { Link } from "react-router-dom"; + +export interface MenuSectionInternalProps { + items: MenuItem[]; + isCollapsed?: boolean; +} + +const RecursiveInternalMenuItem: React.FC<{ item: MenuItem }> = ({ item }) => { + if (!item.children?.length) { + return ( + + {item.label} + + ); + } + + return ( + + {item.label} + + {item.children.map((childItem) => ( + + ))} + + + ); +}; + +const CollapsedMenuItemLink: React.FC<{ item: MenuItem }> = ({ item }) => ( + + + +); + +const ExpandedMenuItemLink: React.FC<{ item: MenuItem }> = ({ item }) => ( + + + {item.label} + + +); + +const DropdownMenuTriggerButton: React.FC<{ item: MenuItem; isCollapsed: boolean }> = ({ + item, + isCollapsed, +}) => ( + + {isCollapsed ? ( + + ) : ( + <> + + {item.label} + + + )} + +); + +export function MenuSectionInternal({ items, isCollapsed }: MenuSectionInternalProps) { + return ( +
+ {items.map((item) => { + if (!item.children?.length) { + return isCollapsed ? ( + + ) : ( + + ); + } + + return ( + + + + {item.children.map((childItem) => ( + + ))} + + + ); + })} +
+ ); +} diff --git a/frontend/app/src/screens/layout/menu-navigation/components/menu-section-object.tsx b/frontend/app/src/screens/layout/menu-navigation/components/menu-section-object.tsx new file mode 100644 index 0000000000..74bcd50596 --- /dev/null +++ b/frontend/app/src/screens/layout/menu-navigation/components/menu-section-object.tsx @@ -0,0 +1,132 @@ +import { + DropdownMenu, + DropdownMenuAccordion, + DropdownMenuAccordionContent, + DropdownMenuAccordionTrigger, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { Tooltip } from "@/components/ui/tooltip"; +import { ObjectAvatar } from "@/screens/layout/menu-navigation/components/object-avatar"; +import { menuNavigationItemStyle } from "@/screens/layout/menu-navigation/styles"; +import { MenuItem } from "@/screens/layout/menu-navigation/types"; +import { classNames } from "@/utils/common"; +import { constructPath } from "@/utils/fetch"; +import { Icon } from "@iconify-icon/react"; +import React from "react"; +import { Link } from "react-router-dom"; + +export interface MenuSectionObjectsProps { + items: MenuItem[]; + isCollapsed?: boolean; +} + +const MenuItemIcon: React.FC<{ item: MenuItem }> = ({ item }) => { + if (item.icon) { + return ; + } + return ; +}; + +const RecursiveObjectMenuItem: React.FC<{ + item: MenuItem; + isCollapsed?: boolean; + level?: number; +}> = ({ item, isCollapsed, level = 0 }) => { + if (!item.children?.length) { + return ( + + + + {item.label} + + + ); + } + + return ( + + + + {item.path ? ( + + {item.label} + + ) : ( + item.label + )} + + + + {item.children.map((child) => ( + + ))} + + + ); +}; + +const TopLevelMenuItem: React.FC<{ + item: MenuItem; + isCollapsed?: boolean; +}> = ({ item, isCollapsed }) => { + if (!item.children?.length) { + return ( + + + {item.label} + + ); + } + + return ( + + + + + + + + + + {item.label} + + + + +

{item.label}

+ {item.children.map((child) => ( + + ))} +
+
+ ); +}; + +export const MenuSectionObject: React.FC = ({ isCollapsed, items }) => ( +
+ {items.map((item) => ( + + ))} +
+); diff --git a/frontend/app/src/screens/layout/menu-navigation/components/object-avatar.tsx b/frontend/app/src/screens/layout/menu-navigation/components/object-avatar.tsx new file mode 100644 index 0000000000..7a5da232bc --- /dev/null +++ b/frontend/app/src/screens/layout/menu-navigation/components/object-avatar.tsx @@ -0,0 +1,27 @@ +import { classNames } from "@/utils/common"; + +const STYLES = [ + "bg-green-50 text-green-600", + "bg-yellow-50 text-yellow-600", + "bg-indigo-50 text-indigo-600", + "bg-orange-50 text-orange-600", + "bg-pink-50 text-pink-600", + "bg-purple-50 text-purple-600", + "bg-blue-50 text-blue-600", +]; + +export function ObjectAvatar({ name = "" }: { name: string }) { + const firstLetter = name[0]; + if (!firstLetter) { + return
; + } + + const styleIndex = firstLetter.charCodeAt(0) % STYLES.length; + return ( +
+ {firstLetter.toUpperCase()} +
+ ); +} diff --git a/frontend/app/src/screens/layout/menu-navigation/menu-navigation.tsx b/frontend/app/src/screens/layout/menu-navigation/menu-navigation.tsx new file mode 100644 index 0000000000..e37a4530a5 --- /dev/null +++ b/frontend/app/src/screens/layout/menu-navigation/menu-navigation.tsx @@ -0,0 +1,55 @@ +import { ALERT_TYPES, Alert } from "@/components/ui/alert"; +import { Divider } from "@/components/ui/divider"; +import { ScrollArea } from "@/components/ui/scroll-area"; +import { CONFIG } from "@/config/config"; +import { useAuth } from "@/hooks/useAuth"; +import { MenuSectionInternal } from "@/screens/layout/menu-navigation/components/menu-section-internal"; +import { MenuSectionObject } from "@/screens/layout/menu-navigation/components/menu-section-object"; +import { currentBranchAtom } from "@/state/atoms/branches.atom"; +import { menuAtom } from "@/state/atoms/schema.atom"; +import { fetchUrl } from "@/utils/fetch"; +import { useAtom, useAtomValue } from "jotai"; +import { useEffect, useState } from "react"; +import { toast } from "react-toastify"; + +export interface MenuNavigationProps { + isCollapsed?: boolean; +} + +export default function MenuNavigation({ isCollapsed }: MenuNavigationProps) { + const { accessToken } = useAuth(); + const currentBranch = useAtomValue(currentBranchAtom); + const [menu, setMenu] = useAtom(menuAtom); + const [isLoading, setIsLoading] = useState(false); + + useEffect(() => { + if (!currentBranch) return; + + const headers = accessToken && { + authorization: `Bearer ${accessToken}`, + }; + + try { + setIsLoading(true); + fetchUrl(CONFIG.MENU_URL(currentBranch?.name), { headers }).then((menu) => setMenu(menu)); + } catch (error) { + console.error("error: ", error); + toast(); + } finally { + setIsLoading(false); + } + }, [currentBranch, accessToken]); + + if (isLoading) return
Loading...
; + if (!menu?.sections) return
; + + return ( + <> + + + + + + + ); +} diff --git a/frontend/app/src/screens/layout/menu-navigation/styles.tsx b/frontend/app/src/screens/layout/menu-navigation/styles.tsx new file mode 100644 index 0000000000..57ba3b5f19 --- /dev/null +++ b/frontend/app/src/screens/layout/menu-navigation/styles.tsx @@ -0,0 +1,2 @@ +export const menuNavigationItemStyle = + "flex items-center outline-none gap-2 p-2 rounded font-medium text-neutral-900 hover:bg-neutral-100 focus:bg-neutral-100 group data-[state=open]:bg-indigo-50 data-[state=open]:text-indigo-700"; diff --git a/frontend/app/src/screens/layout/menu-navigation/types.ts b/frontend/app/src/screens/layout/menu-navigation/types.ts new file mode 100644 index 0000000000..30ec6b0576 --- /dev/null +++ b/frontend/app/src/screens/layout/menu-navigation/types.ts @@ -0,0 +1,10 @@ +import { components } from "@/infraops"; + +export type MenuItem = components["schemas"]["MenuItemList"]; + +export type MenuData = { + sections: { + object: MenuItem[]; + internal: MenuItem[]; + }; +}; diff --git a/frontend/app/src/screens/layout/navigation-list.tsx b/frontend/app/src/screens/layout/navigation-list.tsx deleted file mode 100644 index a35073d9e8..0000000000 --- a/frontend/app/src/screens/layout/navigation-list.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import { - BoltIcon, - ChartBarIcon, - CpuChipIcon, - LinkIcon, - ListBulletIcon, - WifiIcon, -} from "@heroicons/react/24/outline"; - -export const navigation = [ - { name: "Dashboard", icon: ListBulletIcon, current: true, href: "#" }, - { - name: "Connections", - icon: LinkIcon, - current: false, - children: [ - { name: "Cables", href: "#" }, - { name: "Wireless Links", href: "#" }, - { name: "Interface Connections", href: "#" }, - { name: "Console Connections", href: "#" }, - { name: "Power Connections", href: "#" }, - ], - }, - { - name: "Wireless", - icon: WifiIcon, - current: false, - children: [ - { name: "Wireless LANs", href: "#" }, - { name: "Wireless LAN Groups", href: "#" }, - ], - }, - { - name: "Power", - icon: BoltIcon, - current: false, - children: [ - { name: "Power Feeds", href: "#" }, - { name: "Power Panels", href: "#" }, - ], - }, - { - name: "Virtualization", - icon: CpuChipIcon, - current: false, - children: [ - { name: "Virtual Machines", href: "#" }, - { name: "Interfaces", href: "#" }, - { name: "Clusters", href: "#" }, - { name: "Cluster Types", href: "#" }, - { name: "Cluster Groups", href: "#" }, - ], - }, - { - name: "Reports", - icon: ChartBarIcon, - current: false, - children: [ - { name: "Overview", href: "#" }, - { name: "Devices", href: "#" }, - { name: "Settings", href: "#" }, - ], - }, -]; - -export const userNavigation = [{ name: "Your Profile", href: "/profile" }]; diff --git a/frontend/app/src/screens/layout/sidebar.tsx b/frontend/app/src/screens/layout/sidebar.tsx new file mode 100644 index 0000000000..9b574812cf --- /dev/null +++ b/frontend/app/src/screens/layout/sidebar.tsx @@ -0,0 +1,46 @@ +import { AccountMenu } from "@/components/account-menu"; +import { Button } from "@/components/buttons/button-primitive"; +import { SearchAnywhere } from "@/components/search/search-anywhere"; +import { SIDEBAR_COLLAPSED_KEY } from "@/config/localStorage"; +import { useLocalStorage } from "@/hooks/useLocalStorage"; +import MenuNavigation from "@/screens/layout/menu-navigation/menu-navigation"; +import { classNames } from "@/utils/common"; +import { Icon } from "@iconify-icon/react"; + +export default function Sidebar() { + const [collapsed, setCollapsed] = useLocalStorage(SIDEBAR_COLLAPSED_KEY); + + const booleanCollapsed = collapsed === "true"; + + return ( + + ); +} diff --git a/frontend/app/src/screens/layout/sidebar/desktop-menu-header.tsx b/frontend/app/src/screens/layout/sidebar/desktop-menu-header.tsx deleted file mode 100644 index 27199356fb..0000000000 --- a/frontend/app/src/screens/layout/sidebar/desktop-menu-header.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import { Circle } from "@/components/display/circle"; -import { classNames } from "@/utils/common"; -import { Disclosure } from "@headlessui/react"; -import { Icon } from "@iconify-icon/react"; -import { MenuItem } from "./desktop-menu"; -import { DropDownMenuItem } from "./desktop-menu-item"; - -interface Props { - title: string; - items: MenuItem[]; - icon?: string; - subItem?: boolean; -} - -export default function DropDownMenuHeader(props: Props) { - const { title, items, icon, subItem } = props; - - return ( - - {({ open }) => ( - <> - - {icon ? ( - - ) : ( - - )} - - {title} - - - - - - {items.map((item, index: number) => { - if (item.children?.length) { - return ( - - ); - } - - return ; - })} - - - )} - - ); -} diff --git a/frontend/app/src/screens/layout/sidebar/desktop-menu-item.tsx b/frontend/app/src/screens/layout/sidebar/desktop-menu-item.tsx deleted file mode 100644 index 0cd49438a9..0000000000 --- a/frontend/app/src/screens/layout/sidebar/desktop-menu-item.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import useFilters from "@/hooks/useFilters"; -import { classNames } from "@/utils/common"; -import { constructPath } from "@/utils/fetch"; -import { Icon } from "@iconify-icon/react"; -import { NavLink } from "react-router-dom"; - -interface DropDownMenuItemProps { - path: string; - title?: string; - icon?: string; -} - -export const DropDownMenuItem = ({ path, icon, title }: DropDownMenuItemProps) => { - const [, setFilters] = useFilters(); - const onClickMenuItem = () => setFilters([]); - - return ( - - classNames( - "p-2 group flex items-center rounded text-sm font-medium text-gray-600", - isActive ? "bg-gray-300" : "hover:bg-gray-100 hover:text-gray-900" - ) - }> - {icon && } - - {title} - - ); -}; diff --git a/frontend/app/src/screens/layout/sidebar/desktop-menu.tsx b/frontend/app/src/screens/layout/sidebar/desktop-menu.tsx deleted file mode 100644 index 43eb137b44..0000000000 --- a/frontend/app/src/screens/layout/sidebar/desktop-menu.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import { ALERT_TYPES, Alert } from "@/components/ui/alert"; -import { SearchInput } from "@/components/ui/search-input"; -import { CONFIG } from "@/config/config"; -import LoadingScreen from "@/screens/loading-screen/loading-screen"; -import { currentBranchAtom } from "@/state/atoms/branches.atom"; -import { currentSchemaHashAtom, menuAtom } from "@/state/atoms/schema.atom"; -import { classNames } from "@/utils/common"; -import { fetchUrl } from "@/utils/fetch"; -import { useAtom, useAtomValue } from "jotai/index"; -import { useEffect, useMemo, useState } from "react"; -import { toast } from "react-toastify"; -import DropDownMenuHeader from "./desktop-menu-header"; - -export type MenuItem = { - title: string; - path: string; - icon: string; - children: MenuItem[]; - kind: string; -}; - -type MenuProps = { - className?: string; -}; -export function DesktopMenu({ className = "" }: MenuProps) { - const branch = useAtomValue(currentBranchAtom); - const currentSchemaHash = useAtomValue(currentSchemaHashAtom); - const [menu, setMenu] = useAtom(menuAtom); - - const [isLoading, setIsLoading] = useState(false); - const [query, setQuery] = useState(""); - - const fetchMenu = async () => { - if (!currentSchemaHash) return; - - try { - setIsLoading(true); - - const result: MenuItem[] = await fetchUrl(CONFIG.MENU_URL(branch?.name)); - - setMenu(result); - - setIsLoading(false); - } catch (error) { - console.error("error: ", error); - toast(); - setIsLoading(false); - } - }; - - useEffect(() => { - fetchMenu(); - }, [currentSchemaHash]); - - function filterDataByString(data: MenuItem[], searchString: string): MenuItem[] { - const lowercaseSearch = searchString.toLowerCase(); - - return data.reduce((acc, item) => { - const lowercaseTitle = item.title.toLowerCase(); - const filteredChildren = filterDataByString(item.children || [], searchString); - - if (filteredChildren.length > 0 || lowercaseTitle.includes(lowercaseSearch)) { - acc.push({ - ...item, - children: filteredChildren.length > 0 ? filteredChildren : item.children, - }); - } - - return acc; - }, [] as MenuItem[]); - } - - const menuFiltered = useMemo( - () => filterDataByString(menu, query), - [currentSchemaHash, query, menu.length] - ); - - return ( -
- setQuery(e.target.value)} - className="shadow-none border-0 rounded-none border-b border-gray-200 focus-visible:ring-0 py-4" - placeholder="Search menu" - data-testid="search-menu" - /> - - {isLoading && } - - {!isLoading && ( - - )} -
- ); -} diff --git a/frontend/app/src/screens/layout/sidebar/index.tsx b/frontend/app/src/screens/layout/sidebar/index.tsx deleted file mode 100644 index e5837231af..0000000000 --- a/frontend/app/src/screens/layout/sidebar/index.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import BranchSelector from "@/components/branch-selector"; -import { ReactComponent as InfrahubLogo } from "@/images/Infrahub-SVG-hori.svg"; -import { Footer } from "@/screens/layout/footer"; -import { Link } from "react-router-dom"; -import { DesktopMenu } from "./desktop-menu"; - -export function Sidebar() { - return ( -
-
- - - - -
- -
- - -
- -
-
- ); -} diff --git a/frontend/app/src/screens/loading-screen/loading-screen.tsx b/frontend/app/src/screens/loading-screen/loading-screen.tsx index 8b0fb76476..3cb49ef7c9 100644 --- a/frontend/app/src/screens/loading-screen/loading-screen.tsx +++ b/frontend/app/src/screens/loading-screen/loading-screen.tsx @@ -20,7 +20,8 @@ export default function LoadingScreen(props: Props) { className={classNames( "flex flex-col items-center justify-center h-auto w-auto", className ?? "" - )}> + )} + > } setShowEditModal(true)} - data-testid="edit-button"> + data-testid="edit-button" + > {!schema.kind?.match(/Core.*Group/g)?.length && ( // Hide group buttons on group list view @@ -57,13 +57,14 @@ export function DetailsButtons({ schema, objectDetailsData }: DetailsButtonsProp )} setShowDeleteModal(true)}> + onClick={() => setShowDeleteModal(true)} + >
@@ -78,7 +79,8 @@ export function DetailsButtons({ schema, objectDetailsData }: DetailsButtonsProp /> } open={showEditModal} - setOpen={setShowEditModal}> + setOpen={setShowEditModal} + > setShowEditModal(false)} onUpdateComplete={() => graphqlClient.refetchQueries({ include: [schema.kind!] })} diff --git a/frontend/app/src/screens/object-item-details/action-buttons/index.tsx b/frontend/app/src/screens/object-item-details/action-buttons/index.tsx index d991b74175..fd8471e450 100644 --- a/frontend/app/src/screens/object-item-details/action-buttons/index.tsx +++ b/frontend/app/src/screens/object-item-details/action-buttons/index.tsx @@ -1,7 +1,7 @@ +import { TASK_TAB } from "@/config/constants"; +import { QSP } from "@/config/qsp"; import { StringParam, useQueryParam } from "use-query-params"; import { DetailsButtons } from "./details-buttons"; -import { QSP } from "@/config/qsp"; -import { TASK_TAB } from "@/config/constants"; import { RelationshipsButtons } from "./relationships-buttons"; export function ActionButtons(props: any) { diff --git a/frontend/app/src/screens/object-item-details/action-buttons/relationships-buttons.tsx b/frontend/app/src/screens/object-item-details/action-buttons/relationships-buttons.tsx index 965c36d22e..7447477eba 100644 --- a/frontend/app/src/screens/object-item-details/action-buttons/relationships-buttons.tsx +++ b/frontend/app/src/screens/object-item-details/action-buttons/relationships-buttons.tsx @@ -2,12 +2,12 @@ import { ButtonWithTooltip } from "@/components/buttons/button-primitive"; import SlideOver, { SlideOverTitle } from "@/components/display/slide-over"; import DynamicForm from "@/components/form/dynamic-form"; import { SelectOption } from "@/components/inputs/select"; -import { Alert, ALERT_TYPES } from "@/components/ui/alert"; +import { ALERT_TYPES, Alert } from "@/components/ui/alert"; import { QSP } from "@/config/qsp"; import graphqlClient from "@/graphql/graphqlClientApollo"; import { ADD_RELATIONSHIP } from "@/graphql/mutations/relationships/addRelationship"; -import { usePermission } from "@/hooks/usePermission"; import { useMutation } from "@/hooks/useQuery"; +import { Permission } from "@/screens/permission/types"; import { genericsState, schemaState } from "@/state/atoms/schema.atom"; import { Icon } from "@iconify-icon/react"; import { useAtomValue } from "jotai"; @@ -16,8 +16,11 @@ import { useParams } from "react-router-dom"; import { toast } from "react-toastify"; import { StringParam, useQueryParam } from "use-query-params"; -export function RelationshipsButtons() { - const permission = usePermission(); +interface RelationshipsButtonsProps { + permission: Permission; +} + +export function RelationshipsButtons({ permission }: RelationshipsButtonsProps) { const { objectKind, objectid } = useParams(); const [addRelationship] = useMutation(ADD_RELATIONSHIP); const generics = useAtomValue(genericsState); @@ -89,11 +92,12 @@ export function RelationshipsButtons() { return ( <> setShowAddDrawer(true)} - data-testid="open-relationship-form-button"> + data-testid="open-relationship-form-button" + > @@ -110,7 +114,8 @@ export function RelationshipsButtons() { ) } open={showAddDrawer} - setOpen={setShowAddDrawer}> + setOpen={setShowAddDrawer} + > { return ( -
+
{name}
{value} diff --git a/frontend/app/src/screens/object-item-details/object-item-details-paginated.tsx b/frontend/app/src/screens/object-item-details/object-item-details-paginated.tsx index f64b99945f..1c5340ea71 100644 --- a/frontend/app/src/screens/object-item-details/object-item-details-paginated.tsx +++ b/frontend/app/src/screens/object-item-details/object-item-details-paginated.tsx @@ -5,15 +5,15 @@ import { Tabs } from "@/components/tabs"; import { Link } from "@/components/ui/link"; import { DEFAULT_BRANCH_NAME, MENU_EXCLUDELIST, TASK_TAB, TASK_TARGET } from "@/config/constants"; import { QSP } from "@/config/qsp"; -import { usePermission } from "@/hooks/usePermission"; +import graphqlClient from "@/graphql/graphqlClientApollo"; import { useTitle } from "@/hooks/useTitle"; -import NoDataFound from "@/screens/errors/no-data-found"; import ObjectItemMetaEdit from "@/screens/object-item-meta-edit/object-item-meta-edit"; +import { Permission } from "@/screens/permission/types"; import { TaskItemDetails } from "@/screens/tasks/task-item-details"; import { TaskItems } from "@/screens/tasks/task-items"; import { currentBranchAtom } from "@/state/atoms/branches.atom"; import { showMetaEditState } from "@/state/atoms/metaEditFieldDetails.atom"; -import { genericsState, IModelSchema, schemaState } from "@/state/atoms/schema.atom"; +import { IModelSchema, genericsState, schemaState } from "@/state/atoms/schema.atom"; import { metaEditFieldDetailsState } from "@/state/atoms/showMetaEdit.atom copy"; import { constructPath } from "@/utils/fetch"; import { ObjectAttributeValue } from "@/utils/getObjectItemDisplayValue"; @@ -30,22 +30,23 @@ import { useAtomValue } from "jotai/index"; import { useRef } from "react"; import { Navigate, useLocation } from "react-router-dom"; import { StringParam, useQueryParam } from "use-query-params"; +import { ActionButtons } from "./action-buttons"; import { ObjectAttributeRow } from "./object-attribute-row"; import RelationshipDetails from "./relationship-details-paginated"; import { RelationshipsDetails } from "./relationships-details-paginated"; -import graphqlClient from "@/graphql/graphqlClientApollo"; -import { ActionButtons } from "./action-buttons"; type ObjectDetailsProps = { schema: IModelSchema; objectDetailsData: any; taskData?: Object; hideHeaders?: boolean; + permission: Permission; }; export default function ObjectItemDetails({ schema, objectDetailsData, + permission, taskData, hideHeaders, }: ObjectDetailsProps) { @@ -54,7 +55,6 @@ export default function ObjectItemDetails({ const [qspTab, setQspTab] = useQueryParam(QSP.TAB, StringParam); const [qspTaskId, setQspTaskId] = useQueryParam(QSP.TASK_ID, StringParam); - const permission = usePermission(); const [showMetaEditModal, setShowMetaEditModal] = useAtom(showMetaEditState); const [metaEditFieldDetails, setMetaEditFieldDetails] = useAtom(metaEditFieldDetailsState); const branch = useAtomValue(currentBranchAtom); @@ -82,14 +82,6 @@ export default function ObjectItemDetails({ : `${schema.label} details` ); - if (!objectDetailsData) { - return ( -
- -
- ); - } - const tabs = [ { label: schema?.label, @@ -117,7 +109,13 @@ export default function ObjectItemDetails({ {!hideHeaders && ( } + rightItems={ + + } /> )} @@ -151,9 +149,9 @@ export default function ObjectItemDetails({
{attribute.label}
{ setMetaEditFieldDetails({ type: "attribute", @@ -165,7 +163,8 @@ export default function ObjectItemDetails({ variant="ghost" size="icon" data-testid="edit-metadata-button" - data-cy="metadata-edit-button"> + data-cy="metadata-edit-button" + >
@@ -226,7 +225,8 @@ export default function ObjectItemDetails({ { name: QSP.TAB, value: TASK_TAB }, { name: QSP.TASK_ID, exclude: true }, ])} - className="flex items-center p-2 "> + className="flex items-center p-2 " + > All tasks @@ -251,7 +251,8 @@ export default function ObjectItemDetails({
} open={showMetaEditModal} - setOpen={setShowMetaEditModal}> + setOpen={setShowMetaEditModal} + > setShowMetaEditModal(false)} onUpdateComplete={() => graphqlClient.refetchQueries({ include: [schema.kind!] })} diff --git a/frontend/app/src/screens/object-item-details/relationship-details-paginated.tsx b/frontend/app/src/screens/object-item-details/relationship-details-paginated.tsx index 4454b7f42d..8671e808cb 100644 --- a/frontend/app/src/screens/object-item-details/relationship-details-paginated.tsx +++ b/frontend/app/src/screens/object-item-details/relationship-details-paginated.tsx @@ -6,9 +6,11 @@ import { ALERT_TYPES, Alert } from "@/components/ui/alert"; import { Link as StyledLink } from "@/components/ui/link"; import graphqlClient from "@/graphql/graphqlClientApollo"; import { updateObjectWithId } from "@/graphql/mutations/objects/updateObjectWithId"; -import { usePermission } from "@/hooks/usePermission"; +import useQuery from "@/hooks/useQuery"; import NoDataFound from "@/screens/errors/no-data-found"; import ObjectItemEditComponent from "@/screens/object-item-edit/object-item-edit-paginated"; +import { ObjectItemsCell, TextCell } from "@/screens/object-items/object-items-cell"; +import { getPermission } from "@/screens/permission/utils"; import { currentBranchAtom } from "@/state/atoms/branches.atom"; import { showMetaEditState } from "@/state/atoms/metaEditFieldDetails.atom"; import { schemaState } from "@/state/atoms/schema.atom"; @@ -25,8 +27,11 @@ import { useAtom, useAtomValue } from "jotai"; import { Fragment, useState } from "react"; import { Link, useParams } from "react-router-dom"; import { toast } from "react-toastify"; +import ErrorScreen from "../errors/error-screen"; +import UnauthorizedScreen from "../errors/unauthorized-screen"; +import LoadingScreen from "../loading-screen/loading-screen"; +import { getObjectPermissionsQuery } from "../permission/queries/getObjectPermissions"; import { ObjectAttributeRow } from "./object-attribute-row"; -import { ObjectItemsCell, TextCell } from "@/screens/object-items/object-items-cell"; type iRelationDetailsProps = { parentNode: any; @@ -51,7 +56,6 @@ export default function RelationshipDetails(props: iRelationDetailsProps) { } = props; const { objectKind, objectid } = useParams(); - const permission = usePermission(); const schemaList = useAtomValue(schemaState); const branch = useAtomValue(currentBranchAtom); @@ -78,6 +82,24 @@ export default function RelationshipDetails(props: iRelationDetailsProps) { const [, setShowMetaEditModal] = useAtom(showMetaEditState); const [, setMetaEditFieldDetails] = useAtom(metaEditFieldDetailsState); + const { loading, data, error } = useQuery(gql(getObjectPermissionsQuery(objectKind))); + + const permission = data && getPermission(data?.[objectKind]?.permissions?.edges); + + if (error) { + if (error.networkError?.statusCode === 403) { + const { message } = error.networkError?.result?.errors?.[0] ?? {}; + + return ; + } + + return ; + } + + if (loading) { + return ; + } + if (relationshipsData && relationshipsData?.properties?.is_visible === false) { return null; } @@ -156,7 +178,8 @@ export default function RelationshipDetails(props: iRelationDetailsProps) { relationshipsData.node?.id, relationshipsData.node?.__typename ) - )}> + )} + > {relationshipsData.node?.display_label} ) : ( @@ -177,9 +200,9 @@ export default function RelationshipDetails(props: iRelationDetailsProps) { { setMetaEditFieldDetails({ type: "relationship", @@ -188,7 +211,8 @@ export default function RelationshipDetails(props: iRelationDetailsProps) { }); setShowMetaEditModal(true); }} - data-cy="metadata-edit-button"> + data-cy="metadata-edit-button" + >
@@ -229,7 +253,8 @@ export default function RelationshipDetails(props: iRelationDetailsProps) {
+ data-testid="relationship-row" + > {newColumns?.map((column) => ( @@ -360,7 +387,8 @@ export default function RelationshipDetails(props: iRelationDetailsProps) { ) } open={!!relatedObjectToEdit} - setOpen={() => setRelatedObjectToEdit(undefined)}> + setOpen={() => setRelatedObjectToEdit(undefined)} + > { setRelatedObjectToEdit(undefined); diff --git a/frontend/app/src/screens/object-item-edit/generateObjectEditFormQuery.ts b/frontend/app/src/screens/object-item-edit/generateObjectEditFormQuery.ts index ea63b9cecc..51019f12e1 100644 --- a/frontend/app/src/screens/object-item-edit/generateObjectEditFormQuery.ts +++ b/frontend/app/src/screens/object-item-edit/generateObjectEditFormQuery.ts @@ -1,16 +1,14 @@ -import { iNodeSchema, IProfileSchema } from "@/state/atoms/schema.atom"; -import { jsonToGraphQLQuery } from "json-to-graphql-query"; -import { addAttributesToRequest, addRelationshipsToRequest } from "@/graphql/utils"; import { getRelationshipsForForm } from "@/components/form/utils/getRelationshipsForForm"; +import { addAttributesToRequest, addRelationshipsToRequest } from "@/graphql/utils"; +import { IProfileSchema, iNodeSchema } from "@/state/atoms/schema.atom"; +import { jsonToGraphQLQuery } from "json-to-graphql-query"; export const generateObjectEditFormQuery = ({ schema, objectId, - withProfiles, }: { schema: iNodeSchema | IProfileSchema; objectId: string; - withProfiles?: boolean; }): string => { const request = { query: { @@ -23,7 +21,7 @@ export const generateObjectEditFormQuery = ({ node: { id: true, display_label: true, - ...addAttributesToRequest(schema.attributes ?? []), + ...addAttributesToRequest(schema.attributes ?? [], { withPermissions: true }), ...addRelationshipsToRequest(getRelationshipsForForm(schema.relationships ?? [], true)), ...("generate_profile" in schema && schema.generate_profile ? { diff --git a/frontend/app/src/screens/object-item-edit/object-item-edit-paginated.tsx b/frontend/app/src/screens/object-item-edit/object-item-edit-paginated.tsx index a4a4310f4c..4e7667d68a 100644 --- a/frontend/app/src/screens/object-item-edit/object-item-edit-paginated.tsx +++ b/frontend/app/src/screens/object-item-edit/object-item-edit-paginated.tsx @@ -1,22 +1,22 @@ +import ObjectForm, { ObjectFormProps } from "@/components/form/object-form"; +import { getUpdateMutationFromFormData } from "@/components/form/utils/mutations/getUpdateMutationFromFormData"; import { ALERT_TYPES, Alert } from "@/components/ui/alert"; import graphqlClient from "@/graphql/graphqlClientApollo"; import { updateObjectWithId } from "@/graphql/mutations/objects/updateObjectWithId"; import useQuery from "@/hooks/useQuery"; +import { useSchema } from "@/hooks/useSchema"; import { DynamicFieldData } from "@/screens/edit-form-hook/dynamic-control-types"; import ErrorScreen from "@/screens/errors/error-screen"; import NoDataFound from "@/screens/errors/no-data-found"; import LoadingScreen from "@/screens/loading-screen/loading-screen"; +import { generateObjectEditFormQuery } from "@/screens/object-item-edit/generateObjectEditFormQuery"; import { currentBranchAtom } from "@/state/atoms/branches.atom"; import { datetimeAtom } from "@/state/atoms/time.atom"; +import { areObjectArraysEqualById } from "@/utils/array"; import { stringifyWithoutQuotes } from "@/utils/string"; import { gql } from "@apollo/client"; import { useAtomValue } from "jotai/index"; import { toast } from "react-toastify"; -import ObjectForm, { ObjectFormProps } from "@/components/form/object-form"; -import { getUpdateMutationFromFormData } from "@/components/form/utils/mutations/getUpdateMutationFromFormData"; -import { areObjectArraysEqualById } from "@/utils/array"; -import { generateObjectEditFormQuery } from "@/screens/object-item-edit/generateObjectEditFormQuery"; -import { useSchema } from "@/hooks/useSchema"; interface Props { objectname: string; diff --git a/frontend/app/src/screens/object-item-meta-edit/object-item-meta-edit.tsx b/frontend/app/src/screens/object-item-meta-edit/object-item-meta-edit.tsx index 95c783c41e..304a27b0d4 100644 --- a/frontend/app/src/screens/object-item-meta-edit/object-item-meta-edit.tsx +++ b/frontend/app/src/screens/object-item-meta-edit/object-item-meta-edit.tsx @@ -1,3 +1,5 @@ +import DynamicForm from "@/components/form/dynamic-form"; +import { getRelationshipDefaultValue } from "@/components/form/utils/getRelationshipDefaultValue"; import { ALERT_TYPES, Alert } from "@/components/ui/alert"; import graphqlClient from "@/graphql/graphqlClientApollo"; import { updateObjectWithId } from "@/graphql/mutations/objects/updateObjectWithId"; @@ -9,9 +11,7 @@ import { stringifyWithoutQuotes } from "@/utils/string"; import { gql } from "@apollo/client"; import { useAtomValue } from "jotai/index"; import { toast } from "react-toastify"; -import DynamicForm from "@/components/form/dynamic-form"; import { mapValues } from "remeda"; -import { getRelationshipDefaultValue } from "@/components/form/utils/getRelationshipDefaultValue"; interface Props { row: any; diff --git a/frontend/app/src/screens/object-items/object-items-cell.tsx b/frontend/app/src/screens/object-items/object-items-cell.tsx index b2342a8219..4ad9bae94b 100644 --- a/frontend/app/src/screens/object-items/object-items-cell.tsx +++ b/frontend/app/src/screens/object-items/object-items-cell.tsx @@ -1,14 +1,14 @@ -import { getObjectDetailsUrl2 } from "@/utils/objects"; -import { Link, LinkProps } from "react-router-dom"; +import { Badge } from "@/components/ui/badge"; +import { AttributeSchema, RelationshipSchema } from "@/screens/schema/types"; +import { classNames } from "@/utils/common"; import { - getDisplayValue, RelationshipManyType, RelationshipOneType, + getDisplayValue, } from "@/utils/getObjectItemDisplayValue"; -import { AttributeSchema, RelationshipSchema } from "@/screens/schema/types"; -import { Badge } from "@/components/ui/badge"; -import { classNames } from "@/utils/common"; +import { getObjectDetailsUrl2 } from "@/utils/objects"; import { HTMLAttributes } from "react"; +import { Link, LinkProps } from "react-router-dom"; type ObjectItemsCellProps = { row: any; @@ -34,7 +34,9 @@ export const ObjectItemsCell = ({ row, attribute }: ObjectItemsCellProps) => { }; export const TextCell = ({ className, ...props }: HTMLAttributes) => { - return ; + return ( + + ); }; export const LinkCell = ({ className, children, ...props }: LinkProps) => { @@ -51,7 +53,8 @@ export const RelationshipOneCell = ({ data }: { data: RelationshipOneType }) => return ( + className="hover:underline" + > {data.node.display_label} ); diff --git a/frontend/app/src/screens/object-items/object-items-paginated.tsx b/frontend/app/src/screens/object-items/object-items-paginated.tsx index 418cc74671..3d08588c4c 100644 --- a/frontend/app/src/screens/object-items/object-items-paginated.tsx +++ b/frontend/app/src/screens/object-items/object-items-paginated.tsx @@ -1,5 +1,7 @@ import { ButtonWithTooltip } from "@/components/buttons/button-primitive"; import { Filters } from "@/components/filters/filters"; +import { ObjectCreateFormTrigger } from "@/components/form/object-create-form-trigger"; +import ModalDeleteObject from "@/components/modals/modal-delete-object"; import { Pagination } from "@/components/ui/pagination"; import { SearchInput, SearchInputProps } from "@/components/ui/search-input"; import { @@ -9,22 +11,22 @@ import { SEARCH_PARTIAL_MATCH, } from "@/config/constants"; import useFilters, { Filter } from "@/hooks/useFilters"; -import { usePermission } from "@/hooks/usePermission"; +import { useObjectItems } from "@/hooks/useObjectItems"; import { useTitle } from "@/hooks/useTitle"; import ErrorScreen from "@/screens/errors/error-screen"; import NoDataFound from "@/screens/errors/no-data-found"; +import { GroupsAutoGeneratedFilterButton } from "@/screens/groups/groups-auto-generated-filter-button"; import LoadingScreen from "@/screens/loading-screen/loading-screen"; +import { ObjectItemsCell, TextCell } from "@/screens/object-items/object-items-cell"; +import { isOfKind } from "@/screens/schema/utils"; import { IModelSchema } from "@/state/atoms/schema.atom"; import { classNames, debounce } from "@/utils/common"; +import { getDisplayValue } from "@/utils/getObjectItemDisplayValue"; import { getSchemaObjectColumns } from "@/utils/getSchemaObjectColumns"; import { Icon } from "@iconify-icon/react"; import { useState } from "react"; import { Navigate } from "react-router-dom"; -import { useObjectItems } from "@/hooks/useObjectItems"; -import { ObjectItemsCell, TextCell } from "@/screens/object-items/object-items-cell"; -import { getDisplayValue } from "@/utils/getObjectItemDisplayValue"; -import { ObjectCreateFormTrigger } from "@/components/form/object-create-form-trigger"; -import ModalDeleteObject from "@/components/modals/modal-delete-object"; +import UnauthorizedScreen from "../errors/unauthorized-screen"; type ObjectItemsProps = { schema: IModelSchema; @@ -39,7 +41,6 @@ export default function ObjectItems({ preventBlock, preventLinks, }: ObjectItemsProps) { - const permission = usePermission(); const [filters, setFilters] = useFilters(); const [rowToDelete, setRowToDelete] = useState(); @@ -51,12 +52,22 @@ export default function ObjectItems({ return ; } + if (!schema.kind) { + return ; + } + // Get all the needed columns (attributes + relationships) const columns = getSchemaObjectColumns({ schema: schema, forListView: true }); - const { loading, error, data = {}, refetch } = useObjectItems(schema, filters); + const { + loading, + error, + data = {}, + refetch, + permission, + } = useObjectItems(schema, filters, kindFilter?.value); - const result = data && schema?.kind ? data[kindFilter?.value || schema?.kind] ?? {} : {}; + const result = data && schema?.kind ? (data[kindFilter?.value || schema?.kind] ?? {}) : {}; const { count = "...", edges } = result; @@ -92,15 +103,23 @@ export default function ObjectItems({ const debouncedHandleSearch = debounce(handleSearch, 500); if (error) { + if (error.networkError?.statusCode === 403) { + const { message } = error.networkError?.result?.errors?.[0] ?? {}; + + return ; + } + return ; } + if (!loading && !permission.view.isAllowed) { + return ; + } + return ( <> -
-
+
+
- + {isOfKind("CoreGroup", schema) && } + +
{loading && !rows && } {/* TODO: use new Table component for list */} - {rows && ( + {!loading && rows && (
@@ -252,28 +277,30 @@ export default function RelationshipDetails(props: iRelationDetailsProps) { )} { setRelatedObjectToEdit(node); }} - data-cy="metadata-edit-button"> + data-cy="metadata-edit-button" + > { setRelatedRowToDelete(node); }} - data-testid="relationship-delete-button"> + data-testid="relationship-delete-button" + >
@@ -139,7 +166,8 @@ export default function ObjectItems({ "border-b border-gray-200", !preventLinks && "hover:bg-gray-50" )} - data-cy="object-table-row"> + data-cy="object-table-row" + > {columns?.map((attribute, index) => { return ( diff --git a/frontend/app/src/screens/objects/hierarchical-tree.tsx b/frontend/app/src/screens/objects/hierarchical-tree.tsx index 7b14bfb732..ee1b3a78a3 100644 --- a/frontend/app/src/screens/objects/hierarchical-tree.tsx +++ b/frontend/app/src/screens/objects/hierarchical-tree.tsx @@ -1,23 +1,25 @@ -import { useAtomValue } from "jotai"; import { Tree, TreeItemProps, TreeProps } from "@/components/ui/tree"; -import { EMPTY_TREE, PrefixNode, updateTreeData } from "@/screens/ipam/ipam-tree/utils"; -import { genericsState, IModelSchema, schemaState } from "@/state/atoms/schema.atom"; -import { useEffect, useState } from "react"; -import { useLazyQuery } from "@/hooks/useQuery"; -import { gql } from "@apollo/client"; -import { TREE_ROOT_ID } from "@/screens/ipam/constants"; -import { Link, useNavigate } from "react-router-dom"; -import { Icon } from "@iconify-icon/react"; -import { constructPath } from "@/utils/fetch"; -import { ITreeViewOnLoadDataProps, NodeId } from "react-accessible-treeview"; -import { getObjectDetailsUrl } from "@/utils/objects"; import { objectAncestorsQuery, objectChildrenQuery, objectTopLevelTreeQuery, } from "@/graphql/queries/objects/objectTreeQuery"; +import useFilters from "@/hooks/useFilters"; +import { useLazyQuery } from "@/hooks/useQuery"; +import { HIDE_AUTO_GENERATED_FILTER } from "@/screens/groups/groups-auto-generated-filter-button"; +import { TREE_ROOT_ID } from "@/screens/ipam/constants"; +import { EMPTY_TREE, PrefixNode, updateTreeData } from "@/screens/ipam/ipam-tree/utils"; import { currentBranchAtom } from "@/state/atoms/branches.atom"; +import { IModelSchema, genericsState, schemaState } from "@/state/atoms/schema.atom"; import { datetimeAtom } from "@/state/atoms/time.atom"; +import { constructPath } from "@/utils/fetch"; +import { getObjectDetailsUrl } from "@/utils/objects"; +import { gql } from "@apollo/client"; +import { Icon } from "@iconify-icon/react"; +import { useAtomValue } from "jotai"; +import { useEffect, useState } from "react"; +import { ITreeViewOnLoadDataProps, NodeId } from "react-accessible-treeview"; +import { Link, useNavigate } from "react-router-dom"; export type HierarchicalTreeProps = { schema: IModelSchema; @@ -29,12 +31,25 @@ export const HierarchicalTree = ({ schema, currentNodeId, className }: Hierarchi const navigate = useNavigate(); const currentBranch = useAtomValue(currentBranchAtom); const currentDate = useAtomValue(datetimeAtom); + const [filters] = useFilters(); + const hasAutoGeneratedFiltered = filters.some( + (filter) => + filter.name === HIDE_AUTO_GENERATED_FILTER.name && + filter.value === HIDE_AUTO_GENERATED_FILTER.value + ); const [treeData, setTreeData] = useState(EMPTY_TREE); const [expandedIds, setExpandedIds] = useState([]); const [selectedIds, setSelectedIds] = useState([]); - const [getObjectTopLevelTree] = useLazyQuery(gql(objectTopLevelTreeQuery({ kind: schema.kind }))); + const [getObjectTopLevelTree] = useLazyQuery( + gql( + objectTopLevelTreeQuery({ + kind: schema.kind, + filters: hasAutoGeneratedFiltered ? `group_type__value: "default"` : undefined, + }) + ) + ); const [getObjectAncestors] = useLazyQuery(gql(objectAncestorsQuery({ kind: schema.kind }))); const [getTreeItemChildren] = useLazyQuery(gql(objectChildrenQuery({ kind: schema.kind }))); const [isLoading, setLoading] = useState(true); @@ -98,7 +113,10 @@ export const HierarchicalTree = ({ schema, currentNodeId, className }: Hierarchi setLoading(true); setSelectedIds([]); setExpandedIds([]); + + let isCancelled = false; fetchTree().then((tree) => { + if (isCancelled) return; if (!tree) return; setLoading(false); @@ -107,7 +125,11 @@ export const HierarchicalTree = ({ schema, currentNodeId, className }: Hierarchi setSelectedIds([currentNodeId]); } }); - }, [schema.kind, currentBranch, currentDate]); + + return () => { + isCancelled = true; + }; + }, [schema.kind, currentBranch, currentDate, hasAutoGeneratedFiltered]); const onLoadData = async ({ element }: ITreeViewOnLoadDataProps) => { if (!element.isBranch || element.children.length > 0) return; // To avoid refetching data @@ -156,8 +178,13 @@ const ObjectTreeItem = ({ element }: TreeItemProps) => { to={url} tabIndex={-1} className="flex items-center gap-2" - data-testid="hierarchical-tree-item"> - {schema?.icon ? :
} + data-testid="hierarchical-tree-item" + > + {schema?.icon ? ( + + ) : ( +
+ )} {element.name} ); diff --git a/frontend/app/src/screens/objects/object-header.tsx b/frontend/app/src/screens/objects/object-header.tsx index 649d3ed22e..b26191cc09 100644 --- a/frontend/app/src/screens/objects/object-header.tsx +++ b/frontend/app/src/screens/objects/object-header.tsx @@ -1,16 +1,12 @@ -import graphqlClient from "@/graphql/graphqlClientApollo"; -import Content from "@/screens/layout/content"; -import { IModelSchema } from "@/state/atoms/schema.atom"; -import { Badge } from "@/components/ui/badge"; -import { constructPath } from "@/utils/fetch"; -import { PROFILE_KIND } from "@/config/constants"; -import { Link } from "react-router-dom"; -import { useObjectItems } from "@/hooks/useObjectItems"; import { ObjectHelpButton } from "@/components/menu/object-help-button"; +import { Skeleton } from "@/components/skeleton"; +import graphqlClient from "@/graphql/graphqlClientApollo"; import useFilters from "@/hooks/useFilters"; -import { Icon } from "@iconify-icon/react"; import { useObjectDetails } from "@/hooks/useObjectDetails"; -import { Skeleton } from "@/components/skeleton"; +import { useObjectItems } from "@/hooks/useObjectItems"; +import Content from "@/screens/layout/content"; +import { getPermission } from "@/screens/permission/utils"; +import { IModelSchema } from "@/state/atoms/schema.atom"; type ObjectHeaderProps = { schema: IModelSchema; @@ -31,33 +27,29 @@ const ObjectItemsHeader = ({ schema }: ObjectHeaderProps) => { const kindFilter = filters?.find((filter) => filter.name == "kind__value"); const schemaKind = kindFilter?.value || (schema.kind as string); - const isProfile = schema.namespace === "Profile" || schemaKind === PROFILE_KIND; - const breadcrumbModelLabel = isProfile ? "All Profiles" : schema.label || schema.name; + const { count, permissions } = data?.[schemaKind] ?? { count: undefined, permissions: undefined }; + const currentPermission = getPermission(permissions?.edges); + + if (!currentPermission.view.isAllowed) { + return null; + } return ( - - -

- {breadcrumbModelLabel} -

- {loading && !error ? "..." : data?.[schemaKind]?.count} - -
- } + graphqlClient.refetchQueries({ include: [schema.kind!] })} - data-testid="object-header"> - - + data-testid="object-header" + end={ + + } + /> ); }; @@ -66,45 +58,29 @@ const ObjectDetailsHeader = ({ schema, objectId }: ObjectHeaderProps & { objectI if (error) return null; - const schemaKind = schema.kind as string; - const isProfile = schema.namespace === "Profile" || schemaKind === PROFILE_KIND; - const breadcrumbModelLabel = isProfile ? "All Profiles" : schema.label || schema.name; - const objectDetailsData = data?.[schema.kind!]?.edges[0]?.node; return ( - - -

{breadcrumbModelLabel}

- - - {loading ? ( - <> - - - - ) : ( - <> - -

{objectDetailsData?.display_label ?? "not found"}

- - )} -
+ loading ? ( + + ) : ( + (objectDetailsData?.display_label ?? `${schema.label} not found`) + ) } description={schema.description} isReloadLoading={loading} reload={() => graphqlClient.refetchQueries({ include: [schema.kind!] })} - data-testid="object-header"> - - + end={ + + } + data-testid="object-header" + /> ); }; diff --git a/frontend/app/src/screens/permission/queries/getObjectPermissions.ts b/frontend/app/src/screens/permission/queries/getObjectPermissions.ts new file mode 100644 index 0000000000..9181ddc8c9 --- /dev/null +++ b/frontend/app/src/screens/permission/queries/getObjectPermissions.ts @@ -0,0 +1,24 @@ +import { jsonToGraphQLQuery } from "json-to-graphql-query"; + +export const getObjectPermissionsQuery = (kind: string) => { + const request = { + query: { + __name: "getObjectPermissions", + [kind]: { + permissions: { + edges: { + node: { + kind: true, + view: true, + create: true, + update: true, + delete: true, + }, + }, + }, + }, + }, + }; + + return jsonToGraphQLQuery(request); +}; diff --git a/frontend/app/src/screens/permission/types.ts b/frontend/app/src/screens/permission/types.ts new file mode 100644 index 0000000000..73d3ffc337 --- /dev/null +++ b/frontend/app/src/screens/permission/types.ts @@ -0,0 +1,9 @@ +export type PermissionDecisionData = "ALLOW" | "ALLOW_DEFAULT" | "ALLOW_OTHER" | "DENY"; + +export type PermissionAction = "view" | "create" | "update" | "delete"; + +export type PermissionData = Record & { kind: string }; + +export type PermissionDecision = { isAllowed: true } | { isAllowed: false; message: string }; + +export type Permission = Record; diff --git a/frontend/app/src/screens/permission/utils.ts b/frontend/app/src/screens/permission/utils.ts new file mode 100644 index 0000000000..a906f681be --- /dev/null +++ b/frontend/app/src/screens/permission/utils.ts @@ -0,0 +1,66 @@ +import { + Permission, + PermissionAction, + PermissionData, + PermissionDecision, + PermissionDecisionData, +} from "@/screens/permission/types"; +import { store } from "@/state"; +import { configState } from "@/state/atoms/config.atom"; +import { warnUnexpectedType } from "@/utils/common"; + +const getMessage = (action: string, decision?: PermissionDecisionData): string => { + if (!decision) + return `Unable to determine permission to ${action} this object. Please contact your administrator.`; + + switch (decision) { + case "DENY": + return `You don't have permission to ${action} this object.`; + case "ALLOW_DEFAULT": + return `This action is only allowed on the default branch. Please switch to the default branch to ${action} this object.`; + case "ALLOW_OTHER": + return `This action is not allowed on the default branch. Please switch to a different branch to ${action} this object.`; + case "ALLOW": + return `You have permission to ${action} this object on any branch.`; + default: + warnUnexpectedType(decision); + return ""; + } +}; + +export function getPermission(permission?: Array<{ node: PermissionData }>): Permission { + if (!Array.isArray(permission)) return PERMISSION_ALLOW_ALL; + + const config = store.get(configState); + + const createPermissionAction = (action: PermissionAction): PermissionDecision => { + if (action === "view" && config?.main.allow_anonymous_access) return { isAllowed: true }; + + const permissionAllowNode = permission.find(({ node }) => node[action] === "ALLOW"); + + if (permissionAllowNode) { + return { isAllowed: true }; + } + + const permissionDeniedNode = permission.find(({ node }) => node[action] !== "ALLOW"); + + return { + isAllowed: false, + message: getMessage(action, permissionDeniedNode?.node?.[action]), + }; + }; + + return { + view: createPermissionAction("view"), + create: createPermissionAction("create"), + update: createPermissionAction("update"), + delete: createPermissionAction("delete"), + }; +} + +export const PERMISSION_ALLOW_ALL: Permission = { + view: { isAllowed: true }, + create: { isAllowed: true }, + update: { isAllowed: true }, + delete: { isAllowed: true }, +}; diff --git a/frontend/app/src/screens/proposed-changes/action-button/pc-approve-button.tsx b/frontend/app/src/screens/proposed-changes/action-button/pc-approve-button.tsx index 5f719c0387..35aeb78368 100644 --- a/frontend/app/src/screens/proposed-changes/action-button/pc-approve-button.tsx +++ b/frontend/app/src/screens/proposed-changes/action-button/pc-approve-button.tsx @@ -1,27 +1,30 @@ import { Button, ButtonProps } from "@/components/buttons/button-primitive"; -import { updateObjectWithId } from "@/graphql/mutations/objects/updateObjectWithId"; +import { ALERT_TYPES, Alert } from "@/components/ui/alert"; import { PROPOSED_CHANGES_OBJECT } from "@/config/constants"; -import { stringifyWithoutQuotes } from "@/utils/string"; -import { gql } from "@apollo/client"; import graphqlClient from "@/graphql/graphqlClientApollo"; -import { toast } from "react-toastify"; -import { Alert, ALERT_TYPES } from "@/components/ui/alert"; -import React, { useState } from "react"; -import { useAtomValue } from "jotai/index"; +import { updateObjectWithId } from "@/graphql/mutations/objects/updateObjectWithId"; +import { useAuth } from "@/hooks/useAuth"; import { currentBranchAtom } from "@/state/atoms/branches.atom"; import { datetimeAtom } from "@/state/atoms/time.atom"; -import { useAuth } from "@/hooks/useAuth"; +import { stringifyWithoutQuotes } from "@/utils/string"; +import { gql } from "@apollo/client"; +import { useAtomValue } from "jotai/index"; +import { useState } from "react"; +import { toast } from "react-toastify"; -interface PcMergeButtonProps extends ButtonProps { +interface PcApproveButtonProps extends ButtonProps { proposedChangeId: string; approvers: Array; + state: "closed" | "open" | "merged"; } export const PcApproveButton = ({ approvers = [], proposedChangeId, + state, + disabled, ...props -}: PcMergeButtonProps) => { +}: PcApproveButtonProps) => { const branch = useAtomValue(currentBranchAtom); const date = useAtomValue(datetimeAtom); const auth = useAuth(); @@ -81,8 +84,9 @@ export const PcApproveButton = ({ variant="outline" onClick={handleApprove} isLoading={isLoadingApprove} - disabled={!auth?.permissions?.write || !approverId || !canApprove} - {...props}> + disabled={disabled || !approverId || !canApprove || state === "closed" || state === "merged"} + {...props} + > Approve ); diff --git a/frontend/app/src/screens/proposed-changes/action-button/pc-close-button.tsx b/frontend/app/src/screens/proposed-changes/action-button/pc-close-button.tsx index 3c983c19af..95e25ff2e4 100644 --- a/frontend/app/src/screens/proposed-changes/action-button/pc-close-button.tsx +++ b/frontend/app/src/screens/proposed-changes/action-button/pc-close-button.tsx @@ -1,27 +1,30 @@ import { Button, ButtonProps } from "@/components/buttons/button-primitive"; -import React, { useState } from "react"; -import { updateObjectWithId } from "@/graphql/mutations/objects/updateObjectWithId"; +import { ALERT_TYPES, Alert } from "@/components/ui/alert"; import { PROPOSED_CHANGES_OBJECT } from "@/config/constants"; -import { stringifyWithoutQuotes } from "@/utils/string"; -import { gql } from "@apollo/client"; import graphqlClient from "@/graphql/graphqlClientApollo"; -import { toast } from "react-toastify"; -import { Alert, ALERT_TYPES } from "@/components/ui/alert"; -import { useAtomValue } from "jotai/index"; +import { updateObjectWithId } from "@/graphql/mutations/objects/updateObjectWithId"; import { currentBranchAtom } from "@/state/atoms/branches.atom"; import { datetimeAtom } from "@/state/atoms/time.atom"; -import { usePermission } from "@/hooks/usePermission"; +import { stringifyWithoutQuotes } from "@/utils/string"; +import { gql } from "@apollo/client"; +import { useAtomValue } from "jotai/index"; +import { useState } from "react"; +import { toast } from "react-toastify"; interface PcCloseButtonProps extends ButtonProps { proposedChangeId: string; state: "closed" | "open" | "merged"; } -export const PcCloseButton = ({ proposedChangeId, state, ...props }: PcCloseButtonProps) => { +export const PcCloseButton = ({ + proposedChangeId, + state, + disabled, + ...props +}: PcCloseButtonProps) => { const [isLoadingClose, setIsLoadingClose] = useState(false); const branch = useAtomValue(currentBranchAtom); const date = useAtomValue(datetimeAtom); - const permission = usePermission(); const handleClose = async () => { setIsLoadingClose(true); @@ -77,8 +80,9 @@ export const PcCloseButton = ({ proposedChangeId, state, ...props }: PcCloseButt variant="danger" onClick={handleClose} isLoading={isLoadingClose} - disabled={!permission.write.allow || state === "merged"} - {...props}> + disabled={!disabled || state === "merged"} + {...props} + > {state === "closed" ? "Re-open" : "Close"} ); diff --git a/frontend/app/src/screens/proposed-changes/action-button/pc-merge-button.tsx b/frontend/app/src/screens/proposed-changes/action-button/pc-merge-button.tsx index 289ebe34e1..e30fbcd264 100644 --- a/frontend/app/src/screens/proposed-changes/action-button/pc-merge-button.tsx +++ b/frontend/app/src/screens/proposed-changes/action-button/pc-merge-button.tsx @@ -1,16 +1,15 @@ import { Button, ButtonProps } from "@/components/buttons/button-primitive"; -import { updateObjectWithId } from "@/graphql/mutations/objects/updateObjectWithId"; +import { ALERT_TYPES, Alert } from "@/components/ui/alert"; import { PROPOSED_CHANGES_OBJECT } from "@/config/constants"; -import { stringifyWithoutQuotes } from "@/utils/string"; -import { gql } from "@apollo/client"; import graphqlClient from "@/graphql/graphqlClientApollo"; -import { toast } from "react-toastify"; -import { Alert, ALERT_TYPES } from "@/components/ui/alert"; -import React, { useState } from "react"; -import { useAtomValue } from "jotai/index"; +import { updateObjectWithId } from "@/graphql/mutations/objects/updateObjectWithId"; import { currentBranchAtom } from "@/state/atoms/branches.atom"; import { datetimeAtom } from "@/state/atoms/time.atom"; -import { usePermission } from "@/hooks/usePermission"; +import { stringifyWithoutQuotes } from "@/utils/string"; +import { gql } from "@apollo/client"; +import { useAtomValue } from "jotai/index"; +import { useState } from "react"; +import { toast } from "react-toastify"; interface PcMergeButtonProps extends ButtonProps { proposedChangeId: string; @@ -22,12 +21,12 @@ export const PcMergeButton = ({ sourceBranch, proposedChangeId, state, + disabled, ...props }: PcMergeButtonProps) => { const [isLoadingMerge, setIsLoadingMerge] = useState(false); const branch = useAtomValue(currentBranchAtom); const date = useAtomValue(datetimeAtom); - const permission = usePermission(); const handleMerge = async () => { if (!sourceBranch) return; @@ -77,8 +76,9 @@ export const PcMergeButton = ({ variant="active" onClick={handleMerge} isLoading={isLoadingMerge} - disabled={!permission.write.allow || state === "closed" || state === "merged"} - {...props}> + disabled={disabled || state === "closed" || state === "merged"} + {...props} + > Merge ); diff --git a/frontend/app/src/screens/proposed-changes/conversations.tsx b/frontend/app/src/screens/proposed-changes/conversations.tsx index a709e3125b..9d5b1ab308 100644 --- a/frontend/app/src/screens/proposed-changes/conversations.tsx +++ b/frontend/app/src/screens/proposed-changes/conversations.tsx @@ -1,7 +1,9 @@ import { AddComment } from "@/components/conversations/add-comment"; import { Thread } from "@/components/conversations/thread"; +import { Card } from "@/components/ui/card"; +import { FormRef } from "@/components/ui/form"; import { - ACCOUNT_OBJECT, + ACCOUNT_GENERIC_OBJECT, PROPOSED_CHANGES_CHANGE_THREAD_OBJECT, PROPOSED_CHANGES_THREAD_COMMENT_OBJECT, PROPOSED_CHANGES_THREAD_OBJECT, @@ -16,15 +18,13 @@ import ErrorScreen from "@/screens/errors/error-screen"; import LoadingScreen from "@/screens/loading-screen/loading-screen"; import { currentBranchAtom } from "@/state/atoms/branches.atom"; import { datetimeAtom } from "@/state/atoms/time.atom"; +import { classNames } from "@/utils/common"; import { stringifyWithoutQuotes } from "@/utils/string"; -import { gql, NetworkStatus } from "@apollo/client"; +import { NetworkStatus, gql } from "@apollo/client"; import { formatISO } from "date-fns"; import { useAtomValue } from "jotai/index"; import { HTMLAttributes, useRef } from "react"; import { useParams } from "react-router-dom"; -import { FormRef } from "@/components/ui/form"; -import { Card } from "@/components/ui/card"; -import { classNames } from "@/utils/common"; export const Conversations = ({ className, ...props }: HTMLAttributes) => { const { proposedChangeId } = useParams(); @@ -36,7 +36,7 @@ export const Conversations = ({ className, ...props }: HTMLAttributes ))} - + diff --git a/frontend/app/src/screens/proposed-changes/create-form.tsx b/frontend/app/src/screens/proposed-changes/create-form.tsx index 93020858f0..e1981082f5 100644 --- a/frontend/app/src/screens/proposed-changes/create-form.tsx +++ b/frontend/app/src/screens/proposed-changes/create-form.tsx @@ -3,7 +3,7 @@ import { MarkdownEditor } from "@/components/editor"; import { Select } from "@/components/inputs/select"; import { ALERT_TYPES, Alert } from "@/components/ui/alert"; import { Card } from "@/components/ui/card"; -import { Combobox } from "@/components/ui/combobox"; +import { Combobox } from "@/components/ui/combobox-legacy"; import { Form, FormField, @@ -13,6 +13,8 @@ import { FormSubmit, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; +import { Spinner } from "@/components/ui/spinner"; +import { ACCOUNT_GENERIC_OBJECT } from "@/config/constants"; import { CREATE_PROPOSED_CHANGE } from "@/graphql/mutations/proposed-changes/createProposedChange"; import { GET_ALL_ACCOUNTS } from "@/graphql/queries/accounts/getAllAccounts"; import { useAuth } from "@/hooks/useAuth"; @@ -24,7 +26,6 @@ import { Icon } from "@iconify-icon/react"; import { useAtomValue } from "jotai"; import { useNavigate } from "react-router-dom"; import { toast } from "react-toastify"; -import { ACCOUNT_OBJECT } from "@/config/constants"; export const ProposedChangeCreateForm = () => { const { user } = useAuth(); @@ -37,6 +38,8 @@ export const ProposedChangeCreateForm = () => { const [createProposedChange, { error }] = useMutation(CREATE_PROPOSED_CHANGE); + if (branches.length === 0) return ; + return (
{ @@ -59,7 +62,8 @@ export const ProposedChangeCreateForm = () => { const url = constructPath(`/proposed-changes/${data.CoreProposedChangeCreate.object.id}`); navigate(url); - }}> + }} + > {
@@ -156,14 +184,15 @@ export default function ObjectItems({ { setRowToDelete(row); setDeleteModal(true); - }}> + }} + >
setRelatedRowToDelete(row)} - className="border-0 border-t" - /> - +
setRelatedRowToDelete(row)} + permission={permission} + className="border-0 border-t" + /> - {relatedRowToDelete && ( - - Are you sure you want to delete the Proposed Change:{" "} - {relatedRowToDelete?.values?.display_label} - - } - onCancel={() => setRelatedRowToDelete(undefined)} - onDelete={submitDeleteProposedChange} - open={!!relatedRowToDelete} - setOpen={() => setRelatedRowToDelete(undefined)} - isLoading={isDeleteLoading} - /> - )} - - + {relatedRowToDelete && ( + + Are you sure you want to delete the Proposed Change:{" "} + {relatedRowToDelete?.values?.display_label} + + } + onCancel={() => setRelatedRowToDelete(undefined)} + onDelete={submitDeleteProposedChange} + open={!!relatedRowToDelete} + setOpen={() => setRelatedRowToDelete(undefined)} + isLoading={isDeleteLoading} + /> + )} + ); }; diff --git a/frontend/app/src/screens/proposed-changes/proposed-change-details.tsx b/frontend/app/src/screens/proposed-changes/proposed-change-details.tsx index f7a1b1b7f5..be90cde176 100644 --- a/frontend/app/src/screens/proposed-changes/proposed-change-details.tsx +++ b/frontend/app/src/screens/proposed-changes/proposed-change-details.tsx @@ -1,23 +1,28 @@ import { Avatar } from "@/components/display/avatar"; import { DateDisplay } from "@/components/display/date-display"; +import { MarkdownViewer } from "@/components/editor/markdown-viewer"; +import { Property, PropertyList } from "@/components/table/property-list"; +import { Badge } from "@/components/ui/badge"; +import { CardWithBorder } from "@/components/ui/card"; import { Tooltip } from "@/components/ui/tooltip"; +import { PROPOSED_CHANGES_OBJECT } from "@/config/constants"; +import useQuery from "@/hooks/useQuery"; +import { PcApproveButton } from "@/screens/proposed-changes/action-button/pc-approve-button"; +import { PcCloseButton } from "@/screens/proposed-changes/action-button/pc-close-button"; +import { PcMergeButton } from "@/screens/proposed-changes/action-button/pc-merge-button"; +import { Conversations } from "@/screens/proposed-changes/conversations"; +import { ProposedChangeEditTrigger } from "@/screens/proposed-changes/proposed-change-edit-trigger"; import { proposedChangedState } from "@/state/atoms/proposedChanges.atom"; +import { classNames } from "@/utils/common"; import { constructPath } from "@/utils/fetch"; import { getProposedChangesStateBadgeType } from "@/utils/proposed-changes"; +import { gql } from "@apollo/client"; import { Icon } from "@iconify-icon/react"; import { useAtom } from "jotai"; -import React, { HTMLAttributes } from "react"; +import { HTMLAttributes } from "react"; import { useNavigate, useParams } from "react-router-dom"; -import { CardWithBorder } from "@/components/ui/card"; -import { Property, PropertyList } from "@/components/table/property-list"; -import { Badge } from "@/components/ui/badge"; -import { ProposedChangeEditTrigger } from "@/screens/proposed-changes/proposed-change-edit-trigger"; -import { PcCloseButton } from "@/screens/proposed-changes/action-button/pc-close-button"; -import { PcMergeButton } from "@/screens/proposed-changes/action-button/pc-merge-button"; -import { PcApproveButton } from "@/screens/proposed-changes/action-button/pc-approve-button"; -import { Conversations } from "@/screens/proposed-changes/conversations"; -import { classNames } from "@/utils/common"; -import { MarkdownViewer } from "@/components/editor/markdown-viewer"; +import { getObjectPermissionsQuery } from "../permission/queries/getObjectPermissions"; +import { getPermission } from "../permission/utils"; export const ProposedChangeDetails = ({ className, ...props }: HTMLAttributes) => { const { proposedChangeId } = useParams(); @@ -25,6 +30,12 @@ export const ProposedChangeDetails = ({ className, ...props }: HTMLAttributes edge.node) ?? []; const approvers = proposedChangesDetails?.approved_by?.edges.map((edge: any) => edge.node) ?? []; @@ -94,20 +105,36 @@ export const ProposedChangeDetails = ({ className, ...props }: HTMLAttributes - + + - ), }, ]; return ( -
+
{proposedChangesDetails?.description?.value && ( @@ -133,7 +160,8 @@ export const ProposedChangeDetails = ({ className, ...props }: HTMLAttributes
navigate(path)} - className="text-base font-semibold leading-6 text-gray-900 cursor-pointer hover:underline"> + className="text-base font-semibold leading-6 text-gray-900 cursor-pointer hover:underline" + > Proposed change
diff --git a/frontend/app/src/screens/proposed-changes/proposed-change-edit-trigger.tsx b/frontend/app/src/screens/proposed-changes/proposed-change-edit-trigger.tsx index 8b0164cd04..205ec686a4 100644 --- a/frontend/app/src/screens/proposed-changes/proposed-change-edit-trigger.tsx +++ b/frontend/app/src/screens/proposed-changes/proposed-change-edit-trigger.tsx @@ -1,36 +1,44 @@ -import { usePermission } from "@/hooks/usePermission"; -import { useSchema } from "@/hooks/useSchema"; -import { PROPOSED_CHANGES_EDITABLE_STATE, PROPOSED_CHANGES_OBJECT } from "@/config/constants"; -import React, { useState } from "react"; import { ButtonWithTooltip } from "@/components/buttons/button-primitive"; -import { Icon } from "@iconify-icon/react"; import SlideOver from "@/components/display/slide-over"; import { ObjectHelpButton } from "@/components/menu/object-help-button"; -import { ProposedChangeEditForm } from "@/screens/proposed-changes/form/proposed-change-edit-form"; +import { PROPOSED_CHANGES_EDITABLE_STATE, PROPOSED_CHANGES_OBJECT } from "@/config/constants"; import graphqlClient from "@/graphql/graphqlClientApollo"; +import useQuery from "@/hooks/useQuery"; +import { useSchema } from "@/hooks/useSchema"; +import { ProposedChangeEditForm } from "@/screens/proposed-changes/form/proposed-change-edit-form"; +import { gql } from "@apollo/client"; +import { Icon } from "@iconify-icon/react"; +import { useState } from "react"; +import { getObjectPermissionsQuery } from "../permission/queries/getObjectPermissions"; +import { getPermission } from "../permission/utils"; export const ProposedChangeEditTrigger = ({ proposedChangesDetails, }: { proposedChangesDetails: any; }) => { - const permission = usePermission(); const { schema: proposedChangeSchema } = useSchema(PROPOSED_CHANGES_OBJECT); const [showEditDrawer, setShowEditDrawer] = useState(false); + const { loading, data } = useQuery(gql(getObjectPermissionsQuery(PROPOSED_CHANGES_OBJECT))); + + const permission = getPermission(data?.[PROPOSED_CHANGES_OBJECT]?.permissions?.edges); + return ( <> setShowEditDrawer(true)} - data-testid="edit-button"> + data-testid="edit-button" + > @@ -57,7 +65,8 @@ export const ProposedChangeEditTrigger = ({
} open={showEditDrawer} - setOpen={setShowEditDrawer}> + setOpen={setShowEditDrawer} + > { diff --git a/frontend/app/src/screens/repository/repository-action-menu.tsx b/frontend/app/src/screens/repository/repository-action-menu.tsx index 0ca963f199..85ed47e024 100644 --- a/frontend/app/src/screens/repository/repository-action-menu.tsx +++ b/frontend/app/src/screens/repository/repository-action-menu.tsx @@ -1,21 +1,21 @@ import { Icon } from "@iconify-icon/react"; import { Button, ButtonWithTooltip } from "@/components/buttons/button-primitive"; +import { ALERT_TYPES, Alert } from "@/components/ui/alert"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; -import { useMutation } from "@/hooks/useQuery"; import { CHECK_REPOSITORY_CONNECTIVITY, REIMPORT_LAST_COMMIT, } from "@/graphql/mutations/repository/actions"; -import { toast } from "react-toastify"; -import { Alert, ALERT_TYPES } from "@/components/ui/alert"; -import { useState } from "react"; +import { useMutation } from "@/hooks/useQuery"; import { Dialog } from "@headlessui/react"; +import { useState } from "react"; +import { toast } from "react-toastify"; const RepositoryActionMenu = ({ repositoryId }: { repositoryId: string }) => { const [isOpen, setIsOpen] = useState(false); @@ -29,14 +29,15 @@ const RepositoryActionMenu = ({ repositoryId }: { repositoryId: string }) => { tooltipEnabled variant="ghost" size="square" - className="p-4"> + className="p-4" + > setIsOpen(true)}> - + Check connectivity @@ -152,7 +153,7 @@ const ReimportLastCommitAction = ({ repositoryId }: { repositoryId: string }) => return ( reimportLastCommit()}> - + Reimport last commit ); diff --git a/frontend/app/src/screens/repository/repository-form.tsx b/frontend/app/src/screens/repository/repository-form.tsx index 4406d7c6ec..b496048c9b 100644 --- a/frontend/app/src/screens/repository/repository-form.tsx +++ b/frontend/app/src/screens/repository/repository-form.tsx @@ -1,21 +1,20 @@ -import { Form, FormSubmit } from "@/components/ui/form"; +import { Button } from "@/components/buttons/button-primitive"; +import { DynamicInput } from "@/components/form/dynamic-form"; +import RelationshipField from "@/components/form/fields/relationship.field"; +import { NodeFormProps } from "@/components/form/node-form"; +import { getFormFieldsFromSchema } from "@/components/form/utils/getFormFieldsFromSchema"; +import { getCreateMutationFromFormData } from "@/components/form/utils/mutations/getCreateMutationFromFormData"; import { Card, CardProps } from "@/components/ui/card"; +import { Form, FormSubmit } from "@/components/ui/form"; +import graphqlClient from "@/graphql/graphqlClientApollo"; +import { createObject } from "@/graphql/mutations/objects/createObject"; +import { useAuth } from "@/hooks/useAuth"; +import { currentBranchAtom } from "@/state/atoms/branches.atom"; +import { datetimeAtom } from "@/state/atoms/time.atom"; import { classNames } from "@/utils/common"; import { stringifyWithoutQuotes } from "@/utils/string"; import { gql } from "@apollo/client"; -import graphqlClient from "@/graphql/graphqlClientApollo"; import { useAtomValue } from "jotai/index"; -import { currentBranchAtom } from "@/state/atoms/branches.atom"; -import { datetimeAtom } from "@/state/atoms/time.atom"; -import { getCreateMutationFromFormData } from "@/components/form/utils/mutations/getCreateMutationFromFormData"; -import { getFormFieldsFromSchema } from "@/components/form/utils/getFormFieldsFromSchema"; -import { useAuth } from "@/hooks/useAuth"; -import { DynamicInput } from "@/components/form/dynamic-form"; -import { NodeFormProps } from "@/components/form/node-form"; -import { createObject } from "@/graphql/mutations/objects/createObject"; -import RelationshipField from "@/components/form/fields/relationship.field"; -import { Button } from "@/components/buttons/button-primitive"; -import React from "react"; const RepositoryForm = ({ onSuccess, @@ -65,7 +64,8 @@ const RepositoryForm = ({ await graphqlClient.reFetchObservableQueries(); if (onSuccess) await onSuccess(result?.data?.[`${schema?.kind}Create`]); - }}> + }} + > {gitUrlFieldProps && ( } - enabled> + enabled + > {roundNumber(utilizationOverall, 0)}% diff --git a/frontend/app/src/screens/resource-manager/constants.ts b/frontend/app/src/screens/resource-manager/constants.ts index 7151afdc14..6edb4bc960 100644 --- a/frontend/app/src/screens/resource-manager/constants.ts +++ b/frontend/app/src/screens/resource-manager/constants.ts @@ -2,3 +2,6 @@ export const RESOURCE_GENERIC_KIND = "CoreResourcePool"; export const RESOURCE_POOL_UTILIZATION_KIND = "InfrahubResourcePoolUtilization"; export const RESOURCE_POOL_ALLOCATED_KIND = "InfrahubResourcePoolAllocated"; export const NUMBER_POOL_KIND = "CoreNumberPool"; + +export const NUMBER_POOL_NODE_FIELD = "node"; +export const NUMBER_POOL_NODE_ATTRIBUTE_FIELD = "node_attribute"; diff --git a/frontend/app/src/screens/resource-manager/number-pool-form.tsx b/frontend/app/src/screens/resource-manager/number-pool-form.tsx index 53011a0109..8bbd2fa0f9 100644 --- a/frontend/app/src/screens/resource-manager/number-pool-form.tsx +++ b/frontend/app/src/screens/resource-manager/number-pool-form.tsx @@ -1,27 +1,41 @@ import { Button } from "@/components/buttons/button-primitive"; -import DropdownField from "@/components/form/fields/dropdown.field"; +import { DEFAULT_FORM_FIELD_VALUE } from "@/components/form/constants"; +import { LabelFormField } from "@/components/form/fields/common"; import InputField from "@/components/form/fields/input.field"; import NumberField from "@/components/form/fields/number.field"; import { NodeFormProps } from "@/components/form/node-form"; -import { FormFieldValue } from "@/components/form/type"; +import { FormAttributeValue, FormFieldValue } from "@/components/form/type"; import { getCurrentFieldValue } from "@/components/form/utils/getFieldDefaultValue"; import { getCreateMutationFromFormDataOnly } from "@/components/form/utils/mutations/getCreateMutationFromFormData"; -import { SelectOption } from "@/components/inputs/select"; -import { Alert, ALERT_TYPES } from "@/components/ui/alert"; -import { Form, FormSubmit } from "@/components/ui/form"; -import { NUMBER_POOL_OBJECT } from "@/config/constants"; +import { updateFormFieldValue } from "@/components/form/utils/updateFormFieldValue"; +import { isRequired } from "@/components/form/utils/validation"; +import { ALERT_TYPES, Alert } from "@/components/ui/alert"; +import { Badge } from "@/components/ui/badge"; +import { + Combobox, + ComboboxContent, + ComboboxItem, + ComboboxList, + ComboboxTrigger, +} from "@/components/ui/combobox"; +import { Form, FormField, FormInput, FormMessage, FormSubmit } from "@/components/ui/form"; +import { NUMBER_POOL_OBJECT, SCHEMA_ATTRIBUTE_KIND } from "@/config/constants"; import graphqlClient from "@/graphql/graphqlClientApollo"; import { createObject } from "@/graphql/mutations/objects/createObject"; import { updateObjectWithId } from "@/graphql/mutations/objects/updateObjectWithId"; -import { useFormValues } from "@/hooks/useFormValues"; +import { + NUMBER_POOL_NODE_ATTRIBUTE_FIELD, + NUMBER_POOL_NODE_FIELD, +} from "@/screens/resource-manager/constants"; +import { AttributeSchema } from "@/screens/schema/types"; import { currentBranchAtom } from "@/state/atoms/branches.atom"; -import { schemaState } from "@/state/atoms/schema.atom"; +import { iNodeSchema, schemaState } from "@/state/atoms/schema.atom"; import { datetimeAtom } from "@/state/atoms/time.atom"; import { AttributeType, RelationshipType } from "@/utils/getObjectItemDisplayValue"; import { stringifyWithoutQuotes } from "@/utils/string"; import { gql } from "@apollo/client"; import { useAtomValue } from "jotai"; -import { useEffect } from "react"; +import { useEffect, useState } from "react"; import { FieldValues, useForm, useFormContext } from "react-hook-form"; import { toast } from "react-toastify"; @@ -132,50 +146,145 @@ export const NumberPoolForm = ({ }; const NodeAttributesSelects = () => { - const schemaList = useAtomValue(schemaState); + const nodes = useAtomValue(schemaState); - const { resetField } = useFormContext(); + const form = useFormContext(); + const selectedNodeField: FormAttributeValue = form.watch(NUMBER_POOL_NODE_FIELD); + const selectedNode = nodes.find((node) => node.kind === selectedNodeField?.value); - // Watch form value to rebuild 2nd select options - const { node } = useFormValues(); - - const availableSchemaList = schemaList?.filter( - (schema) => - !!schema.attributes?.find((attribute) => attribute.kind === "Number" && !attribute.read_only) + const nodesWithNumberAttributes: Array = nodes.filter((node) => + node.attributes?.some( + (attribute) => attribute.kind === SCHEMA_ATTRIBUTE_KIND.NUMBER && !attribute.read_only + ) ); - const selectedNode = availableSchemaList.find((schema) => schema.kind === node?.value); - const nodesOptions: SelectOption[] = availableSchemaList.map((schema) => ({ - id: schema.kind as string, - name: schema.label as string, - badge: schema.namespace, - })); - - const attributesOptions: SelectOption[] = selectedNode?.attributes - ? selectedNode.attributes - ?.filter((attribute) => attribute.kind === "Number") - ?.map((attribute) => ({ id: attribute.name as string, name: attribute.label as string })) - : []; + const numberAttributeOptions: Array = + selectedNode?.attributes?.filter( + (attribute) => attribute.kind === SCHEMA_ATTRIBUTE_KIND.NUMBER + ) ?? []; useEffect(() => { - resetField("node_attribute"); - }, [node?.value]); + if (numberAttributeOptions.length === 1) { + form.setValue( + NUMBER_POOL_NODE_ATTRIBUTE_FIELD, + updateFormFieldValue(numberAttributeOptions[0].name, DEFAULT_FORM_FIELD_VALUE) + ); + } else { + form.resetField(NUMBER_POOL_NODE_ATTRIBUTE_FIELD); + } + }, [selectedNode?.kind]); return ( <> - { + const [open, setOpen] = useState(false); + + return ( +
+ + + + + + {selectedNode && ( +
+ {selectedNode.label} {selectedNode.namespace} +
+ )} +
+
+ + + + {nodesWithNumberAttributes.map((node) => ( + { + const newValue = node.kind === selectedNode?.kind ? null : node.kind; + field.onChange( + updateFormFieldValue(newValue ?? null, DEFAULT_FORM_FIELD_VALUE) + ); + + setOpen(false); + }} + > +
+ {node.label} {node.namespace} +
+
+ ))} +
+
+
+ + +
+ ); + }} /> - { + const [open, setOpen] = useState(false); + const selectedAttribute: FormFieldValue = field.value; + + return ( +
+ + + + + + { + numberAttributeOptions.find( + (attribute) => attribute.name === selectedAttribute?.value + )?.label + } + + + + + + {numberAttributeOptions.map((attribute) => ( + { + const newValue = + attribute.name === selectedNode?.name ? null : attribute.name; + field.onChange(updateFormFieldValue(newValue, DEFAULT_FORM_FIELD_VALUE)); + setOpen(false); + }} + > + {attribute.label} + + ))} + + + + + +
+ ); + }} /> ); diff --git a/frontend/app/src/screens/resource-manager/resource-selector.tsx b/frontend/app/src/screens/resource-manager/resource-selector.tsx index 67351aa5c7..c52af4b6fc 100644 --- a/frontend/app/src/screens/resource-manager/resource-selector.tsx +++ b/frontend/app/src/screens/resource-manager/resource-selector.tsx @@ -1,6 +1,6 @@ import { PropertyList } from "@/components/table/property-list"; import { Badge } from "@/components/ui/badge"; -import { CardWithBorder } from "@/components/ui/card"; +import { Card, CardWithBorder } from "@/components/ui/card"; import { constructPath } from "@/utils/fetch"; import { getObjectDetailsUrl2 } from "@/utils/objects"; import { Icon } from "@iconify-icon/react"; @@ -22,7 +22,7 @@ interface ResourcePoolSelectorProps extends HTMLAttributes { const ResourceSelector = ({ resources, className, ...props }: ResourcePoolSelectorProps) => { return ( - + Resources {resources.length} @@ -35,7 +35,8 @@ const ResourceSelector = ({ resources, className, ...props }: ResourcePoolSelect name: ( + className="font-semibold underline" + > {resource.display_label} ), @@ -49,14 +50,15 @@ const ResourceSelector = ({ resources, className, ...props }: ResourcePoolSelect + className="flex items-center gap-1 text-nowrap hover:underline" + > View
), }))} /> - + ); }; diff --git a/frontend/app/src/screens/role-management/account-form.tsx b/frontend/app/src/screens/role-management/account-form.tsx new file mode 100644 index 0000000000..4fde745ce0 --- /dev/null +++ b/frontend/app/src/screens/role-management/account-form.tsx @@ -0,0 +1,158 @@ +import { Button } from "@/components/buttons/button-primitive"; +import InputField from "@/components/form/fields/input.field"; +import RelationshipField from "@/components/form/fields/relationship.field"; +import { NodeFormProps } from "@/components/form/node-form"; +import { FormFieldValue } from "@/components/form/type"; +import { getCurrentFieldValue } from "@/components/form/utils/getFieldDefaultValue"; +import { getRelationshipDefaultValue } from "@/components/form/utils/getRelationshipDefaultValue"; +import { getCreateMutationFromFormDataOnly } from "@/components/form/utils/mutations/getCreateMutationFromFormData"; +import { isRequired } from "@/components/form/utils/validation"; +import { ALERT_TYPES, Alert } from "@/components/ui/alert"; +import { Form, FormSubmit } from "@/components/ui/form"; +import { ACCOUNT_GROUP_OBJECT, ACCOUNT_OBJECT, OBJECT_PERMISSION_OBJECT } from "@/config/constants"; +import graphqlClient from "@/graphql/graphqlClientApollo"; +import { createObject } from "@/graphql/mutations/objects/createObject"; +import { updateObjectWithId } from "@/graphql/mutations/objects/updateObjectWithId"; +import { useSchema } from "@/hooks/useSchema"; +import { currentBranchAtom } from "@/state/atoms/branches.atom"; +import { datetimeAtom } from "@/state/atoms/time.atom"; +import { AttributeType, RelationshipType } from "@/utils/getObjectItemDisplayValue"; +import { stringifyWithoutQuotes } from "@/utils/string"; +import { gql } from "@apollo/client"; +import { useAtomValue } from "jotai"; +import { FieldValues, useForm } from "react-hook-form"; +import { toast } from "react-toastify"; + +interface NumberPoolFormProps extends Pick { + currentObject?: Record; + onCancel?: () => void; + onUpdateComplete?: () => void; +} + +export const AccountForm = ({ + currentObject, + onSuccess, + onCancel, + onUpdateComplete, +}: NumberPoolFormProps) => { + const branch = useAtomValue(currentBranchAtom); + const date = useAtomValue(datetimeAtom); + const { schema } = useSchema(ACCOUNT_OBJECT); + + const groups = getRelationshipDefaultValue({ + relationshipData: currentObject?.member_of_groups?.value, + }); + + const defaultValues = { + name: getCurrentFieldValue("name", currentObject), + password: getCurrentFieldValue("password", currentObject), + description: getCurrentFieldValue("description", currentObject), + label: getCurrentFieldValue("label", currentObject), + member_of_groups: groups, + }; + + const form = useForm({ + defaultValues, + }); + + async function handleSubmit(data: Record) { + try { + const newObject = getCreateMutationFromFormDataOnly(data, currentObject); + + if (!Object.keys(newObject).length) { + return; + } + + const mutationString = currentObject + ? updateObjectWithId({ + kind: ACCOUNT_OBJECT, + data: stringifyWithoutQuotes({ + id: currentObject.id, + ...newObject, + }), + }) + : createObject({ + kind: ACCOUNT_OBJECT, + data: stringifyWithoutQuotes({ + ...newObject, + }), + }); + + const mutation = gql` + ${mutationString} + `; + + const result = await graphqlClient.mutate({ + mutation, + context: { + branch: branch?.name, + date, + }, + }); + + toast(, { + toastId: "alert-success-account-created", + }); + + if (onSuccess) await onSuccess(result?.data?.[`${OBJECT_PERMISSION_OBJECT}Create`]); + if (onUpdateComplete) await onUpdateComplete(); + } catch (error: unknown) { + console.error("An error occurred while creating the object: ", error); + } + } + + return ( +
+ + + + {!currentObject && ( + + )} + + + + + +
+ {onCancel && ( + + )} + + {currentObject ? "Update" : "Create"} +
+ +
+ ); +}; diff --git a/frontend/app/src/screens/role-management/account-group-form.tsx b/frontend/app/src/screens/role-management/account-group-form.tsx new file mode 100644 index 0000000000..a178756c4a --- /dev/null +++ b/frontend/app/src/screens/role-management/account-group-form.tsx @@ -0,0 +1,175 @@ +import { Button } from "@/components/buttons/button-primitive"; +import DropdownField from "@/components/form/fields/dropdown.field"; +import InputField from "@/components/form/fields/input.field"; +import RelationshipField from "@/components/form/fields/relationship.field"; +import { NodeFormProps } from "@/components/form/node-form"; +import { FormFieldValue } from "@/components/form/type"; +import { getCurrentFieldValue } from "@/components/form/utils/getFieldDefaultValue"; +import { getRelationshipDefaultValue } from "@/components/form/utils/getRelationshipDefaultValue"; +import { getCreateMutationFromFormDataOnly } from "@/components/form/utils/mutations/getCreateMutationFromFormData"; +import { isRequired } from "@/components/form/utils/validation"; +import { DropdownOption } from "@/components/inputs/dropdown"; +import { ALERT_TYPES, Alert } from "@/components/ui/alert"; +import { Form, FormSubmit } from "@/components/ui/form"; +import { + ACCOUNT_GROUP_OBJECT, + ACCOUNT_OBJECT, + ACCOUNT_ROLE_OBJECT, + OBJECT_PERMISSION_OBJECT, +} from "@/config/constants"; +import graphqlClient from "@/graphql/graphqlClientApollo"; +import { createObject } from "@/graphql/mutations/objects/createObject"; +import { updateObjectWithId } from "@/graphql/mutations/objects/updateObjectWithId"; +import { useSchema } from "@/hooks/useSchema"; +import { currentBranchAtom } from "@/state/atoms/branches.atom"; +import { datetimeAtom } from "@/state/atoms/time.atom"; +import { AttributeType, RelationshipType } from "@/utils/getObjectItemDisplayValue"; +import { stringifyWithoutQuotes } from "@/utils/string"; +import { gql } from "@apollo/client"; +import { useAtomValue } from "jotai"; +import { FieldValues, useForm } from "react-hook-form"; +import { toast } from "react-toastify"; + +interface NumberPoolFormProps extends Pick { + currentObject?: Record; + onCancel?: () => void; + onUpdateComplete?: () => void; +} + +export const AccountGroupForm = ({ + currentObject, + onSuccess, + onCancel, + onUpdateComplete, +}: NumberPoolFormProps) => { + const branch = useAtomValue(currentBranchAtom); + const date = useAtomValue(datetimeAtom); + const { schema } = useSchema(ACCOUNT_GROUP_OBJECT); + + const roles = getRelationshipDefaultValue({ + relationshipData: currentObject?.roles?.value, + }); + + const members = getRelationshipDefaultValue({ + relationshipData: currentObject?.members?.value, + }); + + const defaultValues = { + name: getCurrentFieldValue("name", currentObject), + description: getCurrentFieldValue("description", currentObject), + label: getCurrentFieldValue("label", currentObject), + group_type: getCurrentFieldValue("group_type", currentObject), + roles, + members, + }; + + const form = useForm({ + defaultValues, + }); + + async function handleSubmit(data: Record) { + try { + const newObject = getCreateMutationFromFormDataOnly(data, currentObject); + + if (!Object.keys(newObject).length) { + return; + } + + const mutationString = currentObject + ? updateObjectWithId({ + kind: ACCOUNT_GROUP_OBJECT, + data: stringifyWithoutQuotes({ + id: currentObject.id, + ...newObject, + }), + }) + : createObject({ + kind: ACCOUNT_GROUP_OBJECT, + data: stringifyWithoutQuotes({ + ...newObject, + }), + }); + + const mutation = gql` + ${mutationString} + `; + + const result = await graphqlClient.mutate({ + mutation, + context: { + branch: branch?.name, + date, + }, + }); + + toast(, { + toastId: "alert-success-group-created", + }); + + if (onSuccess) await onSuccess(result?.data?.[`${OBJECT_PERMISSION_OBJECT}Create`]); + if (onUpdateComplete) await onUpdateComplete(); + } catch (error: unknown) { + console.error("An error occurred while creating the object: ", error); + } + } + + const typeOptions: DropdownOption[] = + schema?.attributes + ?.find((attribute) => attribute.name === "group_type") + ?.enum?.map((data) => ({ value: data as string, label: data as string })) ?? []; + + return ( +
+
+ + + + + + + + + + + + +
+ {onCancel && ( + + )} + + {currentObject ? "Update" : "Create"} +
+ +
+ ); +}; diff --git a/frontend/app/src/screens/role-management/account-role-form.tsx b/frontend/app/src/screens/role-management/account-role-form.tsx new file mode 100644 index 0000000000..76e19e99a5 --- /dev/null +++ b/frontend/app/src/screens/role-management/account-role-form.tsx @@ -0,0 +1,158 @@ +import { Button } from "@/components/buttons/button-primitive"; +import { NodeFormProps } from "@/components/form/node-form"; +import { FormFieldValue } from "@/components/form/type"; +import { getCurrentFieldValue } from "@/components/form/utils/getFieldDefaultValue"; +import { getCreateMutationFromFormDataOnly } from "@/components/form/utils/mutations/getCreateMutationFromFormData"; +import { ALERT_TYPES, Alert } from "@/components/ui/alert"; +import { Form, FormSubmit } from "@/components/ui/form"; +import { + ACCOUNT_GROUP_OBJECT, + ACCOUNT_PERMISSION_OBJECT, + ACCOUNT_ROLE_OBJECT, +} from "@/config/constants"; +import graphqlClient from "@/graphql/graphqlClientApollo"; +import { createObject } from "@/graphql/mutations/objects/createObject"; +import { updateObjectWithId } from "@/graphql/mutations/objects/updateObjectWithId"; +import { currentBranchAtom } from "@/state/atoms/branches.atom"; +import { datetimeAtom } from "@/state/atoms/time.atom"; +import { AttributeType, RelationshipType } from "@/utils/getObjectItemDisplayValue"; +import { stringifyWithoutQuotes } from "@/utils/string"; +import { gql } from "@apollo/client"; +import { useAtomValue } from "jotai"; +import { FieldValues, useForm } from "react-hook-form"; +import { toast } from "react-toastify"; + +import InputField from "@/components/form/fields/input.field"; +import RelationshipField from "@/components/form/fields/relationship.field"; +import { getRelationshipDefaultValue } from "@/components/form/utils/getRelationshipDefaultValue"; +import { isRequired } from "@/components/form/utils/validation"; + +interface NumberPoolFormProps extends Pick { + currentObject?: Record; + onCancel?: () => void; + onUpdateComplete?: () => void; +} + +export const AccountRoleForm = ({ + currentObject, + onSuccess, + onCancel, + onUpdateComplete, +}: NumberPoolFormProps) => { + const branch = useAtomValue(currentBranchAtom); + const date = useAtomValue(datetimeAtom); + + const groups = getRelationshipDefaultValue({ + relationshipData: currentObject?.groups?.value, + }); + + const permissions = getRelationshipDefaultValue({ + relationshipData: currentObject?.permissions?.value, + }); + + const defaultValues = { + name: getCurrentFieldValue("name", currentObject), + groups, + permissions, + }; + + const form = useForm({ + defaultValues, + }); + + async function handleSubmit(data: Record) { + try { + const newObject = getCreateMutationFromFormDataOnly(data, currentObject); + + if (!Object.keys(newObject).length) { + return; + } + + const mutationString = currentObject + ? updateObjectWithId({ + kind: ACCOUNT_ROLE_OBJECT, + data: stringifyWithoutQuotes({ + id: currentObject.id, + ...newObject, + }), + }) + : createObject({ + kind: ACCOUNT_ROLE_OBJECT, + data: stringifyWithoutQuotes({ + ...newObject, + }), + }); + + const mutation = gql` + ${mutationString} + `; + + const result = await graphqlClient.mutate({ + mutation, + context: { + branch: branch?.name, + date, + }, + }); + + toast(, { + toastId: "alert-success-role-created", + }); + + if (onSuccess) await onSuccess(result?.data?.[`${ACCOUNT_ROLE_OBJECT}Create`]); + if (onUpdateComplete) await onUpdateComplete(); + } catch (error: unknown) { + console.error("An error occurred while creating the object: ", error); + } + } + + return ( +
+
+ + + + + + +
+ {onCancel && ( + + )} + + {currentObject ? "Update" : "Create"} +
+ +
+ ); +}; diff --git a/frontend/app/src/screens/role-management/accounts.tsx b/frontend/app/src/screens/role-management/accounts.tsx new file mode 100644 index 0000000000..c1b86ba39e --- /dev/null +++ b/frontend/app/src/screens/role-management/accounts.tsx @@ -0,0 +1,214 @@ +import { Button } from "@/components/buttons/button-primitive"; +import { ColorDisplay } from "@/components/display/color-display"; +import SlideOver, { SlideOverTitle } from "@/components/display/slide-over"; +import ObjectForm from "@/components/form/object-form"; +import ModalDeleteObject from "@/components/modals/modal-delete-object"; +import { Table, tRowValue } from "@/components/table/table"; +import { Pagination } from "@/components/ui/pagination"; +import { SearchInput } from "@/components/ui/search-input"; +import { ACCOUNT_GENERIC_OBJECT, ACCOUNT_OBJECT } from "@/config/constants"; +import graphqlClient from "@/graphql/graphqlClientApollo"; +import { GET_ROLE_MANAGEMENT_ACCOUNTS } from "@/graphql/queries/role-management/getAccounts"; +import { useDebounce } from "@/hooks/useDebounce"; +import useQuery from "@/hooks/useQuery"; +import { useSchema } from "@/hooks/useSchema"; +import { schemaKindNameState } from "@/state/atoms/schemaKindName.atom"; +import { NetworkStatus } from "@apollo/client"; +import { useAtomValue } from "jotai"; +import { useState } from "react"; +import ErrorScreen from "../errors/error-screen"; +import UnauthorizedScreen from "../errors/unauthorized-screen"; +import LoadingScreen from "../loading-screen/loading-screen"; +import { getPermission } from "../permission/utils"; +import { RelationshipDisplay } from "./relationship-display"; + +function Accounts() { + const [search, setSearch] = useState(""); + const searchDebounced = useDebounce(search, 300); + + const { + loading, + networkStatus, + data: latestData, + previousData, + error, + refetch, + } = useQuery(GET_ROLE_MANAGEMENT_ACCOUNTS, { + variables: { search: searchDebounced }, + notifyOnNetworkStatusChange: true, + }); + const data = latestData || previousData; + const schemaKindName = useAtomValue(schemaKindNameState); + const { schema } = useSchema(ACCOUNT_GENERIC_OBJECT); + + const [rowToDelete, setRowToDelete] = useState | null>(null); + const [rowToUpdate, setRowToUpdate] = useState | null>(null); + const [showDrawer, setShowDrawer] = useState(false); + + const permission = getPermission(data?.[ACCOUNT_GENERIC_OBJECT]?.permissions?.edges); + + const columns = [ + { + name: "name", + label: "Name", + }, + { + name: "description", + label: "Description", + }, + { + name: "account_type", + label: "Type", + }, + { + name: "status", + label: "Status", + }, + { + name: "member_of_groups", + label: "Groups", + }, + ]; + + const rows = + data && + data[ACCOUNT_GENERIC_OBJECT]?.edges.map((edge) => ({ + values: { + id: edge?.node?.id, + name: { value: edge?.node?.name?.value }, + description: { value: edge?.node?.description?.value }, + account_type: { value: edge?.node?.account_type?.value }, + status: { + value: edge?.node?.status?.value, + display: ( + + ), + }, + member_of_groups: { + value: { edges: edge?.node?.member_of_groups?.edges }, + display: ( + edge?.node?.display_label)} + /> + ), + }, + __typename: edge?.node?.__typename, + }, + })); + + if (error) { + if (error.networkError?.statusCode === 403) { + const { message } = error.networkError?.result?.errors?.[0] ?? {}; + + return ; + } + + return ; + } + + if (networkStatus === NetworkStatus.loading) { + return ; + } + + if (!permission?.view.isAllowed) { + return ; + } + + const globalRefetch = () => { + graphqlClient.refetchQueries({ include: ["GET_ROLE_MANAGEMENT_COUNTS"] }); + refetch(); + }; + + return ( + <> +
+
+ setSearch(e.target.value)} + placeholder="Search accounts" + className="border-none focus-visible:ring-0" + containerClassName="flex-grow" + /> + + +
+ +
setRowToDelete(row.values)} + onUpdate={(row) => { + setRowToUpdate(row.values); + setShowDrawer(true); + }} + permission={permission} + /> + + + + + { + setRowToDelete(null); + }} + onDelete={() => globalRefetch()} + /> + + {schema && ( + + } + open={showDrawer} + setOpen={(value) => setShowDrawer(value)} + onClose={() => setRowToUpdate(null)} + > + { + setRowToUpdate(null); + setShowDrawer(false); + }} + onSuccess={() => { + setShowDrawer(false); + globalRefetch(); + }} + /> + + )} + + ); +} + +export function Component() { + return ; +} diff --git a/frontend/app/src/screens/role-management/constants.ts b/frontend/app/src/screens/role-management/constants.ts new file mode 100644 index 0000000000..d7978c9fd4 --- /dev/null +++ b/frontend/app/src/screens/role-management/constants.ts @@ -0,0 +1,29 @@ +export const objectDecisionOptions = [ + { + value: 1, + label: "Deny everywhere", + }, + { + value: 2, + label: "Allow on default branch", + }, + { + value: 4, + label: "Allow on other branches", + }, + { + value: 6, + label: "Allow in all branches", + }, +]; + +export const globalDecisionOptions = [ + { + value: 1, + label: "Deny", + }, + { + value: 6, + label: "Allow", + }, +]; diff --git a/frontend/app/src/screens/role-management/global-permissions-form.tsx b/frontend/app/src/screens/role-management/global-permissions-form.tsx new file mode 100644 index 0000000000..682d3ca941 --- /dev/null +++ b/frontend/app/src/screens/role-management/global-permissions-form.tsx @@ -0,0 +1,158 @@ +import { Button } from "@/components/buttons/button-primitive"; +import { NodeFormProps } from "@/components/form/node-form"; +import { FormFieldValue } from "@/components/form/type"; +import { getCurrentFieldValue } from "@/components/form/utils/getFieldDefaultValue"; +import { getCreateMutationFromFormDataOnly } from "@/components/form/utils/mutations/getCreateMutationFromFormData"; +import { ALERT_TYPES, Alert } from "@/components/ui/alert"; +import { Form, FormSubmit } from "@/components/ui/form"; +import { ACCOUNT_ROLE_OBJECT, GLOBAL_PERMISSION_OBJECT } from "@/config/constants"; +import graphqlClient from "@/graphql/graphqlClientApollo"; +import { createObject } from "@/graphql/mutations/objects/createObject"; +import { updateObjectWithId } from "@/graphql/mutations/objects/updateObjectWithId"; +import { currentBranchAtom } from "@/state/atoms/branches.atom"; +import { datetimeAtom } from "@/state/atoms/time.atom"; +import { AttributeType, RelationshipType } from "@/utils/getObjectItemDisplayValue"; +import { stringifyWithoutQuotes } from "@/utils/string"; +import { gql } from "@apollo/client"; +import { useAtomValue } from "jotai"; +import { FieldValues, useForm } from "react-hook-form"; +import { toast } from "react-toastify"; + +import DropdownField from "@/components/form/fields/dropdown.field"; +import RelationshipField from "@/components/form/fields/relationship.field"; +import { getRelationshipDefaultValue } from "@/components/form/utils/getRelationshipDefaultValue"; +import { isRequired } from "@/components/form/utils/validation"; +import { useSchema } from "@/hooks/useSchema"; +import { globalDecisionOptions } from "./constants"; + +interface NumberPoolFormProps extends Pick { + currentObject?: Record; + onCancel?: () => void; + onUpdateComplete?: () => void; +} + +export const GlobalPermissionForm = ({ + currentObject, + onSuccess, + onCancel, + onUpdateComplete, +}: NumberPoolFormProps) => { + const branch = useAtomValue(currentBranchAtom); + const date = useAtomValue(datetimeAtom); + const { schema } = useSchema(GLOBAL_PERMISSION_OBJECT); + + const roles = getRelationshipDefaultValue({ + relationshipData: currentObject?.roles?.value, + }); + + const defaultValues = { + action: getCurrentFieldValue("action", currentObject), + decision: getCurrentFieldValue("decision", currentObject), + roles, + }; + + const form = useForm({ + defaultValues, + }); + + const actionOptions = schema?.attributes + ?.find((attribute) => { + return attribute.name === "action"; + }) + ?.choices?.map((choice) => { + return { + ...choice, + value: choice.name, + }; + }); + + async function handleSubmit(data: Record) { + try { + const newObject = getCreateMutationFromFormDataOnly(data, currentObject); + + if (!Object.keys(newObject).length) { + return; + } + + const mutationString = currentObject + ? updateObjectWithId({ + kind: GLOBAL_PERMISSION_OBJECT, + data: stringifyWithoutQuotes({ + id: currentObject.id, + ...newObject, + }), + }) + : createObject({ + kind: GLOBAL_PERMISSION_OBJECT, + data: stringifyWithoutQuotes({ + ...newObject, + }), + }); + + const mutation = gql` + ${mutationString} + `; + + const result = await graphqlClient.mutate({ + mutation, + context: { + branch: branch?.name, + date, + }, + }); + + toast(, { + toastId: "alert-success-object-permission-created", + }); + + if (onSuccess) await onSuccess(result?.data?.[`${GLOBAL_PERMISSION_OBJECT}Create`]); + if (onUpdateComplete) await onUpdateComplete(); + } catch (error: unknown) { + console.error("An error occurred while creating the object: ", error); + } + } + + return ( +
+
+ + + attribute.name === "decision")?.description + } + items={globalDecisionOptions} + rules={{ required: true, validate: { required: isRequired } }} + /> + + + +
+ {onCancel && ( + + )} + + {currentObject ? "Update" : "Create"} +
+ +
+ ); +}; diff --git a/frontend/app/src/screens/role-management/global-permissions.tsx b/frontend/app/src/screens/role-management/global-permissions.tsx new file mode 100644 index 0000000000..6bb2141050 --- /dev/null +++ b/frontend/app/src/screens/role-management/global-permissions.tsx @@ -0,0 +1,203 @@ +import { Button } from "@/components/buttons/button-primitive"; +import SlideOver, { SlideOverTitle } from "@/components/display/slide-over"; +import ObjectForm from "@/components/form/object-form"; +import ModalDeleteObject from "@/components/modals/modal-delete-object"; +import { Table, tRowValue } from "@/components/table/table"; +import { BadgeCopy } from "@/components/ui/badge-copy"; +import { Pagination } from "@/components/ui/pagination"; +import { SearchInput } from "@/components/ui/search-input"; +import { GLOBAL_PERMISSION_OBJECT } from "@/config/constants"; +import graphqlClient from "@/graphql/graphqlClientApollo"; +import { GET_ROLE_MANAGEMENT_GLOBAL_PERMISSIONS } from "@/graphql/queries/role-management/getGlobalPermissions"; +import { useDebounce } from "@/hooks/useDebounce"; +import useQuery from "@/hooks/useQuery"; +import { useSchema } from "@/hooks/useSchema"; +import { schemaKindNameState } from "@/state/atoms/schemaKindName.atom"; +import { NetworkStatus } from "@apollo/client"; +import { useAtomValue } from "jotai"; +import { useState } from "react"; +import ErrorScreen from "../errors/error-screen"; +import UnauthorizedScreen from "../errors/unauthorized-screen"; +import LoadingScreen from "../loading-screen/loading-screen"; +import { getPermission } from "../permission/utils"; +import { globalDecisionOptions } from "./constants"; +import { RelationshipDisplay } from "./relationship-display"; + +function GlobalPermissions() { + const schemaKindName = useAtomValue(schemaKindNameState); + const { schema } = useSchema(GLOBAL_PERMISSION_OBJECT); + const [search, setSearch] = useState(""); + const searchDebounced = useDebounce(search, 300); + const { + loading, + networkStatus, + data: latestData, + previousData, + error, + refetch, + } = useQuery(GET_ROLE_MANAGEMENT_GLOBAL_PERMISSIONS, { + variables: { search: searchDebounced }, + notifyOnNetworkStatusChange: true, + }); + const data = latestData || previousData; + const [rowToDelete, setRowToDelete] = useState | null>(null); + const [rowToUpdate, setRowToUpdate] = useState | null>(null); + const [showDrawer, setShowDrawer] = useState(false); + + const permission = getPermission(data?.[GLOBAL_PERMISSION_OBJECT]?.permissions?.edges); + + const columns = [ + { + name: "identifier", + label: "Identifier", + }, + { + name: "action", + label: "Action", + }, + { + name: "decision", + label: "Decision", + }, + { + name: "roles", + label: "Roles", + }, + ]; + + const rows = + data && + data[GLOBAL_PERMISSION_OBJECT]?.edges.map((edge) => { + return { + values: { + id: { value: edge?.node?.id }, + display_label: { value: edge?.node?.display_label }, + action: { value: edge?.node?.action?.value }, + decision: { + display: globalDecisionOptions.find( + (decision) => decision.value === edge?.node?.decision?.value + )?.label, + value: edge?.node?.decision?.value, + }, + roles: { + display: ( + edge?.node?.display_label)} + /> + ), + value: { edges: edge?.node?.roles?.edges }, + }, + identifier: { display: }, + }, + }; + }); + + const globalRefetch = () => { + graphqlClient.refetchQueries({ include: ["GET_ROLE_MANAGEMENT_COUNTS"] }); + refetch(); + }; + + if (error) { + if (error.networkError?.statusCode === 403) { + const { message } = error.networkError?.result?.errors?.[0] ?? {}; + + return ; + } + + return ; + } + + if (networkStatus === NetworkStatus.loading) { + return ; + } + + if (!permission?.view.isAllowed) { + return ; + } + + return ( + <> +
+
+ setSearch(e.target.value)} + placeholder="Search global permissions" + className="border-none focus-visible:ring-0" + containerClassName="flex-grow" + /> + + +
+ +
setRowToDelete(data.values)} + onUpdate={(row) => { + setRowToUpdate(row.values); + setShowDrawer(true); + }} + className="border-0" + permission={permission} + /> + + + + + setRowToDelete(null)} + onDelete={() => globalRefetch()} + /> + + {schema && ( + + } + open={showDrawer} + setOpen={(value) => setShowDrawer(value)} + onClose={() => setRowToUpdate(null)} + > + { + setRowToUpdate(null); + setShowDrawer(false); + }} + onSuccess={() => { + setShowDrawer(false); + globalRefetch(); + }} + /> + + )} + + ); +} + +export function Component() { + return ; +} diff --git a/frontend/app/src/screens/role-management/group-member.tsx b/frontend/app/src/screens/role-management/group-member.tsx new file mode 100644 index 0000000000..79968a9660 --- /dev/null +++ b/frontend/app/src/screens/role-management/group-member.tsx @@ -0,0 +1,48 @@ +import { Button } from "@/components/buttons/button-primitive"; +import { Avatar } from "@/components/display/avatar"; +import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; +import { Tooltip } from "@/components/ui/tooltip"; + +interface GroupMembersProps { + members: Array; +} + +export function GroupMembers({ members }: GroupMembersProps) { + const trimedMembers = members.slice(0, 5); + const remainingItems = members.slice(5); + + return ( +
+
+ {trimedMembers.map((member, index) => ( + + + + ))} +
+ + {!!remainingItems?.length && ( + + + + + + +
+ {remainingItems.map((item, index) => ( + + + + ))} +
+
+
+ )} +
+ ); +} diff --git a/frontend/app/src/screens/role-management/groups.tsx b/frontend/app/src/screens/role-management/groups.tsx new file mode 100644 index 0000000000..a4280a49f3 --- /dev/null +++ b/frontend/app/src/screens/role-management/groups.tsx @@ -0,0 +1,208 @@ +import { Button } from "@/components/buttons/button-primitive"; +import SlideOver, { SlideOverTitle } from "@/components/display/slide-over"; +import ObjectForm from "@/components/form/object-form"; +import ModalDeleteObject from "@/components/modals/modal-delete-object"; +import { Table, tRowValue } from "@/components/table/table"; +import { Pagination } from "@/components/ui/pagination"; +import { SearchInput } from "@/components/ui/search-input"; +import { ACCOUNT_GROUP_OBJECT } from "@/config/constants"; +import graphqlClient from "@/graphql/graphqlClientApollo"; +import { GET_ROLE_MANAGEMENT_GROUPS } from "@/graphql/queries/role-management/getGroups"; +import { useDebounce } from "@/hooks/useDebounce"; +import useQuery from "@/hooks/useQuery"; +import { useSchema } from "@/hooks/useSchema"; +import { schemaKindNameState } from "@/state/atoms/schemaKindName.atom"; +import { NetworkStatus } from "@apollo/client"; +import { useAtomValue } from "jotai"; +import { useState } from "react"; +import ErrorScreen from "../errors/error-screen"; +import UnauthorizedScreen from "../errors/unauthorized-screen"; +import LoadingScreen from "../loading-screen/loading-screen"; +import { getPermission } from "../permission/utils"; +import { GroupMembers } from "./group-member"; +import { RelationshipDisplay } from "./relationship-display"; + +function Groups() { + const [search, setSearch] = useState(""); + const searchDebounced = useDebounce(search, 300); + const { + loading, + networkStatus, + data: latestData, + previousData, + error, + refetch, + } = useQuery(GET_ROLE_MANAGEMENT_GROUPS, { + variables: { search: searchDebounced }, + notifyOnNetworkStatusChange: true, + }); + const data = latestData || previousData; + const schemaKindName = useAtomValue(schemaKindNameState); + const { schema } = useSchema(ACCOUNT_GROUP_OBJECT); + const [rowToDelete, setRowToDelete] = useState | null>(null); + const [rowToUpdate, setRowToUpdate] = useState | null>(null); + const [showDrawer, setShowDrawer] = useState(false); + + const permission = getPermission(data?.[ACCOUNT_GROUP_OBJECT]?.permissions?.edges); + + const columns = [ + { + name: "name", + label: "Name", + }, + { + name: "description", + label: "Description", + }, + { + name: "account_type", + label: "Type", + }, + { + name: "members", + label: "Members", + }, + { + name: "roles", + label: "Roles", + }, + ]; + + const rows = + data && + data[ACCOUNT_GROUP_OBJECT]?.edges.map((edge) => ({ + id: edge?.node?.id, + values: { + id: edge?.node?.id, + name: { value: edge?.node?.name?.value }, + description: { value: edge?.node?.description?.value }, + group_type: { value: edge?.node?.group_type?.value }, + members: { + value: { edges: edge?.node?.members?.edges }, + display: ( + edge?.node?.display_label) ?? []} + /> + ), + }, + roles: { + value: { edges: edge?.node?.roles?.edges }, + display: ( + edge?.node?.display_label)} + /> + ), + }, + __typename: edge?.node?.__typename, + }, + })); + + if (error) { + if (error.networkError?.statusCode === 403) { + const { message } = error.networkError?.result?.errors?.[0] ?? {}; + + return ; + } + + return ; + } + + if (networkStatus === NetworkStatus.loading) { + return ; + } + + if (!permission?.view.isAllowed) { + return ; + } + + const globalRefetch = () => { + graphqlClient.refetchQueries({ include: ["GET_ROLE_MANAGEMENT_COUNTS"] }); + refetch(); + }; + + return ( + <> +
+
+ setSearch(e.target.value)} + placeholder="Search groups" + className="border-none focus-visible:ring-0" + containerClassName="flex-grow" + /> + + +
+ +
setRowToDelete(data.values)} + onUpdate={(row) => { + setRowToUpdate(row.values); + setShowDrawer(true); + }} + permission={permission} + /> + + + + + setRowToDelete(null)} + onDelete={() => globalRefetch()} + /> + + {schema && ( + + } + open={showDrawer} + setOpen={(value) => setShowDrawer(value)} + onClose={() => setRowToUpdate(null)} + > + { + setRowToUpdate(null); + setShowDrawer(false); + }} + onSuccess={() => { + setShowDrawer(false); + globalRefetch(); + }} + /> + + )} + + ); +} + +export function Component() { + return ; +} diff --git a/frontend/app/src/screens/role-management/index.tsx b/frontend/app/src/screens/role-management/index.tsx new file mode 100644 index 0000000000..766d799ce6 --- /dev/null +++ b/frontend/app/src/screens/role-management/index.tsx @@ -0,0 +1,82 @@ +import { Tabs } from "@/components/tabs-routes"; +import { + ACCOUNT_GENERIC_OBJECT, + ACCOUNT_GROUP_OBJECT, + ACCOUNT_ROLE_OBJECT, + GLOBAL_PERMISSION_OBJECT, + OBJECT_PERMISSION_OBJECT, +} from "@/config/constants"; +import { GET_ROLE_MANAGEMENT_COUNTS } from "@/graphql/queries/role-management/getCounts"; +import useQuery from "@/hooks/useQuery"; + +import { constructPath } from "@/utils/fetch"; +import { Icon } from "@iconify-icon/react"; + +export function RoleManagementNavigation() { + const { loading, data, error } = useQuery(GET_ROLE_MANAGEMENT_COUNTS); + + const tabs = [ + { + to: constructPath("/role-management"), + label: ( +
+ + Accounts +
+ ), + count: data && data[ACCOUNT_GENERIC_OBJECT]?.count, + isLoading: loading, + error: !!error, + }, + { + to: constructPath("/role-management/groups"), + label: ( +
+ + Groups +
+ ), + count: data && data[ACCOUNT_GROUP_OBJECT]?.count, + isLoading: loading, + error: !!error, + }, + { + to: constructPath("/role-management/roles"), + label: ( +
+ + Roles +
+ ), + count: data && data[ACCOUNT_ROLE_OBJECT]?.count, + isLoading: loading, + error: !!error, + }, + { + to: constructPath("/role-management/global-permissions"), + label: ( +
+ + Global Permissions +
+ ), + count: data && data[GLOBAL_PERMISSION_OBJECT]?.count, + isLoading: loading, + error: !!error, + }, + { + to: constructPath("/role-management/object-permissions"), + label: ( +
+ + Object Permissions +
+ ), + count: data && data[OBJECT_PERMISSION_OBJECT]?.count, + isLoading: loading, + error: !!error, + }, + ]; + + return ; +} diff --git a/frontend/app/src/screens/role-management/object-permissions-form.tsx b/frontend/app/src/screens/role-management/object-permissions-form.tsx new file mode 100644 index 0000000000..9d35ef97a1 --- /dev/null +++ b/frontend/app/src/screens/role-management/object-permissions-form.tsx @@ -0,0 +1,267 @@ +import { Button } from "@/components/buttons/button-primitive"; +import { NodeFormProps } from "@/components/form/node-form"; +import { FormAttributeValue, FormFieldValue } from "@/components/form/type"; +import { getCurrentFieldValue } from "@/components/form/utils/getFieldDefaultValue"; +import { getCreateMutationFromFormDataOnly } from "@/components/form/utils/mutations/getCreateMutationFromFormData"; +import { ALERT_TYPES, Alert } from "@/components/ui/alert"; +import { Form, FormSubmit } from "@/components/ui/form"; +import { ACCOUNT_ROLE_OBJECT, OBJECT_PERMISSION_OBJECT } from "@/config/constants"; +import graphqlClient from "@/graphql/graphqlClientApollo"; +import { createObject } from "@/graphql/mutations/objects/createObject"; +import { updateObjectWithId } from "@/graphql/mutations/objects/updateObjectWithId"; +import { currentBranchAtom } from "@/state/atoms/branches.atom"; +import { namespacesState, schemaState } from "@/state/atoms/schema.atom"; +import { datetimeAtom } from "@/state/atoms/time.atom"; +import { AttributeType, RelationshipType } from "@/utils/getObjectItemDisplayValue"; +import { stringifyWithoutQuotes } from "@/utils/string"; +import { gql } from "@apollo/client"; +import { useAtomValue } from "jotai"; +import { FieldValues, useForm, useFormContext } from "react-hook-form"; +import { toast } from "react-toastify"; + +import { DEFAULT_FORM_FIELD_VALUE } from "@/components/form/constants"; +import DropdownField from "@/components/form/fields/dropdown.field"; +import RelationshipField from "@/components/form/fields/relationship.field"; +import { getRelationshipDefaultValue } from "@/components/form/utils/getRelationshipDefaultValue"; +import { isRequired } from "@/components/form/utils/validation"; +import { useSchema } from "@/hooks/useSchema"; +import { useEffect } from "react"; +import { objectDecisionOptions } from "./constants"; + +interface NumberPoolFormProps extends Pick { + currentObject?: Record; + onCancel?: () => void; + onUpdateComplete?: () => void; +} + +export const ObjectPermissionForm = ({ + currentObject, + onSuccess, + onCancel, + onUpdateComplete, +}: NumberPoolFormProps) => { + const { schema } = useSchema(OBJECT_PERMISSION_OBJECT); + const branch = useAtomValue(currentBranchAtom); + const date = useAtomValue(datetimeAtom); + + const roles = getRelationshipDefaultValue({ + relationshipData: currentObject?.roles?.value, + }); + + const defaultValues = { + namespace: getCurrentFieldValue("namespace", currentObject), + name: getCurrentFieldValue("name", currentObject), + action: getCurrentFieldValue("action", currentObject), + decision: getCurrentFieldValue("decision", currentObject), + roles, + }; + + const form = useForm({ + defaultValues, + }); + + const actionOptions = [ + { + value: "any", + label: "*", + }, + { + value: "view", + label: "View", + }, + { + value: "create", + label: "Create", + }, + { + value: "update", + label: "Update", + }, + { + value: "delete", + label: "Delete", + }, + ]; + + async function handleSubmit(data: Record) { + try { + const newObject = getCreateMutationFromFormDataOnly(data, currentObject); + + if (!Object.keys(newObject).length) { + return; + } + + const mutationString = currentObject + ? updateObjectWithId({ + kind: OBJECT_PERMISSION_OBJECT, + data: stringifyWithoutQuotes({ + id: currentObject.id, + ...newObject, + }), + }) + : createObject({ + kind: OBJECT_PERMISSION_OBJECT, + data: stringifyWithoutQuotes({ + ...newObject, + }), + }); + + const mutation = gql` + ${mutationString} + `; + + const result = await graphqlClient.mutate({ + mutation, + context: { + branch: branch?.name, + date, + }, + }); + + toast(, { + toastId: "alert-success-object-permission-created", + }); + + if (onSuccess) await onSuccess(result?.data?.[`${OBJECT_PERMISSION_OBJECT}Create`]); + if (onUpdateComplete) await onUpdateComplete(); + } catch (error: unknown) { + console.error("An error occurred while creating the object: ", error); + } + } + + return ( +
+
+ + + + + attribute.name === "decision")?.description + } + items={objectDecisionOptions} + rules={{ required: true, validate: { required: isRequired } }} + /> + + + +
+ {onCancel && ( + + )} + + {currentObject ? "Update" : "Create"} +
+ +
+ ); +}; + +const NodeSelect = () => { + const namespaces = useAtomValue(namespacesState); + const nodes = useAtomValue(schemaState); + + const form = useFormContext(); + const selectedNamespaceField: FormAttributeValue = form.watch("namespace"); + const selectedNameField: FormAttributeValue = form.watch("name"); + + const namespaceOptions = [ + { + value: "*", + label: "*", + }, + ...namespaces.map((namespace) => { + return { + value: namespace.name, + label: namespace.name, + }; + }), + ]; + + const selectedNamespace = + selectedNamespaceField?.value === "*" + ? { value: "*", name: "*" } + : namespaces + .filter((namespace) => { + if (!selectedNameField?.value) { + return true; + } + + return namespace.used_by?.includes(selectedNameField?.value); + }) + .find((namespace) => { + return namespace.name === selectedNamespaceField?.value; + }); + + const nameOptions = [ + { + value: "*", + label: "*", + }, + ...nodes + .filter((node) => { + if (!selectedNamespace || selectedNamespace?.name === "*") return true; + + return node.namespace === selectedNamespace?.name; + }) + .map((node) => ({ + value: node.name, + label: node.label, + badge: node.namespace, + })), + ]; + + useEffect(() => { + // Break if namespace already set + if (selectedNamespaceField?.value) return; + + // Break if no name is provided + if (!selectedNameField?.value) return; + + // Get current node from form field value + const currentNode = nodes.find((node) => node.name === selectedNameField?.value); + if (!currentNode) return; + + form.setValue("namespace", { value: currentNode.namespace, label: currentNode.namespace }); + }, [selectedNameField?.value]); + + return ( + <> + + + + + ); +}; diff --git a/frontend/app/src/screens/role-management/object-permissions.tsx b/frontend/app/src/screens/role-management/object-permissions.tsx new file mode 100644 index 0000000000..769d3b975b --- /dev/null +++ b/frontend/app/src/screens/role-management/object-permissions.tsx @@ -0,0 +1,249 @@ +import { Button } from "@/components/buttons/button-primitive"; +import { Pill } from "@/components/display/pill"; +import SlideOver, { SlideOverTitle } from "@/components/display/slide-over"; +import ObjectForm from "@/components/form/object-form"; +import ModalDeleteObject from "@/components/modals/modal-delete-object"; +import { Table, tRowValue } from "@/components/table/table"; +import { BadgeCopy } from "@/components/ui/badge-copy"; +import { Pagination } from "@/components/ui/pagination"; +import { SearchInput } from "@/components/ui/search-input"; +import { OBJECT_PERMISSION_OBJECT } from "@/config/constants"; +import graphqlClient from "@/graphql/graphqlClientApollo"; +import { GET_ROLE_MANAGEMENT_OBJECT_PERMISSIONS } from "@/graphql/queries/role-management/getObjectPermissions"; +import { useDebounce } from "@/hooks/useDebounce"; +import useQuery from "@/hooks/useQuery"; +import { useSchema } from "@/hooks/useSchema"; +import { schemaKindNameState } from "@/state/atoms/schemaKindName.atom"; +import { NetworkStatus } from "@apollo/client"; +import { Icon } from "@iconify-icon/react"; +import { useAtomValue } from "jotai"; +import { ReactNode, useState } from "react"; +import ErrorScreen from "../errors/error-screen"; +import UnauthorizedScreen from "../errors/unauthorized-screen"; +import LoadingScreen from "../loading-screen/loading-screen"; +import { getPermission } from "../permission/utils"; +import { objectDecisionOptions } from "./constants"; +import { RelationshipDisplay } from "./relationship-display"; + +const icons: Record = { + allow: ( + + + + ), + deny: ( + + + + ), +}; + +function Permissions() { + const [search, setSearch] = useState(""); + const searchDebounced = useDebounce(search, 300); + const { + loading, + networkStatus, + data: latestData, + previousData, + error, + refetch, + } = useQuery(GET_ROLE_MANAGEMENT_OBJECT_PERMISSIONS, { + variables: { search: searchDebounced }, + notifyOnNetworkStatusChange: true, + }); + const data = latestData || previousData; + const schemaKindName = useAtomValue(schemaKindNameState); + const { schema } = useSchema(OBJECT_PERMISSION_OBJECT); + const [rowToDelete, setRowToDelete] = useState | null>(null); + const [rowToUpdate, setRowToUpdate] = useState | null>(null); + const [showDrawer, setShowDrawer] = useState(false); + + const permission = getPermission(data?.[OBJECT_PERMISSION_OBJECT]?.permissions?.edges); + + const columns = [ + { + name: "identifier", + label: "Identifier", + }, + { + name: "namespace", + label: "Namespace", + }, + { + name: "name", + label: "Name", + }, + { + name: "action", + label: "Action", + }, + { + name: "decision", + label: "Decision", + }, + { + name: "roles", + label: "Roles", + }, + ]; + + const rows = + data && + data[OBJECT_PERMISSION_OBJECT]?.edges.map((edge) => { + const iconKey = edge?.node?.decision?.value; + const icon = icons[iconKey]; + + return { + values: { + id: edge?.node?.id, + display_label: edge?.node?.display_label, + display: { + value: edge?.node?.display_label, + display: ( +
+ {icon} {edge?.node?.display_label} +
+ ), + }, + namespace: { + value: edge?.node?.namespace?.value, + }, + name: { + value: edge?.node?.name?.value, + }, + action: { + value: edge?.node?.action?.value, + }, + decision: { + display: objectDecisionOptions.find( + (decision) => decision.value === edge?.node?.decision?.value + )?.label, + value: edge?.node?.decision?.value, + }, + roles: { + value: { edges: edge?.node?.roles?.edges }, + display: ( + edge?.node?.display_label)} + /> + ), + }, + identifier: { + value: edge?.node?.identifier?.value, + display: , + }, + __typename: edge?.node?.__typename, + }, + }; + }); + + if (error) { + if (error.networkError?.statusCode === 403) { + const { message } = error.networkError?.result?.errors?.[0] ?? {}; + + return ; + } + + return ; + } + + if (networkStatus === NetworkStatus.loading) { + return ; + } + + if (!permission?.view.isAllowed) { + return ; + } + + const globalRefetch = () => { + graphqlClient.refetchQueries({ include: ["GET_ROLE_MANAGEMENT_COUNTS"] }); + refetch(); + }; + + return ( + <> +
+
+ setSearch(e.target.value)} + placeholder="Search object permissions" + className="border-none focus-visible:ring-0" + containerClassName="flex-grow" + /> + + +
+ +
setRowToDelete(data.values)} + onUpdate={(row) => { + setRowToUpdate(row.values); + setShowDrawer(true); + }} + permission={permission} + /> + + + + + setRowToDelete(null)} + onDelete={() => globalRefetch()} + /> + + {schema && ( + + } + open={showDrawer} + setOpen={(value) => setShowDrawer(value)} + onClose={() => setRowToUpdate(null)} + > + { + setRowToUpdate(null); + setShowDrawer(false); + }} + onSuccess={() => { + setShowDrawer(false); + globalRefetch(); + }} + /> + + )} + + ); +} + +export function Component() { + return ; +} diff --git a/frontend/app/src/screens/role-management/relationship-display.tsx b/frontend/app/src/screens/role-management/relationship-display.tsx new file mode 100644 index 0000000000..2723303906 --- /dev/null +++ b/frontend/app/src/screens/role-management/relationship-display.tsx @@ -0,0 +1,38 @@ +import { Button } from "@/components/buttons/button-primitive"; +import { Badge } from "@/components/ui/badge"; +import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; + +interface RelationshipDisplayProps { + items: Array; +} + +export function RelationshipDisplay({ items }: RelationshipDisplayProps) { + const trimedItems = items.slice(0, 3); + const remainingItems = items.slice(3); + + return ( +
+
+ {trimedItems.map((item, index) => ( + {item} + ))} +
+ + {!!remainingItems?.length && ( + + + + + + +
+ {remainingItems.map((item, index) => ( + {item} + ))} +
+
+
+ )} +
+ ); +} diff --git a/frontend/app/src/screens/role-management/roles.tsx b/frontend/app/src/screens/role-management/roles.tsx new file mode 100644 index 0000000000..159f065ebd --- /dev/null +++ b/frontend/app/src/screens/role-management/roles.tsx @@ -0,0 +1,198 @@ +import ModalDeleteObject from "@/components/modals/modal-delete-object"; +import { Table, tRowValue } from "@/components/table/table"; +import { Pagination } from "@/components/ui/pagination"; +import { ACCOUNT_ROLE_OBJECT } from "@/config/constants"; +import { GET_ROLE_MANAGEMENT_ROLES } from "@/graphql/queries/role-management/getRoles"; +import { schemaKindNameState } from "@/state/atoms/schemaKindName.atom"; +import { useAtomValue } from "jotai"; +import { useState } from "react"; +import ErrorScreen from "../errors/error-screen"; +import LoadingScreen from "../loading-screen/loading-screen"; + +import { Button } from "@/components/buttons/button-primitive"; +import SlideOver, { SlideOverTitle } from "@/components/display/slide-over"; +import ObjectForm from "@/components/form/object-form"; +import { SearchInput } from "@/components/ui/search-input"; +import graphqlClient from "@/graphql/graphqlClientApollo"; +import { useDebounce } from "@/hooks/useDebounce"; +import useQuery from "@/hooks/useQuery"; +import { useSchema } from "@/hooks/useSchema"; +import { NetworkStatus } from "@apollo/client"; +import UnauthorizedScreen from "../errors/unauthorized-screen"; +import { getPermission } from "../permission/utils"; +import { RelationshipDisplay } from "./relationship-display"; + +function Roles() { + const [search, setSearch] = useState(""); + const searchDebounced = useDebounce(search, 300); + const { + loading, + networkStatus, + data: latestData, + previousData, + error, + refetch, + } = useQuery(GET_ROLE_MANAGEMENT_ROLES, { + variables: { search: searchDebounced }, + notifyOnNetworkStatusChange: true, + }); + const data = latestData || previousData; + const schemaKindName = useAtomValue(schemaKindNameState); + const { schema } = useSchema(ACCOUNT_ROLE_OBJECT); + const [rowToDelete, setRowToDelete] = useState | null>(null); + const [rowToUpdate, setRowToUpdate] = useState | null>(null); + const [showDrawer, setShowDrawer] = useState(false); + + const permission = getPermission(data?.[ACCOUNT_ROLE_OBJECT]?.permissions?.edges); + + const columns = [ + { + name: "name", + label: "Name", + }, + { + name: "groups", + label: "Groups", + }, + { + name: "permissions", + label: "Permissions", + }, + ]; + + const rows = + data && + data[ACCOUNT_ROLE_OBJECT]?.edges.map((edge) => ({ + values: { + id: edge?.node?.id, + name: { value: edge?.node?.name.value }, + description: { value: edge?.node?.description?.value }, + groups: { + value: { edges: edge?.node?.groups?.edges }, + display: ( + edge?.node?.display_label)} + /> + ), + }, + permissions: { + value: { edges: edge?.node?.permissions?.edges }, + display: ( + edge?.node?.identifier?.value)} + /> + ), + }, + __typename: edge?.node?.__typename, + }, + })); + + if (error) { + if (error.networkError?.statusCode === 403) { + const { message } = error.networkError?.result?.errors?.[0] ?? {}; + + return ; + } + + return ; + } + + if (networkStatus === NetworkStatus.loading) { + return ; + } + + if (!permission?.view.isAllowed) { + return ; + } + + const globalRefetch = () => { + graphqlClient.refetchQueries({ include: ["GET_ROLE_MANAGEMENT_COUNTS"] }); + refetch(); + }; + + return ( + <> +
+
+ setSearch(e.target.value)} + placeholder="Search roles" + className="border-none focus-visible:ring-0" + containerClassName="flex-grow" + /> + + +
+ +
setRowToDelete(data.values)} + onUpdate={(row) => { + setRowToUpdate(row.values); + setShowDrawer(true); + }} + permission={permission} + /> + + + + + setRowToDelete(null)} + onDelete={() => globalRefetch()} + /> + + {schema && ( + + } + open={showDrawer} + setOpen={(value) => setShowDrawer(value)} + onClose={() => setRowToUpdate(null)} + > + { + setRowToUpdate(null); + setShowDrawer(false); + }} + onSuccess={() => { + setShowDrawer(false); + globalRefetch(); + }} + /> + + )} + + ); +} + +export function Component() { + return ; +} diff --git a/frontend/app/src/screens/schema/attribute-display.tsx b/frontend/app/src/screens/schema/attribute-display.tsx index 6e7c20b4b4..2ad7b7ea66 100644 --- a/frontend/app/src/screens/schema/attribute-display.tsx +++ b/frontend/app/src/screens/schema/attribute-display.tsx @@ -13,7 +13,8 @@ export const AttributeDisplay = ({ description={attribute.description} isOptional={attribute.optional} isUnique={attribute.unique} - isReadOnly={attribute.read_only}> + isReadOnly={attribute.read_only} + >
@@ -65,7 +66,8 @@ const ChoicesRow = ({
} className="px-1.5 py-0.5 rounded-md flex-grow divide-y divide-gray-600" - style={{ backgroundColor: color ?? undefined }}> + style={{ backgroundColor: color ?? undefined }} + > diff --git a/frontend/app/src/screens/schema/relationship-display.tsx b/frontend/app/src/screens/schema/relationship-display.tsx index 872be6f518..cfb990559f 100644 --- a/frontend/app/src/screens/schema/relationship-display.tsx +++ b/frontend/app/src/screens/schema/relationship-display.tsx @@ -31,7 +31,8 @@ export const RelationshipDisplay = ({ } description={relationship.description} - isOptional={relationship.optional}> + isOptional={relationship.optional} + >
diff --git a/frontend/app/src/screens/schema/schema-help-menu.tsx b/frontend/app/src/screens/schema/schema-help-menu.tsx index 8c9aa43b0f..296691d950 100644 --- a/frontend/app/src/screens/schema/schema-help-menu.tsx +++ b/frontend/app/src/screens/schema/schema-help-menu.tsx @@ -6,10 +6,10 @@ import { DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { INFRAHUB_DOC_LOCAL } from "@/config/config"; -import { IModelSchema, menuFlatAtom } from "@/state/atoms/schema.atom"; -import { constructPath } from "@/utils/fetch"; +import { MENU_EXCLUDELIST } from "@/config/constants"; +import { IModelSchema } from "@/state/atoms/schema.atom"; +import { getObjectDetailsUrl2 } from "@/utils/objects"; import { Icon } from "@iconify-icon/react"; -import { useAtomValue } from "jotai/index"; import { Link } from "react-router-dom"; type SchemaHelpMenuProps = { @@ -17,14 +17,12 @@ type SchemaHelpMenuProps = { }; export const SchemaHelpMenu = ({ schema }: SchemaHelpMenuProps) => { - const menuItems = useAtomValue(menuFlatAtom); - const schemaInMenu = menuItems.find(({ title }) => title === schema.label); + const isListViewDisabled = MENU_EXCLUDELIST.includes(schema.kind as string); const documentationUrl = schema.documentation ? `${INFRAHUB_DOC_LOCAL}${schema.documentation}` : INFRAHUB_DOC_LOCAL; - const objectListUrl = schemaInMenu ? constructPath(schemaInMenu.path) : ""; return ( @@ -42,8 +40,8 @@ export const SchemaHelpMenu = ({ schema }: SchemaHelpMenuProps) => { - - + + Open list view diff --git a/frontend/app/src/screens/schema/schema-page-header.tsx b/frontend/app/src/screens/schema/schema-page-header.tsx deleted file mode 100644 index 4273e1971f..0000000000 --- a/frontend/app/src/screens/schema/schema-page-header.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { ReactElement } from "react"; - -type SchemaPageHeaderProps = { - title: ReactElement | string; - description?: string; -}; -export const SchemaPageHeader = ({ title, description }: SchemaPageHeaderProps) => { - return ( -
-

{title}

- {description &&

{description}

} -
- ); -}; diff --git a/frontend/app/src/screens/schema/schema-selector.tsx b/frontend/app/src/screens/schema/schema-selector.tsx index 6a366c615a..2ea765d3c3 100644 --- a/frontend/app/src/screens/schema/schema-selector.tsx +++ b/frontend/app/src/screens/schema/schema-selector.tsx @@ -1,12 +1,13 @@ import Accordion from "@/components/display/accordion"; import { Badge } from "@/components/ui/badge"; +import { SearchInput } from "@/components/ui/search-input"; import { QSP } from "@/config/qsp"; import { IModelSchema, genericsState, profilesAtom, schemaState } from "@/state/atoms/schema.atom"; import { classNames, isGeneric } from "@/utils/common"; import { Icon } from "@iconify-icon/react"; import { useAtomValue } from "jotai"; import * as R from "ramda"; -import { useEffect, useRef } from "react"; +import { useEffect, useRef, useState } from "react"; import { ArrayParam, useQueryParam } from "use-query-params"; type SchemaSelectorProps = { @@ -17,6 +18,7 @@ export const SchemaSelector = ({ className = "" }: SchemaSelectorProps) => { const nodes = useAtomValue(schemaState); const generics = useAtomValue(genericsState); const profiles = useAtomValue(profilesAtom); + const [search, setSearch] = useState(""); const ref = useRef(null); useEffect(() => { @@ -25,7 +27,10 @@ export const SchemaSelector = ({ className = "" }: SchemaSelectorProps) => { ref.current.scrollIntoView({ behavior: "smooth", block: "nearest" }); }, [selectedKind?.length]); - const schemas: IModelSchema[] = [...nodes, ...generics, ...profiles]; + const schemas: IModelSchema[] = [...nodes, ...generics, ...profiles].filter(({ kind }) => + kind?.toLowerCase().includes(search.toLowerCase()) + ); + const schemasPerNamespace = R.pipe( R.sortBy(R.prop("name")), R.groupBy(R.prop("namespace")) @@ -33,6 +38,13 @@ export const SchemaSelector = ({ className = "" }: SchemaSelectorProps) => { return (
+ setSearch(e.target.value)} + /> + {Object.entries(schemasPerNamespace).map(([namespace, schemas]) => { return ( @@ -52,7 +64,8 @@ export const SchemaSelector = ({ className = "" }: SchemaSelectorProps) => { hover:rounded ${isSelected ? "shadow-lg ring-1 ring-custom-blue-600 rounded" : ""} `} - onClick={() => setKind([schema.kind!])}> + onClick={() => setKind([schema.kind!])} + > {schema.icon && (
diff --git a/frontend/app/src/screens/schema/schema-viewer.tsx b/frontend/app/src/screens/schema/schema-viewer.tsx index 69eadbbafc..b69ff45810 100644 --- a/frontend/app/src/screens/schema/schema-viewer.tsx +++ b/frontend/app/src/screens/schema/schema-viewer.tsx @@ -70,7 +70,8 @@ export const SchemaViewer = ({ "flex flex-col overflow-hidden space-y-4 p-4 shadow-lg border border-gray-200 bg-custom-white rounded-md", className )} - data-testid="schema-viewer"> + data-testid="schema-viewer" + >
{schema.namespace} diff --git a/frontend/app/src/screens/schema/styled.tsx b/frontend/app/src/screens/schema/styled.tsx index 4d5a7295ea..121bf2fe2a 100644 --- a/frontend/app/src/screens/schema/styled.tsx +++ b/frontend/app/src/screens/schema/styled.tsx @@ -44,7 +44,8 @@ export const AccordionStyled = ({ } className="bg-custom-white shadow p-3 rounded" - {...props}> + {...props} + >
{children}
); @@ -107,7 +108,8 @@ export const TabStyled = ({ children }: { children: ReactElement | string }) => "px-4 py-2 text-sm hover:bg-gray-100 focus:outline-none focus:bg-gray-100", selected ? "border-b-2 border-b-custom-blue-600 font-semibold" : "" ) - }> + } + > {children} ); @@ -135,7 +137,8 @@ export const ModelDisplay = ({ kinds }: { kinds?: string[] }) => { className="bg-sky-50 text-sky-800 border-sky-200 hover:bg-sky-100 cursor-pointer" onClick={() => setKinds(selectedKinds && selectedKinds?.length > 0 ? [...selectedKinds, kind] : [kind]) - }> + } + > {kind} ))} diff --git a/frontend/app/src/screens/schema/types.ts b/frontend/app/src/screens/schema/types.ts index e2e4188341..39bd110fad 100644 --- a/frontend/app/src/screens/schema/types.ts +++ b/frontend/app/src/screens/schema/types.ts @@ -1,5 +1,5 @@ import { components } from "@/infraops"; -export type RelationshipSchema = components["schemas"]["RelationshipSchema-Output"]; +export type RelationshipSchema = components["schemas"]["RelationshipSchema"]; export type AttributeSchema = components["schemas"]["AttributeSchema-Output"]; diff --git a/frontend/app/src/screens/schema/utils.ts b/frontend/app/src/screens/schema/utils.ts new file mode 100644 index 0000000000..0008d811b1 --- /dev/null +++ b/frontend/app/src/screens/schema/utils.ts @@ -0,0 +1,8 @@ +import { IModelSchema } from "@/state/atoms/schema.atom"; +import { isGeneric } from "@/utils/common"; + +export const isOfKind = (kind: string, schema: IModelSchema) => { + if (schema.kind === kind) return true; + if (!isGeneric(schema) && schema.inherit_from?.includes(kind)) return true; + return false; +}; diff --git a/frontend/app/src/screens/tasks/task-item-details.tsx b/frontend/app/src/screens/tasks/task-item-details.tsx index 88dc56be53..5c6d82d9cf 100644 --- a/frontend/app/src/screens/tasks/task-item-details.tsx +++ b/frontend/app/src/screens/tasks/task-item-details.tsx @@ -51,7 +51,7 @@ export const TaskItemDetails = forwardRef((props, ref) => { return ; } - const result = data ? data[TASK_OBJECT] ?? {} : {}; + const result = data ? (data[TASK_OBJECT] ?? {}) : {}; const { edges = [] } = result; diff --git a/frontend/app/src/screens/tasks/task-items.tsx b/frontend/app/src/screens/tasks/task-items.tsx index 255e3591dd..34e71b0be9 100644 --- a/frontend/app/src/screens/tasks/task-items.tsx +++ b/frontend/app/src/screens/tasks/task-items.tsx @@ -48,7 +48,7 @@ export const TaskItems = forwardRef(({ hideRelatedNode }: TaskItemsProps, ref) = return ; } - const result = data ? data[TASK_OBJECT] ?? {} : {}; + const result = data ? (data[TASK_OBJECT] ?? {}) : {}; const { count, edges = [] } = result; @@ -107,7 +107,7 @@ export const TaskItems = forwardRef(({ hideRelatedNode }: TaskItemsProps, ref) = {rows && (
-
+
diff --git a/frontend/app/src/screens/user-profile/tab-profile.tsx b/frontend/app/src/screens/user-profile/tab-profile.tsx index b5a27bb77f..6fad37d6db 100644 --- a/frontend/app/src/screens/user-profile/tab-profile.tsx +++ b/frontend/app/src/screens/user-profile/tab-profile.tsx @@ -1,25 +1,24 @@ -import { ACCESS_TOKEN_KEY, ACCOUNT_OBJECT } from "@/config/constants"; -import ObjectItemDetails from "@/screens/object-item-details/object-item-details-paginated"; -import { parseJwt } from "@/utils/common"; -import { useAtomValue } from "jotai"; -import { genericsState } from "@/state/atoms/schema.atom"; +import { ACCOUNT_GENERIC_OBJECT } from "@/config/constants"; +import { ACCESS_TOKEN_KEY } from "@/config/localStorage"; import { useObjectDetails } from "@/hooks/useObjectDetails"; +import ErrorScreen from "@/screens/errors/error-screen"; import NoDataFound from "@/screens/errors/no-data-found"; -import { NetworkStatus } from "@apollo/client"; import LoadingScreen from "@/screens/loading-screen/loading-screen"; -import ErrorScreen from "@/screens/errors/error-screen"; -import { Card } from "@/components/ui/card"; -import Content from "../layout/content"; +import ObjectItemDetails from "@/screens/object-item-details/object-item-details-paginated"; +import { genericsState } from "@/state/atoms/schema.atom"; +import { parseJwt } from "@/utils/common"; +import { NetworkStatus } from "@apollo/client"; +import { useAtomValue } from "jotai"; export default function TabProfile() { const nodes = useAtomValue(genericsState); - const schema = nodes.find(({ kind }) => kind === ACCOUNT_OBJECT); + const schema = nodes.find(({ kind }) => kind === ACCOUNT_GENERIC_OBJECT); const localToken = localStorage.getItem(ACCESS_TOKEN_KEY); const tokenData = parseJwt(localToken); const accountId = tokenData?.sub; - const { data, error, networkStatus } = useObjectDetails(schema, accountId); + const { data, error, networkStatus, permission } = useObjectDetails(schema, accountId); const objectDetailsData = schema && data && data[schema.kind!]?.edges[0]?.node; @@ -40,10 +39,11 @@ export default function TabProfile() { } return ( - - - - - + ); } diff --git a/frontend/app/src/screens/user-profile/tab-tokens.tsx b/frontend/app/src/screens/user-profile/tab-tokens.tsx index 746a891188..a51aaa0fb4 100644 --- a/frontend/app/src/screens/user-profile/tab-tokens.tsx +++ b/frontend/app/src/screens/user-profile/tab-tokens.tsx @@ -1,14 +1,12 @@ -import { Card } from "@/components/ui/card"; -import Content from "@/screens/layout/content"; import { ACCOUNT_TOKEN_OBJECT } from "@/config/constants"; -import { useAtomValue } from "jotai"; import { schemaState } from "@/state/atoms/schema.atom"; -import ObjectItems from "../object-items/object-items-paginated"; -import LoadingScreen from "../loading-screen/loading-screen"; +import { useAtomValue } from "jotai"; import { useState } from "react"; +import LoadingScreen from "../loading-screen/loading-screen"; +import ObjectItems from "../object-items/object-items-paginated"; -import ModalSuccess from "@/components/modals/modal-success"; import { TokenInput } from "@/components/display/token-input"; +import ModalSuccess from "@/components/modals/modal-success"; export default function TabTokens() { const [open, setOpen] = useState(false); @@ -25,12 +23,7 @@ export default function TabTokens() { return ( <> - - -

Tokens

- -
-
+ You won't be able to see it again! - }> + } + >
diff --git a/frontend/app/src/screens/user-profile/tab-preferences.tsx b/frontend/app/src/screens/user-profile/tab-update-password.tsx similarity index 97% rename from frontend/app/src/screens/user-profile/tab-preferences.tsx rename to frontend/app/src/screens/user-profile/tab-update-password.tsx index 21e3b3e6bf..446c7792a2 100644 --- a/frontend/app/src/screens/user-profile/tab-preferences.tsx +++ b/frontend/app/src/screens/user-profile/tab-update-password.tsx @@ -1,19 +1,19 @@ +import PasswordInputField from "@/components/form/fields/password-input.field"; +import { isRequired } from "@/components/form/utils/validation"; import { ALERT_TYPES, Alert } from "@/components/ui/alert"; import { Card } from "@/components/ui/card"; +import { Form, FormSubmit } from "@/components/ui/form"; import { UPDATE_ACCOUNT_PASSWORD } from "@/graphql/mutations/accounts/updateAccountPassword"; +import { useMutation } from "@/hooks/useQuery"; import Content from "@/screens/layout/content"; import { toast } from "react-toastify"; -import { useMutation } from "@/hooks/useQuery"; -import { Form, FormSubmit } from "@/components/ui/form"; -import PasswordInputField from "@/components/form/fields/password-input.field"; -import { isRequired } from "@/components/form/utils/validation"; type UpdatePasswordFormData = { newPassword: string; confirmPassword: string; }; -export default function TabPreferences() { +export default function TabUpdatePassword() { const [updateAccountPassword] = useMutation(UPDATE_ACCOUNT_PASSWORD); const onSubmit = async ({ newPassword }: UpdatePasswordFormData) => { @@ -38,7 +38,8 @@ export default function TabPreferences() { confirmPassword: formData.confirmPassword.value as string, }; await onSubmit(data); - }}> + }} + > { switch (tab) { - case PROFILE_TABS.PREFERENCES: - return ; + case PROFILE_TABS.PASSWORD: + return ; case PROFILE_TABS.TOKENS: return ; default: @@ -54,7 +53,7 @@ export function UserProfilePage() { const schemaList = useAtomValue(genericsState); useTitle("Profile"); - const schema = schemaList.find((s) => s.kind === ACCOUNT_OBJECT); + const schema = schemaList.find((s) => s.kind === ACCOUNT_GENERIC_OBJECT); const queryString = schema ? getProfileDetails({ @@ -88,8 +87,8 @@ export function UserProfilePage() { } return ( - - + @@ -105,7 +104,7 @@ export function UserProfilePage() { -
{renderContent(qspTab)}
-
+ {renderContent(qspTab)} + ); } diff --git a/frontend/app/src/state/atoms/config.atom.ts b/frontend/app/src/state/atoms/config.atom.ts index 410ff24ad3..0e39e7c5a3 100644 --- a/frontend/app/src/state/atoms/config.atom.ts +++ b/frontend/app/src/state/atoms/config.atom.ts @@ -21,11 +21,24 @@ export type MainConfig = { allow_anonymous_access: boolean; }; +export type Provider = { + name: string; + display_label: string; + icon: string; + protocol: string; + readonly authorize_path: string; + readonly token_path: string; +}; + export type Config = { analytics: AnalyticsConfig; logging: LoggingConfig; main: MainConfig; experimental_features: { [key: string]: boolean }; + sso: { + enabled: boolean; + providers: Array; + }; }; export const configState = atom(undefined); diff --git a/frontend/app/src/state/atoms/filters.atom.ts b/frontend/app/src/state/atoms/filters.atom.ts deleted file mode 100644 index 8bab260c57..0000000000 --- a/frontend/app/src/state/atoms/filters.atom.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { atom } from "jotai"; - -// Need to save the data type of the fiter value. Could be string | number | boolean -export interface iComboBoxFilter { - name: string; - value: string; - display_label?: string; -} - -export const comboxBoxFilterState = atom([]); diff --git a/frontend/app/src/state/atoms/schema.atom.ts b/frontend/app/src/state/atoms/schema.atom.ts index 0dd02e7427..0f5aabc5b3 100644 --- a/frontend/app/src/state/atoms/schema.atom.ts +++ b/frontend/app/src/state/atoms/schema.atom.ts @@ -1,5 +1,5 @@ import { components } from "@/infraops"; -import { MenuItem } from "@/screens/layout/sidebar/desktop-menu"; +import { MenuData, MenuItem } from "@/screens/layout/menu-navigation/types"; import { atom } from "jotai"; export type iNodeSchema = components["schemas"]["APINodeSchema"]; @@ -11,7 +11,7 @@ export const genericsState = atom([]); export type IProfileSchema = components["schemas"]["APIProfileSchema"]; export const profilesAtom = atom([]); -export type IModelSchema = iGenericSchema | iNodeSchema; +export type IModelSchema = iGenericSchema | iNodeSchema | IProfileSchema; export type iNamespace = { name: string; @@ -21,16 +21,24 @@ export const namespacesState = atom([]); export const currentSchemaHashAtom = atom(null); -export const menuAtom = atom([]); +export const menuAtom = atom(); -const flattenMenuItems = (menuItems: MenuItem[]): MenuItem[] => { - return menuItems.reduce((acc, menuItem) => { - if (menuItem.children.length === 0) { - return [...acc, menuItem]; +export const menuFlatAtom = atom((get) => { + const menuData = get(menuAtom); + if (!menuData) return []; + + const menuItems: MenuItem[] = []; + + const flattenMenuItems = (menuItem: MenuItem) => { + if (menuItem.path !== "") menuItems.push(menuItem); + + if (menuItem.children && menuItem.children.length > 0) { + menuItem.children.forEach(flattenMenuItems); } + }; - return [...acc, ...flattenMenuItems(menuItem.children)]; - }, []); -}; + menuData.sections.object.forEach(flattenMenuItems); + menuData.sections.internal.forEach(flattenMenuItems); -export const menuFlatAtom = atom((get) => flattenMenuItems(get(menuAtom))); + return menuItems; +}); diff --git a/frontend/app/src/styles/index.css b/frontend/app/src/styles/index.css index e3be6decc0..cf42da013d 100644 --- a/frontend/app/src/styles/index.css +++ b/frontend/app/src/styles/index.css @@ -9,7 +9,7 @@ body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; - font-family: "Montserrat", sans-serif; + font-family: "Inter", sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } diff --git a/frontend/app/src/utils/checks.ts b/frontend/app/src/utils/checks.ts index 7c340dda70..c6206688d5 100644 --- a/frontend/app/src/utils/checks.ts +++ b/frontend/app/src/utils/checks.ts @@ -1,10 +1,4 @@ -import { - CHECKS_LABEL, - CHECK_CONCLUSIONS, - CHECK_SEVERITY, - VALIDATION_CONCLUSIONS, - VALIDATION_STATES, -} from "@/config/constants"; +import { CHECKS_LABEL, VALIDATION_CONCLUSIONS, VALIDATION_STATES } from "@/config/constants"; export const getValidatorsStats = (validators: any[]) => { const successValidators = validators.filter( @@ -70,49 +64,3 @@ export const getValidatorsStats = (validators: any[]) => { }, ].filter(Boolean); }; - -export const getChecksStats = (checks: any[]) => { - const inProgressChecks = checks.filter( - (validator: any) => validator.conclusion.value === CHECK_CONCLUSIONS.UNKNOWN - ); - - const successChecks = checks.filter( - (validator: any) => - validator.severity.value === CHECK_SEVERITY.SUCCESS && - validator.conclusion.value === CHECK_CONCLUSIONS.SUCCESS - ); - - const infoChecks = checks.filter( - (validator: any) => - validator.severity.value === CHECK_SEVERITY.INFO && - validator.conclusion.value === CHECK_CONCLUSIONS.SUCCESS - ); - - const warningChecks = checks.filter( - (validator: any) => - validator.severity.value === CHECK_SEVERITY.WARNING && - validator.conclusion.value === CHECK_CONCLUSIONS.FAILURE - ); - - const errorChecks = checks.filter( - (validator: any) => - validator.severity.value === CHECK_SEVERITY.ERROR && - validator.conclusion.value === CHECK_CONCLUSIONS.FAILURE - ); - - const criticalChecks = checks.filter( - (validator: any) => - validator.severity.value === CHECK_SEVERITY.CRITICAL && - validator.conclusion.value === CHECK_CONCLUSIONS.FAILURE - ); - - return { - total: checks.length, - success: successChecks.length, - info: infoChecks.length, - warning: warningChecks.length, - error: errorChecks.length, - critical: criticalChecks.length, - inProgress: inProgressChecks.length, - }; -}; diff --git a/frontend/app/src/utils/common.ts b/frontend/app/src/utils/common.ts index 21531132ba..1b61ef1dba 100644 --- a/frontend/app/src/utils/common.ts +++ b/frontend/app/src/utils/common.ts @@ -1,5 +1,5 @@ -import { iGenericSchema, IModelSchema } from "@/state/atoms/schema.atom"; -import { clsx, type ClassValue } from "clsx"; +import { IModelSchema, iGenericSchema } from "@/state/atoms/schema.atom"; +import { type ClassValue, clsx } from "clsx"; import * as R from "ramda"; import { twMerge } from "tailwind-merge"; @@ -43,24 +43,24 @@ export const encodeJwt = (data: any): string => { return `.${btoa(JSON.stringify(data))}`; }; -const DEFAULT_DEBOUNCE = 1000; - -export const debounce = (func: Function, wait = DEFAULT_DEBOUNCE, immediate?: boolean) => { - let timeout: any; - return function executedFunction(this: any) { - const context = this; - // eslint-disable-next-line prefer-rest-params - const args = arguments; - const later = () => { - timeout = null; - if (!immediate) func.apply(context, args); - }; - const callNow = immediate && !timeout; - clearTimeout(timeout); - timeout = setTimeout(later, wait); - if (callNow) func.apply(context, args); +const DEFAULT_DEBOUNCE = 300; + +export function debounce any>( + func: T, + delay: number = DEFAULT_DEBOUNCE +): (...args: Parameters) => void { + let timeoutId: ReturnType | null = null; + + return function (this: ThisParameterType, ...args: Parameters): void { + if (timeoutId) { + clearTimeout(timeoutId); + } + + timeoutId = setTimeout(() => { + func.apply(this, args); + }, delay); }; -}; +} // https://fontawesomeicons.com/fa/react-js-change-text-color-based-on-brightness-background const calculateBrightness = (color: string) => { diff --git a/frontend/app/src/utils/fetch.ts b/frontend/app/src/utils/fetch.ts index 2c6bc2b994..c841abfc43 100644 --- a/frontend/app/src/utils/fetch.ts +++ b/frontend/app/src/utils/fetch.ts @@ -92,29 +92,6 @@ export const constructPath = ( export const getCurrentQsp = () => new URL(window.location.href).searchParams; -// Update a QSP in the URL (add, update or remove it) -export const updateQsp = (qsp: string, newValue: string, setSearchParams: Function) => { - const { href } = window.location; - - const url = new URL(href); - - const { searchParams } = url; - - // Get QSP as [ [ key, value ], ... ] - const params = [...Array.from(searchParams), [qsp, newValue]]; - - // Construct the new params as { [name]: value } - const newParams = params.reduce( - (acc, [k, v]) => ({ - ...acc, - [k]: v, - }), - {} - ); - - return setSearchParams(newParams); -}; - export const getUrlWithQsp = (url: string, options: any[]) => { const qsp = new URLSearchParams(options); diff --git a/frontend/app/src/utils/formStructureForFilters.ts b/frontend/app/src/utils/formStructureForFilters.ts deleted file mode 100644 index 07b4c0887c..0000000000 --- a/frontend/app/src/utils/formStructureForFilters.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { DynamicFieldData } from "@/screens/edit-form-hook/dynamic-control-types"; -import { iComboBoxFilter } from "@/state/atoms/filters.atom"; - -const getFormStructureForFilters = ( - schema: any, - currentFilters: any, - peerDropdownOptions: any -): DynamicFieldData[] => { - return schema.filters - ?.map((filter: any) => { - const currentValue = currentFilters?.find((f: iComboBoxFilter) => f.name === filter.name); - - if (filter.kind === "Number") { - return { - label: filter.name, - name: filter.name, - type: "number", - value: currentValue ?? "", - }; - } - - if (filter.kind === "Text" && !filter.enum) { - return { - label: filter.name, - name: filter.name, - type: "text", - value: currentValue ?? "", - }; - } - - if (filter.kind === "Text" && filter.enum) { - return { - label: filter.name, - name: filter.name, - type: "select", - value: currentValue ?? "", - options: filter.enum?.map((row: any) => ({ - name: row, - id: row, - })), - }; - } - - if (filter.kind === "Object") { - if (filter.object_kind && peerDropdownOptions && peerDropdownOptions[filter.object_kind]) { - const { edges } = peerDropdownOptions[filter.object_kind]; - - const options = edges.map((row: any) => ({ - name: row.node.display_label, - id: row.node.id, - })); - - return { - label: filter.name, - name: filter.name, - type: "select", - value: currentValue ? currentValue.value : "", - options, - }; - } - } - - return null; - }) - .filter(Boolean); -}; - -export default getFormStructureForFilters; diff --git a/frontend/app/src/utils/getMutationMetaDetailsFromFormData.ts b/frontend/app/src/utils/getMutationMetaDetailsFromFormData.ts index 8266fc48a7..95103fe098 100644 --- a/frontend/app/src/utils/getMutationMetaDetailsFromFormData.ts +++ b/frontend/app/src/utils/getMutationMetaDetailsFromFormData.ts @@ -1,7 +1,5 @@ import { iNodeSchema } from "@/state/atoms/schema.atom"; -export type MutationMode = "create" | "update"; - const metadataFields = ["source", "owner", "is_visible", "is_protected"]; const isValueValid = (value: any) => { diff --git a/frontend/app/src/utils/getObjectItemDisplayValue.tsx b/frontend/app/src/utils/getObjectItemDisplayValue.tsx index d1c5ff76db..e6974bc011 100644 --- a/frontend/app/src/utils/getObjectItemDisplayValue.tsx +++ b/frontend/app/src/utils/getObjectItemDisplayValue.tsx @@ -19,8 +19,8 @@ import { RelationshipProperty, TextAttribute, } from "@/generated/graphql"; -import { components } from "@/infraops"; import { SchemaAttributeType } from "@/screens/edit-form-hook/dynamic-control-types"; +import { AttributeSchema, RelationshipSchema } from "@/screens/schema/types"; import { iSchemaKindNameMap } from "@/state/atoms/schemaKindName.atom"; import { CheckIcon, XMarkIcon } from "@heroicons/react/24/outline"; @@ -150,9 +150,7 @@ export const getObjectItemDisplayValue = ( ); }; -export type FieldSchema = - | components["schemas"]["AttributeSchema-Output"] - | components["schemas"]["RelationshipSchema-Output"]; +export type FieldSchema = AttributeSchema | RelationshipSchema; export type AttributeType = | TextAttribute diff --git a/frontend/app/src/utils/getSchemaObjectColumns.ts b/frontend/app/src/utils/getSchemaObjectColumns.ts index f2a88dc206..45cb30bf12 100644 --- a/frontend/app/src/utils/getSchemaObjectColumns.ts +++ b/frontend/app/src/utils/getSchemaObjectColumns.ts @@ -9,7 +9,6 @@ import { store } from "@/state"; import { iGenericSchema, iNodeSchema, profilesAtom } from "@/state/atoms/schema.atom"; import * as R from "ramda"; import { isGeneric, sortByOrderWeight } from "./common"; -import { SelectOption } from "@/components/inputs/select"; type tgetObjectAttributes = { schema: iNodeSchema | iGenericSchema | undefined; @@ -78,7 +77,7 @@ export const getObjectRelationships = ({ return relationships; }; -export const getTabs = (schema: iNodeSchema | iGenericSchema) => { +export const getTabs = (schema?: iNodeSchema | iGenericSchema) => { if (!schema) { return []; } @@ -185,46 +184,22 @@ export const getRelationshipOptions = (row: any, field: any, schemas: any[], gen return [option]; }; -export const getOptionsFromAttribute = (attribute: any, value: any): Array => { - if (attribute.kind === "List") { - return (value || [])?.map((option: any) => ({ - name: option, - id: option, - })); - } - - if (attribute.enum) { - return attribute.enum?.map((option: any) => ({ - name: option, - id: option, - })); - } - - if (attribute.choices) { - return attribute.choices?.map((option: any) => ({ - ...option, - name: option.label, - id: option.name, - })); - } - - return []; -}; - type tgetOptionsFromRelationship = { options: any[]; schemas?: any; generic?: any; + peerField?: string; }; export const getOptionsFromRelationship = ({ options, schemas, generic, + peerField, }: tgetOptionsFromRelationship) => { if (!generic) { return options.map((option: any) => ({ - name: option.display_label, + name: peerField ? (option[peerField]?.value ?? option[peerField]) : option.display_label, id: option.id, kind: option.__typename, })); diff --git a/frontend/app/src/utils/objects.ts b/frontend/app/src/utils/objects.ts index 54ad0aacb6..a42e927e35 100644 --- a/frontend/app/src/utils/objects.ts +++ b/frontend/app/src/utils/objects.ts @@ -1,14 +1,15 @@ import { constructPathForIpam } from "@/screens/ipam/common/utils"; import { - IP_ADDRESS_GENERIC, - IP_PREFIX_GENERIC, IPAM_QSP, IPAM_ROUTE, + IP_ADDRESS_GENERIC, + IP_PREFIX_GENERIC, } from "@/screens/ipam/constants"; +import { RESOURCE_GENERIC_KIND } from "@/screens/resource-manager/constants"; import { store } from "@/state"; -import { profilesAtom, schemaState } from "@/state/atoms/schema.atom"; +import { genericsState, profilesAtom, schemaState } from "@/state/atoms/schema.atom"; +import { isGeneric } from "@/utils/common"; import { constructPath, overrideQueryParams } from "./fetch"; -import { RESOURCE_GENERIC_KIND } from "@/screens/resource-manager/constants"; const regex = /^Related/; // starts with Related @@ -23,26 +24,40 @@ export const getObjectDetailsUrl2 = ( objectId?: string, overrideParams?: overrideQueryParams[] ) => { - const nodes = store.get(schemaState); - const profiles = store.get(profilesAtom); - const schema = [...nodes, ...profiles].find(({ kind }) => kind === objectKind); - if (!schema) return constructPath("/", overrideParams); - - const inheritFrom = schema.inherit_from; - - if (inheritFrom?.includes(IP_PREFIX_GENERIC)) { - return constructPathForIpam(`${IPAM_ROUTE.PREFIXES}/${objectId}`, overrideParams); + if (objectKind === IP_PREFIX_GENERIC) { + return constructPathForIpam(`${IPAM_ROUTE.PREFIXES}/${objectId ?? ""}`, overrideParams); } - if (inheritFrom?.includes(IP_ADDRESS_GENERIC)) { - return constructPathForIpam(`${IPAM_ROUTE.ADDRESSES}/${objectId}`, [ + if (objectKind === IP_ADDRESS_GENERIC) { + return constructPathForIpam(`${IPAM_ROUTE.ADDRESSES}/${objectId ?? ""}`, [ { name: IPAM_QSP.TAB, value: "ip-details" }, ...(overrideParams ?? []), ]); } - if (inheritFrom?.includes(RESOURCE_GENERIC_KIND)) { - return constructPathForIpam(`/resource-manager/${objectId}`, overrideParams); + const nodes = store.get(schemaState); + const generics = store.get(genericsState); + const profiles = store.get(profilesAtom); + const schema = [...nodes, ...generics, ...profiles].find(({ kind }) => kind === objectKind); + if (!schema) return "#"; + + if (!isGeneric(schema)) { + const inheritFrom = schema.inherit_from; + + if (inheritFrom?.includes(IP_PREFIX_GENERIC)) { + return constructPathForIpam(`${IPAM_ROUTE.PREFIXES}/${objectId ?? ""}`, overrideParams); + } + + if (inheritFrom?.includes(IP_ADDRESS_GENERIC)) { + return constructPathForIpam(`${IPAM_ROUTE.ADDRESSES}/${objectId ?? ""}`, [ + { name: IPAM_QSP.TAB, value: "ip-details" }, + ...(overrideParams ?? []), + ]); + } + + if (inheritFrom?.includes(RESOURCE_GENERIC_KIND)) { + return constructPathForIpam(`/resource-manager/${objectId ?? ""}`, overrideParams); + } } const path = objectId ? `/objects/${objectKind}/${objectId}` : `/objects/${objectKind}`; diff --git a/frontend/app/src/utils/string.tsx b/frontend/app/src/utils/string.tsx index 417f3f10b5..0576461b03 100644 --- a/frontend/app/src/utils/string.tsx +++ b/frontend/app/src/utils/string.tsx @@ -14,15 +14,9 @@ export const stringifyWithoutQuotes = (obj: object): string => { return JSON.stringify(obj, null, 4).replace(/"([^"]+)":/g, "$1:"); }; -export const cleanTabsAndNewLines = (string: string) => { - return string.replaceAll(/\t*\n*/g, "").replaceAll(/\s+/g, " "); -}; - export const capitalizeFirstLetter = (string: string) => { return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase(); }; -export const concatString = (acc: string, elem: string) => `${acc}${elem}`; - export const pluralize = (count: number, word: string, suffix = "s") => `${count} ${word}${count > 1 ? suffix : ""}`; diff --git a/frontend/app/tailwind.config.js b/frontend/app/tailwind.config.js index b0937125b3..033ba68914 100644 --- a/frontend/app/tailwind.config.js +++ b/frontend/app/tailwind.config.js @@ -65,6 +65,7 @@ export default { }, plugins: [ require("@tailwindcss/forms"), + require("tailwindcss-animate"), function ({ addUtilities, theme }) { const utilities = { ".bg-stripes": { diff --git a/frontend/app/tests/constants.ts b/frontend/app/tests/constants.ts index 153680c3b2..8b80fa29d8 100644 --- a/frontend/app/tests/constants.ts +++ b/frontend/app/tests/constants.ts @@ -1,32 +1,20 @@ -type ScreenshotConfig = { - overwrite: boolean; - scale: boolean; -}; - -export const screenshotConfig: ScreenshotConfig = { - overwrite: true, - scale: true, -}; - -export const SCREENSHOT_ENV_VARIABLE = "SCREENSHOTS"; - export const ADMIN_CREDENTIALS = { username: "admin", password: "infrahub", }; export const READ_WRITE_CREDENTIALS = { - username: "Chloe O'Brian", + username: "cobrian", password: "Password123", }; export const READ_ONLY_CREDENTIALS = { - username: "Jack Bauer", + username: "jbauer", password: "Password123", }; export const ENG_TEAM_ONLY_CREDENTIALS = { - username: "Engineering Team", + username: "shernandez", password: "Password123", }; diff --git a/frontend/app/tests/e2e/auth.setup.ts b/frontend/app/tests/e2e/auth.setup.ts index a12dc9746c..fc640b9fd7 100644 --- a/frontend/app/tests/e2e/auth.setup.ts +++ b/frontend/app/tests/e2e/auth.setup.ts @@ -7,34 +7,52 @@ import { } from "../constants"; setup("authenticate admin", async ({ page }) => { - await page.goto("/signin"); - await expect(page.getByText("Sign in to your account")).toBeVisible(); + await page.goto("/login"); + await expect(page.getByText("Log in to your account")).toBeVisible(); + const loginButton = page.getByRole("button", { + name: "Log in with your credentials", + }); + if (await loginButton.isVisible()) { + await loginButton.click(); + } await page.getByLabel("Username").fill(ADMIN_CREDENTIALS.username); await page.getByLabel("Password").fill(ADMIN_CREDENTIALS.password); - await page.getByRole("button", { name: "Sign in" }).click(); + await page.getByRole("button", { name: "Log in", exact: true }).click(); - await expect(page.getByTestId("current-user-avatar-button")).toBeVisible(); + await expect(page.getByTestId("authenticated-menu-trigger")).toBeVisible(); await page.context().storageState({ path: ACCOUNT_STATE_PATH.ADMIN }); }); setup("authenticate read-write", async ({ page }) => { - await page.goto("/signin"); - await expect(page.getByText("Sign in to your account")).toBeVisible(); + await page.goto("/login"); + await expect(page.getByText("Log in to your account")).toBeVisible(); + const loginButton = page.getByRole("button", { + name: "Log in with your credentials", + }); + if (await loginButton.isVisible()) { + await loginButton.click(); + } await page.getByLabel("Username").fill(READ_WRITE_CREDENTIALS.username); await page.getByLabel("Password").fill(READ_WRITE_CREDENTIALS.password); - await page.getByRole("button", { name: "Sign in" }).click(); + await page.getByRole("button", { name: "Log in", exact: true }).click(); - await expect(page.getByTestId("current-user-avatar-button")).toBeVisible(); + await expect(page.getByTestId("authenticated-menu-trigger")).toBeVisible(); await page.context().storageState({ path: ACCOUNT_STATE_PATH.READ_WRITE }); }); setup("authenticate read-only", async ({ page }) => { - await page.goto("/signin"); - await expect(page.getByText("Sign in to your account")).toBeVisible(); + await page.goto("/login"); + await expect(page.getByText("Log in to your account")).toBeVisible(); + const loginButton = page.getByRole("button", { + name: "Log in with your credentials", + }); + if (await loginButton.isVisible()) { + await loginButton.click(); + } await page.getByLabel("Username").fill(READ_ONLY_CREDENTIALS.username); await page.getByLabel("Password").fill(READ_ONLY_CREDENTIALS.password); - await page.getByRole("button", { name: "Sign in" }).click(); + await page.getByRole("button", { name: "Log in", exact: true }).click(); - await expect(page.getByTestId("current-user-avatar-button")).toBeVisible(); + await expect(page.getByTestId("authenticated-menu-trigger")).toBeVisible(); await page.context().storageState({ path: ACCOUNT_STATE_PATH.READ_ONLY }); }); diff --git a/frontend/app/tests/e2e/branches.spec.ts b/frontend/app/tests/e2e/branches.spec.ts index 0ea38cc253..5fa24ea8fa 100644 --- a/frontend/app/tests/e2e/branches.spec.ts +++ b/frontend/app/tests/e2e/branches.spec.ts @@ -14,7 +14,7 @@ test.describe("Branches creation and deletion", () => { test.describe("when not logged in", () => { test("should not be able to create a branch if not logged in", async ({ page }) => { await page.goto("/"); - await expect(page.getByTestId("branch-select-menu")).toContainText("main"); + await page.getByTestId("branch-selector-trigger").click(); await expect(page.getByTestId("create-branch-button")).toBeDisabled(); }); }); @@ -25,6 +25,7 @@ test.describe("Branches creation and deletion", () => { test("should create a new branch", async ({ page }) => { await page.goto("/"); + await page.getByTestId("branch-selector-trigger").click(); await page.getByTestId("create-branch-button").click(); // Form @@ -34,16 +35,16 @@ test.describe("Branches creation and deletion", () => { await page.getByRole("button", { name: "Create a new branch" }).click(); // After submit - await expect(page.getByTestId("branch-select-menu")).toContainText("test123"); + await expect(page.getByTestId("branch-selector-trigger")).toContainText("test123"); await expect(page).toHaveURL(/.*?branch=test123/); }); test("should display the new branch", async ({ page }) => { await page.goto("/"); - await page.getByTestId("branch-list-display-button").click(); - await expect(page.getByTestId("branch-list-dropdown")).toContainText("test123"); + await page.getByTestId("branch-selector-trigger").click(); + await expect(page.getByTestId("branch-list")).toContainText("test123"); - await page.getByTestId("sidebar-menu").getByText("Branches").click(); + await page.getByRole("link", { name: "View all branches" }).click(); await expect(page).toHaveURL(/.*\/branches/); await page.getByTestId("branches-items").getByText("test123").click(); @@ -71,23 +72,30 @@ test.describe("Branches creation and deletion", () => { await modalDelete.getByRole("button", { name: "Delete" }).click(); // we should stay on the branch test123 - await expect(page.getByTestId("branch-select-menu")).toContainText("test123"); - await page.getByTestId("branch-list-display-button").click(); - await expect(page.getByTestId("branch-list-dropdown")).toContainText("test123"); - await expect(page.getByTestId("branch-list-dropdown")).not.toContainText("test456"); + await expect(page.getByTestId("branch-selector-trigger")).toContainText("test123"); + await page.getByTestId("branch-selector-trigger").click(); + await expect(page.getByTestId("branch-list")).toContainText("test123"); + await expect(page.getByTestId("branch-list")).not.toContainText("test456"); expect(page.url()).toContain("/branches?branch=test123"); }); test("should delete the currently selected branch", async ({ page }) => { - await page.goto("/"); - await page.getByRole("link", { name: "Branches" }).click(); + await page.goto("/branches"); await page.getByText("test123").click(); await page.getByRole("button", { name: "Delete" }).click(); await page.getByTestId("modal-delete-confirm").click(); expect(page.url()).toContain("/branches"); - await page.getByTestId("branch-list-display-button").click(); - await expect(page.getByTestId("branch-list-dropdown")).not.toContainText("test123"); + await page.getByTestId("branch-selector-trigger").click(); + await expect(page.getByTestId("branch-list")).not.toContainText("test123"); }); }); + + test("allow to create a branch with a name that does not exists", async ({ page }) => { + await page.goto("/"); + await page.getByTestId("branch-selector-trigger").click(); + await page.getByTestId("branch-search-input").fill("quick-branch-form"); + await page.getByRole("option", { name: "Create branch quick-branch-form" }).click(); + await expect(page.getByLabel("New branch name *")).toHaveValue("quick-branch-form"); + }); }); diff --git a/frontend/app/tests/e2e/ipam/ip-address-list.spec.ts b/frontend/app/tests/e2e/ipam/ip-address-list.spec.ts index 32edbf10db..ec15762e49 100644 --- a/frontend/app/tests/e2e/ipam/ip-address-list.spec.ts +++ b/frontend/app/tests/e2e/ipam/ip-address-list.spec.ts @@ -31,13 +31,13 @@ test.describe("/ipam/addresses - IP Address list", () => { await page.goto("/ipam/addresses?ipam-tab=ip-details"); await test.step("select a prefix to view all ip addresses", async () => { - await page.getByRole("treeitem", { name: "172.20.20.0/27" }).click(); - await expect(page.getByText("172.20.20.0/27IP Addresses")).toBeVisible(); + await page.getByRole("treeitem", { name: "172.16.0.0/16" }).click(); + await expect(page.getByText("172.16.0.0/16IP Addresses")).toBeVisible(); await expect(page.getByText("Showing 1 to ")).toBeVisible(); }); await test.step("click on any ip address row to view summary", async () => { - await page.getByRole("link", { name: "172.20.20.1/28" }).click(); + await page.getByRole("link", { name: "172.16.0.1/16" }).click(); await expect(page.getByText("Ipam IP Address summary")).toBeVisible(); await expect(page.url()).toContain("ipam-tab=ip-details"); }); @@ -45,7 +45,7 @@ test.describe("/ipam/addresses - IP Address list", () => { await test.step("use breadcrumb to go back to parent prefix", async () => { await page.getByRole("link", { name: "All IP Addresses" }).click(); await expect(page.getByText("Showing 1 to ")).toBeVisible(); - await expect(page.locator("[aria-selected=true]")).toContainText("172.20.20.0/27"); + await expect(page.locator("[aria-selected=true]")).toContainText("172.16.0.0/16"); await expect(page.url()).toContain("/prefixes/"); }); }); diff --git a/frontend/app/tests/e2e/ipam/ip-namespace.spec.ts b/frontend/app/tests/e2e/ipam/ip-namespace.spec.ts index acf0f4ebaf..aafa819222 100644 --- a/frontend/app/tests/e2e/ipam/ip-namespace.spec.ts +++ b/frontend/app/tests/e2e/ipam/ip-namespace.spec.ts @@ -152,8 +152,11 @@ test.describe("/ipam - IP Namespace", () => { await test.step("delete a prefix between 2 other prefixes", async () => { await page.getByTestId("ipam-tree").getByRole("link", { name: "11.0.0.0/8" }).click(); await page.getByText("Prefix Details").click(); - - await page.getByRole("row", { name: "11.0.0.0/10" }).getByTestId("delete-row-button").click(); + await page + .getByRole("row", { name: "11.0.0.0/10" }) + .getByTestId("actions-row-button") + .click(); + await page.getByTestId("delete-row-button").click(); await expect(page.getByTestId("modal-delete")).toContainText( "Are you sure you want to delete the Prefix: 11.0.0.0/10" ); @@ -168,7 +171,11 @@ test.describe("/ipam - IP Namespace", () => { }); await test.step("delete a children prefix", async () => { - await page.getByRole("row", { name: "11.0.0.0/16" }).getByTestId("delete-row-button").click(); + await page + .getByRole("row", { name: "11.0.0.0/16" }) + .getByTestId("actions-row-button") + .click(); + await page.getByTestId("delete-row-button").click(); await expect(page.getByTestId("modal-delete")).toContainText( "Are you sure you want to delete the Prefix: 11.0.0.0/16" ); @@ -185,8 +192,8 @@ test.describe("/ipam - IP Namespace", () => { await test.step("delete top level prefix", async () => { await page.getByText("Summary").click(); await page.getByRole("link", { name: "All Prefixes" }).click(); - - await page.getByRole("row", { name: "11.0.0.0/8" }).getByTestId("delete-row-button").click(); + await page.getByRole("row", { name: "11.0.0.0/8" }).getByTestId("actions-row-button").click(); + await page.getByTestId("delete-row-button").click(); await expect(page.getByTestId("modal-delete")).toContainText( "Are you sure you want to delete the Prefix: 11.0.0.0/8" ); @@ -203,7 +210,6 @@ test.describe("/ipam - IP Namespace", () => { test("delete ip namespace", async ({ page }) => { await page.goto("/objects/BuiltinIPNamespace"); - await page .getByRole("row", { name: "test-namespace" }) .getByTestId("delete-row-button") diff --git a/frontend/app/tests/e2e/ipam/ipam-crud.spec.ts b/frontend/app/tests/e2e/ipam/ipam-crud.spec.ts index c6ed72b982..579d0d145e 100644 --- a/frontend/app/tests/e2e/ipam/ipam-crud.spec.ts +++ b/frontend/app/tests/e2e/ipam/ipam-crud.spec.ts @@ -22,7 +22,7 @@ test.describe("/ipam - Ipam home page", () => { test("should load all ipam home page elements", async ({ page }) => { await page.goto("/ipam"); - await expect(page.getByText("IP Address Manager")).toBeVisible(); + await expect(page.getByRole("heading", { name: "IP Address Manager" })).toBeVisible(); await expect(page.getByTestId("ipam-tree")).toBeVisible(); await expect(page.getByTestId("ipam-main-content")).toBeVisible(); }); @@ -89,11 +89,11 @@ test.describe("/ipam - Ipam home page", () => { await test.step("delete a prefix between 2 other prefixes", async () => { await page.getByTestId("ipam-tree").getByRole("link", { name: "11.0.0.0/8" }).click(); await page.getByText("Prefix Details").click(); - await page .getByRole("row", { name: "11.0.0.0/10" }) - .getByTestId("delete-row-button") + .getByTestId("actions-row-button") .click(); + await page.getByTestId("delete-row-button").click(); await expect(page.getByTestId("modal-delete")).toContainText( "Are you sure you want to delete the Prefix: 11.0.0.0/10" ); @@ -110,8 +110,9 @@ test.describe("/ipam - Ipam home page", () => { await test.step("delete a children prefix", async () => { await page .getByRole("row", { name: "11.0.0.0/16" }) - .getByTestId("delete-row-button") + .getByTestId("actions-row-button") .click(); + await page.getByTestId("delete-row-button").click(); await expect(page.getByTestId("modal-delete")).toContainText( "Are you sure you want to delete the Prefix: 11.0.0.0/16" ); @@ -136,8 +137,9 @@ test.describe("/ipam - Ipam home page", () => { await page.getByText("50").click(); await page .getByRole("row", { name: "11.0.0.0/8" }) - .getByTestId("delete-row-button") + .getByTestId("actions-row-button") .click(); + await page.getByTestId("delete-row-button").click(); await expect(page.getByTestId("modal-delete")).toContainText( "Are you sure you want to delete the Prefix: 11.0.0.0/8" ); @@ -175,8 +177,9 @@ test.describe("/ipam - Ipam home page", () => { await test.step("update ip address from list", async () => { await page .getByRole("row", { name: "10.0.0.1/16" }) - .getByTestId("update-row-button") + .getByTestId("actions-row-button") .click(); + await page.getByTestId("update-row-button").click(); await page.getByLabel("Description").fill("test"); await page.getByRole("button", { name: "Save" }).click(); await expect(page.getByText("IPAddress updated")).toBeVisible(); @@ -198,11 +201,11 @@ test.describe("/ipam - Ipam home page", () => { await test.step("delete ip address", async () => { await page.getByRole("link", { name: "All IP Addresses" }).click(); - await page .getByRole("row", { name: "10.0.0.1/16 from summary" }) - .getByTestId("delete-row-button") + .getByTestId("actions-row-button") .click(); + await page.getByTestId("delete-row-button").click(); await expect(page.getByTestId("modal-delete")).toContainText( "Are you sure you want to delete the IP address: 10.0.0.1/16" ); diff --git a/frontend/app/tests/e2e/ipam/prefix-list.spec.ts b/frontend/app/tests/e2e/ipam/prefix-list.spec.ts index c71207e9b5..ccf1741c9a 100644 --- a/frontend/app/tests/e2e/ipam/prefix-list.spec.ts +++ b/frontend/app/tests/e2e/ipam/prefix-list.spec.ts @@ -7,13 +7,13 @@ test.describe("/ipam/prefixes - Prefix list", () => { await page.getByRole("option", { name: "50" }).click(); await page .getByTestId("ipam-main-content") - .getByRole("row", { name: "203.0.113.0/24 - prefix" }) // prefix need pagination to be visible - .getByRole("link", { name: "203.0.113.0/24" }) // prefix need pagination to be visible + .getByRole("row", { name: "203.111.0.0/16 - prefix" }) // prefix need pagination to be visible + .getByRole("link", { name: "203.111.0.0/16" }) // prefix need pagination to be visible .click(); - expect(page.url()).toContain("/ipam/prefixes/"); await expect(page.getByText("Ipam IP Prefix summary")).toBeVisible(); - await expect(page.getByText("Prefix203.0.113.0/24")).toBeVisible(); - await expect(page.getByText("Utilization93%")).toBeVisible(); + expect(page.url()).toContain("/ipam/prefixes/"); + await expect(page.getByText("Prefix203.111.0.0/16")).toBeVisible(); + await expect(page.getByText("Utilization0%")).toBeVisible(); await expect(page.getByRole("progressbar")).toBeVisible(); await expect(page.getByText("Ip Namespacedefault")).toBeVisible(); }); @@ -25,13 +25,13 @@ test.describe("/ipam/prefixes - Prefix list", () => { ); await test.step("select a prefix to view all sub prefixes", async () => { - await page.getByRole("treeitem", { name: "2001:db8::/112" }).click(); - await expect(page.getByTestId("ipam-main-content")).toContainText("2001:db8::/112"); + await page.getByRole("treeitem", { name: "2001:db8::/100" }).click(); + await expect(page.getByTestId("ipam-main-content")).toContainText("2001:db8::/100"); await expect(page.getByTestId("ipam-main-content")).toContainText("Showing 1 to "); }); - await test.step("to to any sub prefix list of any children prefix", async () => { - await page.getByRole("link", { name: "2001:db8::/120" }).click(); + await test.step("go to any sub prefix list of any children prefix", async () => { + await page.getByRole("link", { name: "2001:db8::/110" }).click(); await expect(page.getByTestId("ipam-main-content")).toContainText("Showing 0 of 0 results"); await expect(page.url()).toContain("ipam-tab=prefix-details"); }); @@ -39,7 +39,7 @@ test.describe("/ipam/prefixes - Prefix list", () => { await test.step("use breadcrumb to go back to parent prefix", async () => { await page .getByTestId("ipam-main-content") - .getByRole("link", { name: "2001:db8::/112" }) + .getByRole("link", { name: "2001:db8::/100" }) .click(); await expect(page.getByTestId("ipam-main-content")).toContainText("Showing 1 to "); await expect(page.url()).toContain("ipam-tab=prefix-details"); diff --git a/frontend/app/tests/e2e/ipam/prefix-summary.spec.ts b/frontend/app/tests/e2e/ipam/prefix-summary.spec.ts index 2088db9069..49b054b0f2 100644 --- a/frontend/app/tests/e2e/ipam/prefix-summary.spec.ts +++ b/frontend/app/tests/e2e/ipam/prefix-summary.spec.ts @@ -4,8 +4,8 @@ test.describe("/ipam/prefixes/:prefixId - Prefix summary", () => { test("go to prefix summary when clicking on any tree item", async ({ page }) => { await page.goto("/ipam"); await page.getByTestId("ipam-tree").getByRole("link", { name: "10.0.0.0/8" }).click(); - expect(page.url()).toContain("/ipam/prefixes/"); await expect(page.getByText("Ipam IP Prefix summary")).toBeVisible(); + expect(page.url()).toContain("/ipam/prefixes/"); await expect(page.getByText("Prefix10.0.0.0/8")).toBeVisible(); await expect(page.getByText("Utilization1%")).toBeVisible(); await expect(page.getByRole("progressbar")).toBeVisible(); @@ -13,6 +13,7 @@ test.describe("/ipam/prefixes/:prefixId - Prefix summary", () => { await test.step("go to all prefixes with breadcrumb", async () => { await page.getByRole("link", { name: "All Prefixes" }).click(); + await expect(page.getByRole("columnheader", { name: "Prefix" })).toBeVisible(); expect(page.url()).toContain("/ipam/prefixes"); }); }); diff --git a/frontend/app/tests/e2e/signin.spec.ts b/frontend/app/tests/e2e/login.spec.ts similarity index 59% rename from frontend/app/tests/e2e/signin.spec.ts rename to frontend/app/tests/e2e/login.spec.ts index 4960eaa6f8..67a5686b43 100644 --- a/frontend/app/tests/e2e/signin.spec.ts +++ b/frontend/app/tests/e2e/login.spec.ts @@ -1,7 +1,7 @@ import { expect, test } from "@playwright/test"; import { ACCOUNT_STATE_PATH, ADMIN_CREDENTIALS } from "../constants"; -test.describe("/signin", () => { +test.describe("/login", () => { test.beforeEach(async function ({ page }) { page.on("response", async (response) => { if (response.status() === 500) { @@ -14,23 +14,25 @@ test.describe("/signin", () => { test("should log in the user", async ({ page }) => { await page.goto("/"); - await page.getByRole("link", { name: "Sign in" }).click(); - await expect(page.getByText("Sign in to your account")).toBeVisible(); + await page.getByRole("link", { name: "Log in anonymous" }).click(); + + await expect(page.getByText("Log in to your account")).toBeVisible(); await page.getByLabel("Username").fill(ADMIN_CREDENTIALS.username); await page.getByLabel("Password").fill(ADMIN_CREDENTIALS.password); - await page.getByRole("button", { name: "Sign in" }).click(); + await page.getByRole("button", { name: "Log in" }).click(); - await expect(page.getByTestId("current-user-avatar-button")).toBeVisible(); + await expect(page.getByTestId("authenticated-menu-trigger")).toBeVisible(); }); test("should display an error message when authentication fails", async ({ page }) => { await page.goto("/"); - await page.getByRole("link", { name: "Sign in" }).click(); - await expect(page.getByText("Sign in to your account")).toBeVisible(); + await page.getByRole("link", { name: "Log in anonymous" }).click(); + + await expect(page.getByText("Log in to your account")).toBeVisible(); await page.getByLabel("Username").fill("wrong username"); await page.getByLabel("Password").fill("wrong password"); - await page.getByRole("button", { name: "Sign in" }).click(); + await page.getByRole("button", { name: "Log in" }).click(); await expect(page.locator("#alert-error-sign-in")).toContainText( "Invalid username and password" @@ -38,20 +40,19 @@ test.describe("/signin", () => { }); test("should redirect to the initial page after login", async ({ page }) => { - await page.goto( - "/objects/BuiltinTag?branch=atl1-delete-upstream&at=2024-05-01T13%3A40%3A00.000Z" - ); + const date = encodeURIComponent(new Date().toISOString()); + const initialPage = `/objects/BuiltinTag?branch=atl1-delete-upstream&at=${date}`; + await page.goto(initialPage); + + await page.getByRole("link", { name: "Log in anonymous" }).click(); - await page.getByRole("link", { name: "Sign in" }).click(); - await expect(page.getByText("Sign in to your account")).toBeVisible(); + await expect(page.getByText("Log in to your account")).toBeVisible(); await page.getByLabel("Username").fill(ADMIN_CREDENTIALS.username); await page.getByLabel("Password").fill(ADMIN_CREDENTIALS.password); - await page.getByRole("button", { name: "Sign in" }).click(); + await page.getByRole("button", { name: "Log in" }).click(); - await expect(page.getByTestId("current-user-avatar-button")).toBeVisible(); - await expect(page.url()).toContain( - "/objects/BuiltinTag?branch=atl1-delete-upstream&at=2024-05-01T13%3A40%3A00.000Z" - ); + await expect(page.getByTestId("authenticated-menu-trigger")).toBeVisible(); + await expect(page.url()).toContain(initialPage); }); }); @@ -61,19 +62,19 @@ test.describe("/signin", () => { test("should log out the user", async ({ page }) => { await page.goto("/"); - await page.getByTestId("current-user-avatar-button").click(); - await page.getByRole("menuitem", { name: "Sign out" }).click(); + await page.getByTestId("authenticated-menu-trigger").click(); + await page.getByRole("menuitem", { name: "Logout" }).click(); - await expect(page.getByRole("link", { name: "Sign in" })).toBeVisible(); + await expect(page.getByRole("link", { name: "Log in anonymous" })).toBeVisible(); }); test("redirect to homepage if user is already logged in", async ({ page }) => { - await page.goto("/signin"); + await page.goto("/login"); await expect(page.getByText("Welcome to Infrahub!")).toBeVisible(); }); - test.fixme("should refresh access token and retry failed request", async ({ page }) => { + test("should refresh access token and retry failed request", async ({ page }) => { let blockRequest = true; // force 401 on first call await page.route("**/graphql/main**", async (route) => { @@ -101,14 +102,7 @@ test.describe("/signin", () => { } }); - const waitForResponse = page.waitForResponse((response) => { - const reqData = response.request().postDataJSON(); - const status = response.status(); - - return reqData?.operationName === "BuiltinTag" && status === 200; - }); - - await Promise.all([waitForResponse, page.goto("/objects/BuiltinTag")]); + await page.goto("/objects/BuiltinTag"); await expect(page.getByRole("cell", { name: "blue" })).toBeVisible(); }); diff --git a/frontend/app/tests/e2e/menu.spec.ts b/frontend/app/tests/e2e/menu.spec.ts deleted file mode 100644 index 25515c812a..0000000000 --- a/frontend/app/tests/e2e/menu.spec.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { expect, test } from "@playwright/test"; - -test.describe("Sidebar menu", () => { - test.beforeEach(async function ({ page }) { - page.on("response", async (response) => { - if (response.status() === 500) { - await expect(response.url()).toBe("This URL responded with a 500 status"); - } - }); - }); - - test("filter item in menu", async ({ page }) => { - await page.goto("/"); - - await test.step("all items are visible", async () => { - await expect(page.getByRole("button", { name: "BGP Session" })).toBeVisible(); - await expect(page.getByRole("link", { name: "All BGP Session(s)" })).toBeVisible(); - await expect(page.getByRole("link", { name: "BGP Peer Group" })).toBeVisible(); - await expect(page.getByRole("link", { name: "Circuit" })).toBeVisible(); - }); - - await test.step("filter with text 'inter'", async () => { - await page.getByTestId("search-menu").fill("inter"); - }); - - await test.step("only items who includes 'inter' are visible", async () => { - await expect(page.getByRole("button", { name: "Device" })).toBeVisible(); - await expect(page.getByRole("link", { name: "Interface", exact: true })).toBeVisible(); - await expect(page.getByRole("link", { name: "MLAG Interface", exact: true })).toBeVisible(); - }); - }); - - test("display groups and all items when group matches but no items matches", async ({ page }) => { - await page.goto("/"); - - await page.getByTestId("search-menu").fill("ipam"); - - await test.step("all items under Ipam group are visible", async () => { - await expect(page.getByRole("button", { name: "IPAM" })).toBeVisible(); - await expect(page.getByRole("link", { name: "Namespaces" })).toBeVisible(); - await expect(page.getByRole("link", { name: "Prefixes" })).toBeVisible(); - await expect(page.getByRole("link", { name: "IP Addresses" })).toBeVisible(); - }); - }); - - test("display no items nor groups when there is no match", async ({ page }) => { - await page.goto("/"); - await expect(page.getByRole("button", { name: "Objects" })).toBeVisible(); - - await page.getByTestId("search-menu").fill("no-item-found"); - - await expect(page.getByTestId("sidebar-menu").getByRole("link")).toBeHidden(); - }); -}); diff --git a/frontend/app/tests/e2e/objects/CoreGraphQLQuery/core-graphql-query.spec.ts b/frontend/app/tests/e2e/objects/CoreGraphQLQuery/core-graphql-query.spec.ts index 9b10bf3530..89153ae9cd 100644 --- a/frontend/app/tests/e2e/objects/CoreGraphQLQuery/core-graphql-query.spec.ts +++ b/frontend/app/tests/e2e/objects/CoreGraphQLQuery/core-graphql-query.spec.ts @@ -86,7 +86,10 @@ test.describe("/objects/CoreGraphQLQuery/:graphqlQueryId - GraphQL Query details await expect(page.getByText("Metadata updated")).toBeVisible(); await test.step("return to list using breadcrumb", async () => { - await page.getByRole("main").getByRole("link", { name: "GraphQL Query" }).click(); + await page + .getByTestId("breadcrumb-navigation") + .getByRole("link", { name: "GraphQL Query" }) + .click(); expect(page.url()).toContain("/objects/CoreGraphQLQuery"); }); }); diff --git a/frontend/app/tests/e2e/objects/artifact-definition.spec.ts b/frontend/app/tests/e2e/objects/artifact-definition.spec.ts index 51d64199e7..4e46d6c6e4 100644 --- a/frontend/app/tests/e2e/objects/artifact-definition.spec.ts +++ b/frontend/app/tests/e2e/objects/artifact-definition.spec.ts @@ -13,8 +13,9 @@ test.describe("/objects/CoreArtifactDefinition - Artifact Definition page", () = }); test("should generate artifacts successfully", async ({ page }) => { - await page.goto("/objects/CoreArtifactDefinition"), - await page.getByRole("link", { name: "startup-config" }).click(); + await page.goto("/objects/CoreArtifactDefinition"); + await page.getByRole("link", { name: "startup-config" }).click(); + await expect(page.getByRole("button", { name: "Generate" })).not.toBeDisabled(); await page.getByRole("button", { name: "Generate" }).click(); await expect(page.getByRole("alert")).toContainText("Artifacts generated"); }); diff --git a/frontend/app/tests/e2e/objects/artifact.spec.ts b/frontend/app/tests/e2e/objects/artifact.spec.ts index 9cb6f1baa7..497f891133 100644 --- a/frontend/app/tests/e2e/objects/artifact.spec.ts +++ b/frontend/app/tests/e2e/objects/artifact.spec.ts @@ -3,6 +3,7 @@ import { ACCOUNT_STATE_PATH } from "../../constants"; test.describe("/objects/CoreArtifact - Artifact page", () => { test.describe.configure({ mode: "serial" }); + test.use({ storageState: ACCOUNT_STATE_PATH.ADMIN }); test.beforeEach(async function ({ page }) { page.on("response", async (response) => { diff --git a/frontend/app/tests/e2e/objects/object-details.spec.ts b/frontend/app/tests/e2e/objects/object-details.spec.ts index 1ab185e5d7..fbd08decb4 100644 --- a/frontend/app/tests/e2e/objects/object-details.spec.ts +++ b/frontend/app/tests/e2e/objects/object-details.spec.ts @@ -60,9 +60,7 @@ test.describe("/objects/:objectKind/:objectid", () => { }); test("should display the select 2 steps correctly", async ({ page }) => { - await page.goto("/"); - await page.getByRole("link", { name: "All Device(s)" }).click(); - await expect(page.getByText("Just a moment")).not.toBeVisible(); + await page.goto("/objects/InfraDevice"); await page.getByRole("link", { name: "atl1-edge1" }).click(); await page.getByText("Interfaces15").click(); await page.getByRole("link", { name: "Backbone: Connected to jfk1-" }).click(); diff --git a/frontend/app/tests/e2e/objects/object-dropdown-creation.spec.ts b/frontend/app/tests/e2e/objects/object-dropdown-creation.spec.ts index b3e8f93057..caa94d9f89 100644 --- a/frontend/app/tests/e2e/objects/object-dropdown-creation.spec.ts +++ b/frontend/app/tests/e2e/objects/object-dropdown-creation.spec.ts @@ -13,20 +13,7 @@ test.describe("object dropdown creation", () => { }); test("should open the creation form and open the tag option creation form", async ({ page }) => { - // Go to home page - await page.goto("/"); - - await Promise.all([ - page.waitForResponse((response) => { - const reqData = response.request().postDataJSON(); - const status = response.status(); - - return reqData?.operationName === "InfraDevice" && status === 200; - }), - - // Open all devices - page.getByRole("link", { name: "All Device(s)" }).click(), - ]); + await page.goto("/objects/InfraDevice"); // Open creation form await page.getByTestId("create-object-button").click(); diff --git a/frontend/app/tests/e2e/objects/object-filters.spec.ts b/frontend/app/tests/e2e/objects/object-filters.spec.ts index 83bb96104c..fa3f37ec4d 100644 --- a/frontend/app/tests/e2e/objects/object-filters.spec.ts +++ b/frontend/app/tests/e2e/objects/object-filters.spec.ts @@ -1,6 +1,9 @@ import { expect, test } from "@playwright/test"; +import { ACCOUNT_STATE_PATH } from "../../constants"; test.describe("Object filters", () => { + test.use({ storageState: ACCOUNT_STATE_PATH.ADMIN }); + test.beforeEach(async function ({ page }) { page.on("response", async (response) => { if (response.status() === 500) { @@ -12,20 +15,16 @@ test.describe("Object filters", () => { test("should filter the objects list", async ({ page }) => { await test.step("access objects list and verify initial state", async () => { await page.goto("/objects/InfraDevice"); - await expect(page.getByRole("main")).toContainText("Filters: 0"); - await expect(page.getByRole("main")).toContainText("Showing 1 to 10 of 30 results"); + await expect(page.getByText("Just a moment")).not.toBeVisible(); + await expect(page.getByTestId("object-items")).toContainText("Filters: 0"); + await expect(page.getByTestId("object-items")).toContainText("Showing 1 to 10 of 30 results"); }); await test.step("start filtering objects", async () => { await test.step("select filters", async () => { await page.getByTestId("apply-filters").click(); - await page - .getByTestId("side-panel-container") - .getByText("Status") - .locator("../..") - .getByTestId("select-open-option-button") - .click(); - await page.getByRole("option", { name: "Provisioning In the process" }).click(); + await page.getByLabel("Role").click(); + await page.getByRole("option", { name: "Edge Router" }).click(); const tagsMultiSelectOpenButton = page .getByTestId("side-panel-container") @@ -46,13 +45,7 @@ test.describe("Object filters", () => { await test.step("verify filter initial value", async () => { await page.getByTestId("apply-filters").click(); - await expect( - page - .getByTestId("side-panel-container") - .getByText("Status") - .locator("../..") - .getByTestId("select-input") - ).toHaveValue("Provisioning"); + await expect(page.getByLabel("Role")).toHaveText("Edge Router"); }); await expect(page.locator("form")).toContainText("red"); @@ -62,14 +55,14 @@ test.describe("Object filters", () => { }); await test.step("verify new state", async () => { - await expect(page.getByRole("main")).toContainText("Filters: 2"); - await expect(page.getByRole("main")).toContainText("Showing 1 to 6 of 6 results"); + await expect(page.getByTestId("object-items")).toContainText("Filters: 2"); + await expect(page.getByTestId("object-items")).toContainText("Showing 1 to 10 of 10 results"); }); await test.step("remove filters and verify initial state", async () => { await page.getByTestId("remove-filters").click(); - await expect(page.getByRole("main")).toContainText("Filters: 0"); - await expect(page.getByRole("main")).toContainText("Showing 1 to 10 of 30 results"); + await expect(page.getByTestId("object-items")).toContainText("Filters: 0"); + await expect(page.getByTestId("object-items")).toContainText("Showing 1 to 10 of 30 results"); }); }); @@ -88,7 +81,9 @@ test.describe("Object filters", () => { await page.getByRole("option", { name: "atl1-core1" }).click(); await page.getByRole("button", { name: "Apply filters" }).click(); - await expect(page.getByRole("row", { name: "InfraInterfaceL3 Loopback0" })).toBeVisible(); + await expect( + page.getByRole("row", { name: "InfraInterfaceL3 atl1-core1 Loopback0" }) + ).toBeVisible(); await expect(page.getByRole("link", { name: "Connected to jfk1-edge2" })).toBeHidden(); }); @@ -99,39 +94,31 @@ test.describe("Object filters", () => { await page.getByTestId("apply-filters").click(); await expect(page.getByTestId("side-panel-container").getByText("Object")).toBeVisible(); - const kindSelector = page - .getByTestId("side-panel-container") - .getByText("Kind") - .locator("../..") - .getByTestId("select-open-option-button"); - await kindSelector.click(); + await page.getByLabel("kind").click(); await expect(page.getByRole("option", { name: "Tag Builtin", exact: true })).toBeVisible(); }); test("should correctly filter from a kind", async ({ page }) => { await page.goto("/objects/InfraInterface"); + await expect(page.getByText("Just a moment")).not.toBeVisible(); await page.getByTestId("apply-filters").click(); await test.step("profiles selector should not be visible", async () => { await expect(page.getByText("Select an object type")).not.toBeVisible(); }); - const kindSelector = page - .getByTestId("side-panel-container") - .getByText("Kind") - .locator("../.."); - await test.step("filter objects", async () => { - await kindSelector.getByTestId("select-open-option-button").click(); + await page.getByLabel("kind").click(); await page.getByRole("option", { name: "Interface L2 Infra", exact: true }).click(); await page.getByRole("button", { name: "Apply filters" }).click(); - await expect(page.getByRole("main")).toContainText("Showing 1 to 10 of 510 results"); + await expect(page.getByTestId("object-items")).toContainText( + "Showing 1 to 10 of 510 results" + ); }); await test.step("verify filter initial value", async () => { await page.getByTestId("apply-filters").click(); - await kindSelector.getByTestId("select-input"); - await expect(kindSelector.getByTestId("select-input")).toHaveValue("Interface L2"); + await expect(page.getByLabel("Kind")).toContainText("Interface L2 Infra"); }); }); }); diff --git a/frontend/app/tests/e2e/objects/object-hierarchical.spec.ts b/frontend/app/tests/e2e/objects/object-hierarchical.spec.ts index e7a482e59d..7ec0098ea6 100644 --- a/frontend/app/tests/e2e/objects/object-hierarchical.spec.ts +++ b/frontend/app/tests/e2e/objects/object-hierarchical.spec.ts @@ -1,6 +1,9 @@ import { expect, test } from "@playwright/test"; +import { ACCOUNT_STATE_PATH } from "../../constants"; test.describe("Object hierarchical view", () => { + test.use({ storageState: ACCOUNT_STATE_PATH.ADMIN }); + test("should display correctly", async ({ page }) => { await test.step("view tree and list for a hierarchical model", async () => { await page.goto("/objects/LocationGeneric"); diff --git a/frontend/app/tests/e2e/objects/object-list-search.spec.ts b/frontend/app/tests/e2e/objects/object-list-search.spec.ts index 4b1c767e19..289ef73a29 100644 --- a/frontend/app/tests/e2e/objects/object-list-search.spec.ts +++ b/frontend/app/tests/e2e/objects/object-list-search.spec.ts @@ -1,9 +1,12 @@ import { expect, test } from "@playwright/test"; +import { ACCOUNT_STATE_PATH } from "../../constants"; const OBJECT_NAME = "atl1-core1"; const SEARCH = "atl"; test.describe("Object list search", async () => { + test.use({ storageState: ACCOUNT_STATE_PATH.ADMIN }); + test.beforeEach(async function ({ page }) { page.on("response", async (response) => { if (response.status() === 500) { @@ -17,13 +20,13 @@ test.describe("Object list search", async () => { await test.step("should access object list and verify the total amount of results", async () => { await expect(page.locator("tbody")).toContainText(OBJECT_NAME); - await expect(page.getByRole("main")).toContainText("Showing 1 to 10 of 30 results"); + await expect(page.getByTestId("object-items")).toContainText("Showing 1 to 10 of 30 results"); }); await test.step("should search an object and verify the total amount of results", async () => { await page.getByTestId("object-list-search-bar").fill(SEARCH); await expect(page.locator("tbody")).toContainText(OBJECT_NAME); - await expect(page.getByRole("main")).toContainText("Showing 1 to 6 of 6 results"); + await expect(page.getByTestId("object-items")).toContainText("Showing 1 to 6 of 6 results"); }); }); }); diff --git a/frontend/app/tests/e2e/objects/object-list.spec.ts b/frontend/app/tests/e2e/objects/object-list.spec.ts index cab72ee9ca..ed715e85d7 100644 --- a/frontend/app/tests/e2e/objects/object-list.spec.ts +++ b/frontend/app/tests/e2e/objects/object-list.spec.ts @@ -10,28 +10,6 @@ test.describe("/objects/:objectKind", () => { }); }); - test("should display 'kind' column on when the object is a generic", async ({ page }) => { - await page.goto("/objects/CoreGroup"); - await expect(page.locator("thead")).toContainText("Kind"); - }); - - test("should display default column when a relationship schema has no attributes/relationship", async ({ - page, - }) => { - await page.goto("/objects/CoreStandardGroup"); - await page.getByTestId("object-items").getByRole("link", { name: "arista_devices" }).click(); - await page.getByText("Members").click(); - await expect(page.getByRole("columnheader", { name: "Type" })).toBeVisible(); - await expect(page.getByRole("columnheader", { name: "Name" })).toBeVisible(); - }); - - test("clicking on a relationship value redirects to its details page", async ({ page }) => { - await page.goto("/objects/InfraDevice"); - await page.getByRole("link", { name: "Juniper JunOS" }).first().click(); - await expect(page.getByText("NameJuniper JunOS")).toBeVisible(); - expect(page.url()).toContain("/objects/InfraPlatform/"); - }); - test.describe("when not logged in", () => { test("should not be able to create a new object", async ({ page }) => { await page.goto("/objects/BuiltinTag"); @@ -55,7 +33,7 @@ test.describe("/objects/:objectKind", () => { // then const newTab = await newTabPromise; - await newTab.waitForURL(linkHref); + await newTab.waitForURL(linkHref!); expect(newTab.url()).toContain(linkHref); }); }); @@ -63,6 +41,28 @@ test.describe("/objects/:objectKind", () => { test.describe("when logged in as Admin", () => { test.use({ storageState: ACCOUNT_STATE_PATH.ADMIN }); + test("should display 'kind' column on when the object is a generic", async ({ page }) => { + await page.goto("/objects/CoreGroup"); + await expect(page.locator("thead")).toContainText("Kind"); + }); + + test("should display default column when a relationship schema has no attributes/relationship", async ({ + page, + }) => { + await page.goto("/objects/CoreStandardGroup"); + await page.getByTestId("object-items").getByRole("link", { name: "arista_devices" }).click(); + await page.getByText("Members").click(); + await expect(page.getByRole("columnheader", { name: "Type" })).toBeVisible(); + await expect(page.getByRole("columnheader", { name: "Name" })).toBeVisible(); + }); + + test("clicking on a relationship value redirects to its details page", async ({ page }) => { + await page.goto("/objects/InfraDevice"); + await page.getByRole("link", { name: "Juniper JunOS" }).first().click(); + await expect(page.getByText("NameJuniper JunOS")).toBeVisible(); + expect(page.url()).toContain("/objects/InfraPlatform/"); + }); + test("should be able to create a new object", async ({ page }) => { await page.goto("/objects/BuiltinTag"); diff --git a/frontend/app/tests/e2e/objects/object-metadata.spec.ts b/frontend/app/tests/e2e/objects/object-metadata.spec.ts index f127256dbc..3c8385bdcf 100644 --- a/frontend/app/tests/e2e/objects/object-metadata.spec.ts +++ b/frontend/app/tests/e2e/objects/object-metadata.spec.ts @@ -13,10 +13,7 @@ test.describe("Object metadata", () => { }); test("should contain initial values and update them", async ({ page }) => { - await page.goto("/"); - - // Access all devices - await page.getByRole("link", { name: "All Device(s)" }).click(); + await page.goto("/objects/InfraDevice"); // Access device details await page.getByRole("link", { name: "atl1-core2" }).click(); @@ -41,8 +38,8 @@ test.describe("Object metadata", () => { // Select Architecture team await page.getByText("Owner Kind ?").getByLabel("Kind").first().click(); - await page.getByRole("option", { name: "Account" }).click(); - await page.getByText("Owner Kind ?").getByLabel("Account").click(); + await page.getByRole("option", { name: "Account group" }).click(); + await page.getByText("Owner Kind ?").getByLabel("Account group").click(); await page.getByRole("option", { name: "Architecture Team" }).click(); // Save @@ -51,11 +48,8 @@ test.describe("Object metadata", () => { // Verify the alert await expect(page.getByText("Metadata updated")).toBeVisible(); - // Verify metadata updates - await page.goto("/"); - // Access all devices - await page.getByRole("link", { name: "All Device(s)" }).click(); + await page.goto("/objects/InfraDevice"); // Access device details await page.getByRole("link", { name: "atl1-core2" }).click(); @@ -67,7 +61,7 @@ test.describe("Object metadata", () => { await metadataTooltipUpdated.getByTestId("edit-metadata-button").click(); // Source should be Account + Pop-Builder - await expect(page.getByTestId("select-input").nth(0)).toHaveValue("Account"); + await expect(page.getByTestId("select-input").nth(0)).toHaveValue("Account group"); await expect(page.getByTestId("select-input").nth(1)).toHaveValue("Architecture Team"); // Is protected should be checked diff --git a/frontend/app/tests/e2e/objects/object-relationships.spec.ts b/frontend/app/tests/e2e/objects/object-relationships.spec.ts index 8e496431f0..456901b189 100644 --- a/frontend/app/tests/e2e/objects/object-relationships.spec.ts +++ b/frontend/app/tests/e2e/objects/object-relationships.spec.ts @@ -26,7 +26,7 @@ test.describe("/objects/:objectKind/:objectid - relationship tab", () => { await expect(page.getByTestId("manage-groups")).toBeDisabled(); await expect(page.getByTestId("delete-button")).toBeDisabled(); - await page.getByText("Devices5").click(); + await page.getByText("Devices10").click(); await expect(page.getByTestId("open-relationship-form-button")).toBeDisabled(); }); }); @@ -40,24 +40,24 @@ test.describe("/objects/:objectKind/:objectid - relationship tab", () => { await test.step("Navigate to relationship tab of an object", async () => { await page.goto("/objects/InfraPlatform"); await page.getByRole("link", { name: "Cisco IOS", exact: true }).click(); - await page.getByText("Devices5").click(); + await page.getByText("Devices10").click(); }); await test.step("Delete the relationship", async () => { await page - .getByRole("row", { name: "ord1-edge2" }) + .getByRole("row", { name: "ord1-leaf2" }) .getByTestId("relationship-delete-button") .click(); await expect(page.getByRole("paragraph")).toContainText( - "Are you sure you want to remove the association between `Cisco IOS` and `ord1-edge2`? The `InfraDevice` `ord1-edge2` won't be deleted in the process." + "Are you sure you want to remove the association between `Cisco IOS` and `ord1-leaf2`? The `InfraDevice` `ord1-leaf2` won't be deleted in the process." ); await page.getByTestId("modal-delete-confirm").click(); }); await test.step("Verify deletion of relationship", async () => { await expect(page.getByRole("alert")).toContainText("Item removed from the group"); - await expect(page.getByRole("main")).toContainText("Showing 1 to 4 of 4 results"); - await expect(page.getByLabel("Tabs")).toContainText("Devices4"); + await expect(page.getByText("Showing 1 to 9 of 9 results")).toBeVisible(); + await expect(page.getByLabel("Tabs")).toContainText("Devices9"); }); }); @@ -65,21 +65,22 @@ test.describe("/objects/:objectKind/:objectid - relationship tab", () => { await test.step("Navigate to relationship tab of an object", async () => { await page.goto("/objects/InfraPlatform"); await page.getByRole("link", { name: "Cisco IOS", exact: true }).click(); - await page.getByText("Devices4").click(); + await page.getByText("Devices9").click(); + await expect(page.getByText("Just a moment")).not.toBeVisible(); }); await test.step("Add a new relationship", async () => { await page.getByTestId("open-relationship-form-button").click(); await page.getByTestId("side-panel-container").getByLabel("Devices").click(); - await page.getByRole("option", { name: "ord1-edge2" }).click(); + await page.getByRole("option", { name: "ord1-leaf2" }).click(); await page.getByRole("button", { name: "Save" }).click(); }); await test.step("Verify new relationship addition", async () => { await expect(page.getByRole("alert")).toContainText("Association with InfraDevice added"); - await expect(page.getByRole("main")).toContainText("Showing 1 to 5 of 5 results"); - await expect(page.getByLabel("Tabs")).toContainText("Devices5"); - await expect(page.getByRole("cell", { name: "ord1-edge2" })).toBeVisible(); + await expect(page.getByText("Showing 1 to 10 of 10 results")).toBeVisible(); + await expect(page.getByLabel("Tabs")).toContainText("Devices10"); + await expect(page.getByRole("cell", { name: "ord1-leaf2" })).toBeVisible(); }); }); @@ -106,7 +107,7 @@ test.describe("/objects/:objectKind/:objectid - relationship tab", () => { test("should access to the pool selector on relationships add", async ({ page }) => { await page.goto("/objects/InfraInterfaceL3/"); - await page.getByRole("link", { name: "Connected to den1-edge1" }).click(); + await page.getByRole("link", { name: "Connected to den1-edge1::Ethernet1" }).click(); await page.getByText("Ip Addresses1").click(); await page.getByTestId("open-relationship-form-button").click(); await expect(page.getByTestId("select-open-pool-option-button")).toBeVisible(); @@ -122,7 +123,7 @@ test.describe("/objects/:objectKind/:objectid - relationship tab", () => { await test.step("Navigate to relationship tab of an object", async () => { await page.goto("/objects/InfraPlatform"); await page.getByRole("link", { name: "Cisco IOS", exact: true }).click(); - await page.getByText("Devices5").click(); + await page.getByText("Devices10").click(); }); await page.getByRole("link", { name: "atl1", exact: true }).first().click(); await expect(page.getByText("Nameatl1")).toBeVisible(); diff --git a/frontend/app/tests/e2e/objects/object-update.spec.ts b/frontend/app/tests/e2e/objects/object-update.spec.ts index 4373da528c..78ba318c73 100644 --- a/frontend/app/tests/e2e/objects/object-update.spec.ts +++ b/frontend/app/tests/e2e/objects/object-update.spec.ts @@ -27,7 +27,7 @@ test.describe("Object update", () => { await page.getByLabel("Description").fill("New description"); await page.getByTestId("side-panel-container").getByLabel("Status").click(); - await page.getByRole("option", { name: "Active" }).click(); + await page.getByRole("option", { name: "Maintenance" }).click(); await page.getByTestId("side-panel-container").getByLabel("Role").click(); await page.getByRole("option", { name: "Edge Router" }).click(); @@ -56,7 +56,7 @@ test.describe("Object update", () => { await expect(page.getByText("Nameatl1-core1-new-name")).toBeVisible(); await expect(page.getByText("New description")).toBeVisible(); await expect(page.getByRole("link", { name: "AS701 701" })).toBeVisible(); - await expect(page.getByText("Active")).toBeVisible(); + await expect(page.getByText("Maintenance")).toBeVisible(); await expect(page.getByText("Edge Router")).toBeVisible(); await expect(page.getByRole("link", { name: "green" })).toBeVisible(); await expect(page.getByRole("link", { name: "red", exact: true })).toBeVisible(); @@ -67,20 +67,8 @@ test.describe("Object update", () => { await expect(page.getByLabel("Name *")).toHaveValue("atl1-core1-new-name"); await expect(page.getByLabel("Description")).toHaveValue("New description"); await expect(page.getByLabel("Type *")).toHaveValue("MX204"); - await expect( - page - .getByTestId("side-panel-container") - .getByLabel("Status") - .locator("../..") - .locator("input") - ).toHaveValue("Active"); - await expect( - page - .getByTestId("side-panel-container") - .getByLabel("Role") - .locator("../..") - .locator("input") - ).toHaveValue("Edge Router"); + await expect(page.getByLabel("Status")).toHaveText("Maintenance"); + await expect(page.getByLabel("Role")).toHaveText("Edge Router"); await expect( page.getByTestId("side-panel-container").getByLabel("Asn").locator("../..").locator("input") ).toHaveValue("AS701 701"); @@ -99,7 +87,7 @@ test.describe("Object update", () => { await test.step("assert initial object values", async () => { await expect(page.getByText("Nameatl1-leaf1")).toBeVisible(); - await expect(page.getByText("StatusActive")).toBeVisible(); + // await expect(page.getByText("StatusActive")).toBeVisible(); await expect(page.getByText("RoleLeaf Switch")).toBeVisible(); await expect(page.getByText("AsnAS64496 64496")).toBeVisible(); }); @@ -108,10 +96,10 @@ test.describe("Object update", () => { await page.getByTestId("edit-button").click(); await page.getByTestId("side-panel-container").getByLabel("Status").click(); - await page.getByRole("option", { name: "Empty", exact: true }).click(); + await page.getByRole("option", { name: "Active" }).click(); await page.getByTestId("side-panel-container").getByLabel("Role").click(); - await page.getByRole("option", { name: "Empty", exact: true }).click(); + await page.getByRole("option", { name: "Leaf Switch" }).click(); await page.getByTestId("side-panel-container").getByLabel("Asn").click(); await page.getByRole("option", { name: "Empty", exact: true }).click(); diff --git a/frontend/app/tests/e2e/objects/profiles/profiles.spec.ts b/frontend/app/tests/e2e/objects/profiles/profiles.spec.ts index ae29a99534..8b19cef56c 100644 --- a/frontend/app/tests/e2e/objects/profiles/profiles.spec.ts +++ b/frontend/app/tests/e2e/objects/profiles/profiles.spec.ts @@ -51,7 +51,10 @@ test.describe("/objects/CoreProfile - Profiles page", () => { await expect(page.getByText("DescriptionA profile for E2E")).toBeVisible(); await test.step("return to profiles list using breadcrumb", async () => { - await page.getByTestId("object-header").getByRole("link", { name: "All Profiles" }).click(); + await page + .getByTestId("breadcrumb-navigation") + .getByRole("link", { name: "Profile", exact: true }) + .click(); expect(page.url()).toContain("/objects/CoreProfile"); }); }); @@ -194,31 +197,26 @@ test.describe("/objects/CoreProfile - Profile for Interface L2 and fields verifi await page.getByTestId("create-object-button").click(); await page.getByLabel("Select an object type").click(); - await page - .locator("div") - .filter({ hasText: /^Clear$/ }) - .getByRole("combobox") - .fill("l2"); await page.getByRole("option", { name: "Interface L2 Infra", exact: true }).click(); }); await test.step("verify Interface L2 optional attributes are all visible", async () => { - await expect(page.getByText("Profile Name *")).toBeVisible(); - await expect(page.getByText("Description")).toBeVisible(); - await expect(page.getByText("MTU")).toBeVisible(); - await expect(page.getByText("Enabled")).toBeVisible(); - await expect(page.getByText("Status")).toBeVisible(); - await expect(page.getByText("Role")).toBeVisible(); + await expect(page.getByLabel("Profile Name *")).toBeVisible(); + await expect(page.getByLabel("Description")).toBeVisible(); + await expect(page.getByLabel("MTU")).toBeVisible(); + await expect(page.getByLabel("Enabled")).toBeVisible(); + await expect(page.getByLabel("Status")).toBeVisible(); + await expect(page.getByLabel("Role")).toBeVisible(); }); await test.step("verify Interface L2 mandatory attributes and relationships are not visible", async () => { - await expect(page.getByText("Layer2 Mode *")).not.toBeVisible(); - await expect(page.getByText("Speed *")).not.toBeVisible(); - await expect(page.getByText("Untagged VLAN")).not.toBeVisible(); + await expect(page.getByLabel("Layer2 Mode *")).not.toBeVisible(); + await expect(page.getByLabel("Speed *")).not.toBeVisible(); + await expect(page.getByLabel("Untagged VLAN")).not.toBeVisible(); await expect( page.getByTestId("side-panel-container").getByText("Tagged VLANs") ).not.toBeVisible(); - await expect(page.getByText("Device *")).not.toBeVisible(); + await expect(page.getByLabel("Device *")).not.toBeVisible(); }); }); @@ -235,11 +233,7 @@ test.describe("/objects/CoreProfile - Profile for Interface L2 and fields verifi await page.getByLabel("Profile Priority").fill("2000"); await page.getByLabel("MTU").fill("256"); await page.getByLabel("Enabled").check(); - await page - .locator("div:below(:text('Status'))") - .first() - .getByTestId("select-open-option-button") - .click(); + await page.getByLabel("Status").click(); await page.getByText("Provisioning").click(); await page.getByRole("button", { name: "Save" }).click(); await expect(page.getByText("InfraInterfaceL2 created")).toBeVisible(); @@ -257,11 +251,7 @@ test.describe("/objects/CoreProfile - Profile for Interface L2 and fields verifi await test.step("fill and submit form", async () => { await page.getByLabel("Profile Name *").fill(GENERIC_PROFILE_NAME); await page.getByLabel("Profile Priority").fill("2000"); - await page - .locator("div:below(:text('Status'))") - .first() - .getByTestId("select-open-option-button") - .click(); + await page.getByLabel("Status").click(); await page.getByText("Maintenance", { exact: true }).click(); await page.getByRole("button", { name: "Save" }).click(); await expect(page.getByText("InfraInterface created")).toBeVisible(); diff --git a/frontend/app/tests/e2e/permissions/accounts-object.spec.ts b/frontend/app/tests/e2e/permissions/accounts-object.spec.ts deleted file mode 100644 index 63bf86db96..0000000000 --- a/frontend/app/tests/e2e/permissions/accounts-object.spec.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { test, expect } from "@playwright/test"; -import { ACCOUNT_STATE_PATH } from "../../constants"; - -test.describe("/objects/CoreGenericAccount - Admin permissions", () => { - test.use({ storageState: ACCOUNT_STATE_PATH.ADMIN }); - - test("should be allowed to add accounts", async ({ page }) => { - await page.goto("/objects/CoreGenericAccount"); - await expect(page.getByTestId("create-object-button")).toBeEnabled(); - }); -}); - -test.describe("/objects/CoreGenericAccount - Read write permissions", () => { - test.use({ storageState: ACCOUNT_STATE_PATH.READ_WRITE }); - test("should not be allowed to add accounts", async ({ page }) => { - await page.goto("/objects/CoreGenericAccount"); - await expect(page.getByTestId("create-object-button")).not.toBeEnabled(); - }); -}); - -test.describe("/objects/CoreGenericAccount - Read only permissions", () => { - test.use({ storageState: ACCOUNT_STATE_PATH.READ_ONLY }); - test("should not be allowed to add accounts", async ({ page }) => { - await page.goto("/objects/CoreGenericAccount"); - await expect(page.getByTestId("create-object-button")).not.toBeEnabled(); - }); -}); diff --git a/frontend/app/tests/e2e/permissions/role-management.spec.ts b/frontend/app/tests/e2e/permissions/role-management.spec.ts new file mode 100644 index 0000000000..dbfcf939a1 --- /dev/null +++ b/frontend/app/tests/e2e/permissions/role-management.spec.ts @@ -0,0 +1,11 @@ +import { expect, test } from "@playwright/test"; +import { ACCOUNT_STATE_PATH } from "../../constants"; + +test.describe("Users & Permissions - Admin", () => { + test.use({ storageState: ACCOUNT_STATE_PATH.ADMIN }); + + test("should be allowed to add accounts", async ({ page }) => { + await page.goto("/role-management"); + await expect(page.getByTestId("create-object-button")).toBeEnabled(); + }); +}); diff --git a/frontend/app/tests/e2e/profile/profile.spec.ts b/frontend/app/tests/e2e/profile/profile.spec.ts index 08ece5ee0d..26be89955a 100644 --- a/frontend/app/tests/e2e/profile/profile.spec.ts +++ b/frontend/app/tests/e2e/profile/profile.spec.ts @@ -11,11 +11,11 @@ test.describe("/profile", () => { }); test.describe("when not logged in", () => { - test("should see 'Sign in' and no user avatar on header", async ({ page }) => { + test("should see 'Login' and no user avatar on header", async ({ page }) => { await page.goto("/"); - await expect(page.getByRole("link", { name: "Sign in" })).toBeVisible(); - await expect(page.getByTestId("current-user-avatar-button")).toBeHidden(); + await expect(page.getByTestId("unauthenticated-menu-trigger")).toBeVisible(); + await expect(page.getByTestId("authenticated-menu-trigger")).toBeHidden(); }); }); @@ -25,8 +25,8 @@ test.describe("/profile", () => { test("should access the profile page", async ({ page }) => { await test.step("go to profile page", async () => { await page.goto("/"); - await page.getByTestId("current-user-avatar-button").click(); - await page.getByRole("menuitem", { name: "Your Profile" }).click(); + await page.getByTestId("authenticated-menu-trigger").click(); + await page.getByRole("menuitem", { name: "Account settings" }).click(); }); await test.step("display account details", async () => { @@ -44,15 +44,15 @@ test.describe("/profile", () => { test("should access the profile page", async ({ page }) => { await test.step("go to profile page", async () => { await page.goto("/"); - await page.getByTestId("current-user-avatar-button").click(); - await page.getByRole("menuitem", { name: "Your Profile" }).click(); + await page.getByTestId("authenticated-menu-trigger").click(); + await page.getByRole("menuitem", { name: "Account settings" }).click(); }); await test.step("display account details", async () => { await expect( page.getByRole("heading", { name: "Chloe O'Brian", exact: true }) ).toBeVisible(); - await expect(page.getByText("NameChloe O'Brian")).toBeVisible(); + await expect(page.getByText("LabelChloe O'Brian")).toBeVisible(); await expect(page.getByText("Roleread-write")).toBeVisible(); }); }); @@ -64,13 +64,13 @@ test.describe("/profile", () => { test("should access the profile page", async ({ page }) => { await test.step("go to profile page", async () => { await page.goto("/"); - await page.getByTestId("current-user-avatar-button").click(); - await page.getByRole("menuitem", { name: "Your Profile" }).click(); + await page.getByTestId("authenticated-menu-trigger").click(); + await page.getByRole("menuitem", { name: "Account settings" }).click(); }); await test.step("display account details", async () => { await expect(page.getByRole("heading", { name: "Jack Bauer", exact: true })).toBeVisible(); - await expect(page.getByText("NameJack Bauer")).toBeVisible(); + await expect(page.getByText("LabelJack Bauer")).toBeVisible(); await expect(page.getByText("Roleread-only")).toBeVisible(); }); }); diff --git a/frontend/app/tests/e2e/profile/tokens.spec.ts b/frontend/app/tests/e2e/profile/tokens.spec.ts index 7f0ba3ddd9..179fc1187b 100644 --- a/frontend/app/tests/e2e/profile/tokens.spec.ts +++ b/frontend/app/tests/e2e/profile/tokens.spec.ts @@ -12,8 +12,6 @@ test.describe("/profile?tab=tokens", () => { test.describe("when not logged in as admin account", () => { test("should not access profile tokens", async ({ page }) => { - await page.goto("/"); - await expect(page.getByText("Just a moment")).not.toBeVisible(); await page.goto("/profile?tab=tokens"); await expect(page.getByText("Welcome to Infrahub")).toBeVisible(); }); @@ -25,12 +23,10 @@ test.describe("/profile?tab=tokens", () => { test("should access and manage profile tokens", async ({ page }) => { await test.step("go to profile page and access tokens", async () => { await page.goto("/"); - await page.getByTestId("current-user-avatar-button").click(); - await page.getByRole("menuitem", { name: "Your Profile" }).click(); + await page.getByTestId("authenticated-menu-trigger").click(); + await page.getByRole("menuitem", { name: "Account settings" }).click(); await page.getByText("Tokens").click(); - await expect(page.getByRole("heading", { name: "Tokens" })).toBeVisible(); await expect(page.getByTestId("create-object-button")).toBeVisible(); - await expect(page.getByText("Just a moment")).not.toBeVisible(); }); await test.step("create a new token", async () => { @@ -56,7 +52,7 @@ test.describe("/profile?tab=tokens", () => { await expect(page.getByText("Are you sure you want to")).toBeVisible(); await page.getByTestId("modal-delete-confirm").click(); await expect(page.getByText("Are you sure you want to")).not.toBeVisible(); - await expect(page.getByText("test token")).not.toBeVisible(); + await expect(page.getByRole("cell", { name: "test token" })).not.toBeVisible(); }); }); }); diff --git a/frontend/app/tests/e2e/proposed-changes/proposed-changes.spec.ts b/frontend/app/tests/e2e/proposed-changes/proposed-changes.spec.ts index 625c8dfdf1..d9c02e4bc3 100644 --- a/frontend/app/tests/e2e/proposed-changes/proposed-changes.spec.ts +++ b/frontend/app/tests/e2e/proposed-changes/proposed-changes.spec.ts @@ -15,7 +15,7 @@ test.describe("/proposed-changes", () => { test("should not be able to create a proposed changes", async ({ page }) => { await page.goto("/proposed-changes"); - await expect(page.locator("header").getByText("Proposed changes")).toBeVisible(); + await expect(page.getByRole("heading", { name: "Proposed changes" })).toBeVisible(); await expect(page.getByTestId("add-proposed-changes-button")).toBeDisabled(); }); }); @@ -26,16 +26,16 @@ test.describe("/proposed-changes", () => { test("allow to create a proposed change", async ({ page }) => { await page.goto("/proposed-changes"); - await expect(page.locator("header").getByText("Proposed changes")).toBeVisible(); + await expect(page.getByRole("heading", { name: "Proposed changes" })).toBeVisible(); await expect(page.getByTestId("add-proposed-changes-button")).toBeEnabled(); await page.getByTestId("add-proposed-changes-button").click(); - await expect(page.getByRole("main")).toContainText("Create a proposed change"); + await expect(page.getByRole("heading", { name: "Create a proposed change" })).toBeVisible(); }); test("display validation errors when form is submitted with wrong value", async ({ page }) => { await page.goto("/proposed-changes/new"); - await expect(page.getByRole("main")).toContainText("Create a proposed change"); + await expect(page.getByRole("heading", { name: "Create a proposed change" })).toBeVisible(); await page.getByRole("button", { name: "Create proposed change" }).click(); await expect(page.getByLabel("Name *").locator("..")).toContainText("Required"); await expect(page.getByText("Source Branch *").locator("..")).toContainText("Required"); @@ -71,8 +71,8 @@ test.describe("/proposed-changes", () => { await page.getByLabel("Name *").fill(pcName); await page.getByTestId("codemirror-editor").getByRole("textbox").fill("My description"); await page.getByTestId("select-open-option-button").click(); - await page.getByRole("option", { name: "Architecture Team" }).click(); - await page.getByRole("option", { name: "Crm Synchronization" }).click(); + await page.getByRole("option", { name: "Olivia Carter" }).click(); + await page.getByRole("option", { name: "CRM Synchronization" }).click(); await page.getByTestId("select-open-option-button").click(); await page.getByRole("button", { name: "Create proposed change" }).click(); @@ -101,9 +101,9 @@ test.describe("/proposed-changes", () => { await page.getByRole("button", { name: "Save" }).click(); await expect(page.getByText("ProposedChange updated")).toBeVisible(); - await expect(page.locator("header").getByText(pcNameEdit)).toBeVisible(); + await expect(page.getByRole("heading", { name: pcNameEdit, exact: true })).toBeVisible(); await expect(page.getByTestId("pc-description")).toContainText("My description edit"); - await expect(page.getByText("ReviewersAT")).toBeVisible(); + await expect(page.getByText("ReviewersOC")).toBeVisible(); }); }); @@ -135,7 +135,9 @@ test.describe("/proposed-changes", () => { }); await test.step("not able to edit proposed change", async () => { + await expect(page.getByRole("button", { name: "Approve" })).toBeDisabled(); await expect(page.getByRole("button", { name: "Merge" })).toBeDisabled(); + await expect(page.getByRole("button", { name: "Close" })).toBeDisabled(); await expect(page.getByTestId("edit-button")).toBeDisabled(); }); }); @@ -145,8 +147,9 @@ test.describe("/proposed-changes", () => { await page .getByRole("link", { name: `${pcNameEdit} 0 ${pcBranchName}` }) .locator("../..") - .getByTestId("delete-row-button") + .getByTestId("actions-row-button") .click(); + await page.getByTestId("delete-row-button").click(); await expect(page.getByTestId("modal-delete")).toBeVisible(); await page.getByTestId("modal-delete-confirm").click(); await expect(page.getByText(`Proposed changes '${pcNameEdit}' deleted`)).toBeVisible(); diff --git a/frontend/app/tests/e2e/proposed-changes/proposed-changes_checks.spec.ts b/frontend/app/tests/e2e/proposed-changes/proposed-changes_checks.spec.ts index 6514c38032..87d2097c3a 100644 --- a/frontend/app/tests/e2e/proposed-changes/proposed-changes_checks.spec.ts +++ b/frontend/app/tests/e2e/proposed-changes/proposed-changes_checks.spec.ts @@ -18,7 +18,7 @@ test.describe("/proposed-changes checks", () => { await test.step("create a new proposed change", async () => { await page.getByTestId("add-proposed-changes-button").click(); - await expect(page.getByText("Create Proposed Change")).toBeVisible(); + await expect(page.getByRole("heading", { name: "Create a proposed change" })).toBeVisible(); await page.getByLabel("Name *").fill("pc-checks"); await page.getByLabel("Source Branch *").click(); await page.getByRole("option", { name: "atl1-delete-upstream" }).click(); @@ -46,8 +46,9 @@ test.describe("/proposed-changes checks", () => { .getByRole("link", { name: "pc-checks 0 atl1-delete-" }) .first() .locator("../..") - .getByTestId("delete-row-button") + .getByTestId("actions-row-button") .click(); + await page.getByTestId("delete-row-button").click(); await expect(page.getByTestId("modal-delete")).toBeVisible(); await page.getByTestId("modal-delete-confirm").click(); await expect(page.getByText("Proposed changes 'pc-checks' deleted")).toBeVisible(); diff --git a/frontend/app/tests/e2e/proposed-changes/proposed-changes_diff.spec.ts b/frontend/app/tests/e2e/proposed-changes/proposed-changes_diff.spec.ts index ee4a14c6fa..fb0d732663 100644 --- a/frontend/app/tests/e2e/proposed-changes/proposed-changes_diff.spec.ts +++ b/frontend/app/tests/e2e/proposed-changes/proposed-changes_diff.spec.ts @@ -1,4 +1,4 @@ -import { test, expect } from "@playwright/test"; +import { expect, test } from "@playwright/test"; import { ACCOUNT_STATE_PATH } from "../../constants"; test.describe("/proposed-changes diff data", () => { @@ -24,7 +24,7 @@ test.describe("/proposed-changes diff data", () => { await page.getByLabel("Name *").fill("conflict-test"); await page.getByTestId("select-open-option-button").click(); await page.getByRole("option", { name: "Admin" }).click(); - await page.getByRole("main").click(); + await page.getByTestId("select-open-option-button").click(); await page.getByRole("button", { name: "Create proposed change" }).click(); await expect(page.getByText("Proposed change created")).toBeVisible(); await page.getByText("Data").click(); @@ -104,12 +104,12 @@ test.describe("/proposed-changes diff data", () => { .click(); await page.getByRole("textbox").fill("test"); await page.getByRole("button", { name: "Comment", exact: true }).click(); - await expect(page.getByText("AAdminless than a minute ago")).toBeVisible(); + // await expect(page.getByText("AAdminless than a minute ago")).toBeVisible(); await page.getByTestId("comment").getByText("test").click(); await page.getByRole("button", { name: "Reply" }).click(); await page.getByRole("textbox").fill("test 2"); await page.getByRole("button", { name: "Comment", exact: true }).click(); - await expect(page.getByText("AAdminless than a minute agotest")).toBeVisible(); + // await expect(page.getByText("AAdminless than a minute agotest")).toBeVisible(); await expect(page.getByLabel("Resolve thread")).not.toBeChecked(); await page.getByLabel("Resolve thread").click(); await page.getByRole("button", { name: "Confirm", exact: true }).click(); @@ -123,8 +123,9 @@ test.describe("/proposed-changes diff data", () => { .getByRole("link", { name: "conflict-test" }) .first() .locator("../..") - .getByTestId("delete-row-button") + .getByTestId("actions-row-button") .click(); + await page.getByTestId("delete-row-button").click(); await expect(page.getByTestId("modal-delete")).toBeVisible(); await page.getByTestId("modal-delete-confirm").click(); await expect(page.getByText("Proposed changes 'conflict-test' deleted")).toBeVisible(); diff --git a/frontend/app/tests/e2e/resource-manager/number-pool.spec.ts b/frontend/app/tests/e2e/resource-manager/number-pool.spec.ts index cb66185ca5..146e6782d6 100644 --- a/frontend/app/tests/e2e/resource-manager/number-pool.spec.ts +++ b/frontend/app/tests/e2e/resource-manager/number-pool.spec.ts @@ -9,12 +9,12 @@ test.describe("/resource-manager - Resource Manager", () => { await page.goto("/resource-manager"); await page.getByTestId("create-object-button").click(); await page.getByLabel("Select an object type").click(); - await page.getByText("Number PoolCore").click(); + await page.getByRole("option", { name: "Number Pool Core" }).click(); await expect(page.getByText("Name *")).toBeVisible(); await page.getByLabel("Name *").fill("number pool test"); await page.getByLabel("Node *").click(); - await page.getByText("Interface L2", { exact: true }).click(); - await page.getByLabel("Attribute *").click(); + await page.getByRole("option", { name: "Interface L2 Infra", exact: true }).click(); + await page.getByText("Number Attribute *").click(); await page.getByRole("option", { name: "Speed" }).click(); await page.getByLabel("Start range *").fill("1"); await page.getByLabel("End range *").fill("10"); diff --git a/frontend/app/tests/e2e/resource-manager/resource-pool.spec.ts b/frontend/app/tests/e2e/resource-manager/resource-pool.spec.ts index b34638fb41..a52e83ace8 100644 --- a/frontend/app/tests/e2e/resource-manager/resource-pool.spec.ts +++ b/frontend/app/tests/e2e/resource-manager/resource-pool.spec.ts @@ -56,7 +56,6 @@ test.describe("/resource-manager - Resource Manager", () => { test("delete a pool", async ({ page }) => { await page.goto("/resource-manager"); - await page .getByRole("row", { name: "test prefix pool" }) .getByTestId("delete-row-button") diff --git a/frontend/app/tests/e2e/role-management/read.spec.ts b/frontend/app/tests/e2e/role-management/read.spec.ts new file mode 100644 index 0000000000..d916fc180b --- /dev/null +++ b/frontend/app/tests/e2e/role-management/read.spec.ts @@ -0,0 +1,48 @@ +import { expect, test } from "@playwright/test"; +import { ACCOUNT_STATE_PATH } from "../../constants"; + +test.describe("Role management - READ", () => { + test.use({ storageState: ACCOUNT_STATE_PATH.ADMIN }); + + test("should read correctly the different views", async ({ page }) => { + await test.step("access main view", async () => { + await page.goto("/role-management"); + }); + + await test.step("check counts", async () => { + await expect(page.getByRole("link", { name: "Accounts 12" })).toBeVisible(); + await expect(page.getByRole("link", { name: "Groups 6" })).toBeVisible(); + await expect(page.getByRole("link", { name: "Roles 7" })).toBeVisible(); + await expect(page.getByRole("link", { name: "Global Permissions 8" })).toBeVisible(); + await expect(page.getByRole("link", { name: "Object Permissions 4" })).toBeVisible(); + }); + + await test.step("check accounts view", async () => { + await expect(page.getByRole("cell", { name: "admin", exact: true })).toBeVisible(); + await expect(page.getByRole("cell", { name: "Pop-Builder" })).toBeVisible(); + }); + + await test.step("check groups view", async () => { + await page.getByRole("link", { name: "Groups 6" }).click(); + await expect(page.getByText("Showing 1 to 6 of 6 results")).toBeVisible(); + await expect( + page.getByTestId("breadcrumb-navigation").getByRole("link", { name: "Groups" }) + ).toBeVisible(); + await expect(page.getByRole("cell", { name: "Operations Team" })).toBeVisible(); + }); + + await test.step("check roles view", async () => { + await page.getByRole("link", { name: "Roles 7" }).click(); + await expect(page.getByText("General Access")).toBeVisible(); + await expect(page.getByText("Infrahub Users")).toBeVisible(); + await expect(page.getByText("global:edit_default_branch:")).toBeVisible(); + await expect(page.getByRole("cell", { name: "1" }).first()).toBeVisible(); + }); + + await test.step("check global permissions view", async () => { + await page.getByRole("link", { name: "Global Permissions" }).click(); + await expect(page.getByRole("cell", { name: "super_admin", exact: true })).toBeVisible(); + await expect(page.getByText("global:super_admin:")).toBeVisible(); + }); + }); +}); diff --git a/frontend/app/tests/e2e/schema.spec.ts b/frontend/app/tests/e2e/schema.spec.ts index 46337a1d70..76106b52d0 100644 --- a/frontend/app/tests/e2e/schema.spec.ts +++ b/frontend/app/tests/e2e/schema.spec.ts @@ -13,7 +13,7 @@ test.describe("/schema - Schema visualizer", () => { await page.goto("/schema"); await test.step("open schema viewer", async () => { - await page.getByText("CoreArtifact", { exact: true }).click(); + await page.getByText("CoreGraphQL Query", { exact: true }).click(); await expect(page.getByTestId("schema-viewer")).toBeVisible(); }); @@ -46,4 +46,13 @@ test.describe("/schema - Schema visualizer", () => { await expect(page.getByRole("menuitem", { name: "Open list view" })).toBeEnabled(); }); }); + + test("filter schema list", async ({ page }) => { + await page.goto("/schema"); + await expect(page.getByRole("heading", { name: "Core Account Node" })).toBeVisible(); + + await page.getByPlaceholder("Search schema").fill("tag"); + await expect(page.getByRole("heading", { name: "Builtin Tag Node" })).toBeVisible(); + await expect(page.getByRole("heading", { name: "Core Account Node" })).not.toBeVisible(); + }); }); diff --git a/frontend/app/tests/e2e/search.spec.ts b/frontend/app/tests/e2e/search.spec.ts index f9d1e0f398..b4edc2056b 100644 --- a/frontend/app/tests/e2e/search.spec.ts +++ b/frontend/app/tests/e2e/search.spec.ts @@ -13,7 +13,7 @@ test.describe("when searching an object", () => { await page.goto("/"); await test.step("open search anywhere modal with click", async () => { - await page.getByPlaceholder("Search anywhere").click(); + await page.getByTestId("search-anywhere-trigger").click(); await expect(page.getByTestId("search-anywhere")).toBeVisible(); }); @@ -23,24 +23,23 @@ test.describe("when searching an object", () => { }); await test.step("open search anywhere modal when typing on header input", async () => { - await page.getByPlaceholder("Search anywhere").fill("e"); + await page.keyboard.press("Enter"); await expect(page.getByTestId("search-anywhere")).toBeVisible(); }); }); test("displays link to Device list", async ({ page }) => { await page.goto("/"); - await expect(page.getByTestId("sidebar-menu")).toBeVisible(); // Wait for schema to load await test.step("open search anywhere modal with click", async () => { - await page.getByPlaceholder("Search anywhere").click(); + await page.getByTestId("search-anywhere-trigger").click(); await expect(page.getByTestId("search-anywhere")).toBeVisible(); }); await test.step("find a matching result", async () => { await page.getByTestId("search-anywhere").getByPlaceholder("Search anywhere").fill("devi"); await expect(page.getByTestId("search-anywhere")).toContainText("Go to"); - await page.getByRole("option", { name: "All Device(s)" }).click(); + await page.getByRole("option", { name: "Menu Device" }).click(); await expect(page.getByRole("heading", { name: "Device" })).toBeVisible(); expect(page.url()).toContain("/objects/InfraDevice"); }); @@ -50,7 +49,7 @@ test.describe("when searching an object", () => { await page.goto("/"); await test.step("open search anywhere modal", async () => { - await page.getByPlaceholder("Search anywhere").click(); + await page.getByTestId("search-anywhere-trigger").click(); await expect(page.getByTestId("search-anywhere")).toBeVisible(); }); @@ -67,7 +66,7 @@ test.describe("when searching an object", () => { await page.goto("/"); await test.step("open search anywhere modal", async () => { - await page.getByPlaceholder("Search anywhere").click(); + await page.getByTestId("search-anywhere-trigger").click(); await expect(page.getByTestId("search-anywhere")).toBeVisible(); }); @@ -86,7 +85,7 @@ test.describe("when searching an object", () => { const uuid = (await page.locator("dd").first().textContent()) as string; await test.step("open search anywhere modal", async () => { - await page.getByPlaceholder("Search anywhere").click(); + await page.getByTestId("search-anywhere-trigger").click(); await expect(page.getByTestId("search-anywhere")).toBeVisible(); }); diff --git a/frontend/app/tests/e2e/tutorial/tutorial-1_object-create-update-diff-and-merge.spec.ts b/frontend/app/tests/e2e/tutorial/tutorial-1_object-create-update-diff-and-merge.spec.ts index 8cf5cfbd10..e4ca1bfa8f 100644 --- a/frontend/app/tests/e2e/tutorial/tutorial-1_object-create-update-diff-and-merge.spec.ts +++ b/frontend/app/tests/e2e/tutorial/tutorial-1_object-create-update-diff-and-merge.spec.ts @@ -18,12 +18,12 @@ test.describe("Getting started with Infrahub - Object and branch creation, updat let dateBeforeTest: Date; test("1. Create a new organization", async ({ page }) => { - dateBeforeTest = new Date( - Math.floor(new Date().getTime() / (1000 * 60 * 10)) * (1000 * 60 * 10) - ); + dateBeforeTest = new Date(); await page.goto("/"); - await page.getByTestId("sidebar-menu").getByRole("link", { name: "Tenant" }).click(); + + await page.getByTestId("sidebar").getByRole("button", { name: "Organization" }).click(); + await page.getByRole("menuitem", { name: "Tenant" }).click(); await test.step("fill and submit form for new organization", async () => { await page.getByTestId("create-object-button").click(); @@ -45,6 +45,7 @@ test.describe("Getting started with Infrahub - Object and branch creation, updat test("2. Create a new branch", async ({ page }) => { await page.goto("/"); + await page.getByTestId("branch-selector-trigger").click(); await page.getByTestId("create-branch-button").click(); await test.step("fill and submit form for new organization", async () => { @@ -55,14 +56,17 @@ test.describe("Getting started with Infrahub - Object and branch creation, updat }); // After submit - await expect(page.getByTestId("branch-select-menu")).toContainText("cr1234"); + await expect(page.getByTestId("branch-selector-trigger")).toContainText("cr1234"); await expect(page).toHaveURL(/.*?branch=cr1234/); }); test("3. Update an organization", async ({ page }) => { await test.step("Go to the newly created organization on branch cr1234", async () => { await page.goto("/?branch=cr1234"); - await page.getByRole("link", { name: "Tenant" }).click(); + + await page.getByTestId("sidebar").getByRole("button", { name: "Organization" }).click(); + await page.getByRole("menuitem", { name: "Tenant" }).click(); + const myFirstOrgLink = page.getByRole("link", { name: "my-first-tenant" }); await expect(myFirstOrgLink).toBeVisible(); await saveScreenshotForDocs(page, "tutorial_1_organizations"); @@ -86,8 +90,8 @@ test.describe("Getting started with Infrahub - Object and branch creation, updat }); await test.step("See initial value on main branch", async () => { - await page.getByTestId("branch-list-display-button").click(); - await page.getByText("main", { exact: true }).click(); + await page.getByTestId("branch-selector-trigger").click(); + await page.getByRole("option", { name: "main default" }).click(); await expect(page.getByText("Testing Infrahub")).toBeVisible(); }); }); @@ -95,7 +99,8 @@ test.describe("Getting started with Infrahub - Object and branch creation, updat test("4. View the Diff and Merge the branch cr1234 into main", async ({ page }) => { await test.step("Go to branch cr1234 page", async () => { await page.goto("/?branch=cr1234"); - await page.getByTestId("sidebar-menu").getByRole("link", { name: "Branches" }).click(); + await page.getByTestId("branch-selector-trigger").click(); + await page.getByRole("link", { name: "View all branches" }).click(); await saveScreenshotForDocs(page, "tutorial_1_branch_list"); await page.getByTestId("branches-items").getByText("cr1234").click(); await expect(page.locator("dl")).toContainText("cr1234"); @@ -129,10 +134,13 @@ test.describe("Getting started with Infrahub - Object and branch creation, updat }); await test.step("Validate merged changes in main", async () => { - await page.getByTestId("branch-list-display-button").click(); - await page.getByTestId("branch-list-dropdown").getByText("main", { exact: true }).click(); - await expect(page.getByTestId("branch-list-display-button")).toContainText("main"); - await page.getByTestId("sidebar-menu").getByRole("link", { name: "Tenant" }).click(); + await page.getByTestId("branch-selector-trigger").click(); + await page.getByRole("option", { name: "main default" }).click(); + await expect(page.getByTestId("branch-selector-trigger")).toContainText("main"); + + await page.getByTestId("sidebar").getByRole("button", { name: "Organization" }).click(); + await page.getByRole("menuitem", { name: "Tenant" }).click(); + await expect(page.locator("tbody")).toContainText("Changes from branch cr1234"); }); }); @@ -150,6 +158,7 @@ test.describe("Getting started with Infrahub - Object and branch creation, updat await page .getByRole("option", { name: format(dateBeforeTest, "h:mm aa"), exact: true }) .click(); + await expect(page.getByRole("link", { name: "Duff" })).toBeVisible(); await expect( page.getByRole("link", { name: "Changes from branch cr1234" }) ).not.toBeVisible(); @@ -157,7 +166,8 @@ test.describe("Getting started with Infrahub - Object and branch creation, updat await test.step("Row my-first-tenant is visible again when we reset date input", async () => { await page.getByTestId("reset-timeframe-selector").click(); - await expect(page.getByRole("link", { name: "my-first-tenant" })).toBeVisible(); + await expect(page.getByRole("link", { name: "Changes from branch cr1234" })).toBeVisible(); + await expect(page.getByRole("link", { name: "Testing Infrahub" })).not.toBeVisible(); }); }); }); diff --git a/frontend/app/tests/e2e/tutorial/tutorial-2_data-lineage-and-metadata.spec.ts b/frontend/app/tests/e2e/tutorial/tutorial-2_data-lineage-and-metadata.spec.ts index d8056d2a79..25a9274248 100644 --- a/frontend/app/tests/e2e/tutorial/tutorial-2_data-lineage-and-metadata.spec.ts +++ b/frontend/app/tests/e2e/tutorial/tutorial-2_data-lineage-and-metadata.spec.ts @@ -28,7 +28,7 @@ test.describe("Getting started with Infrahub - Data lineage and metadata", () => await test.step("Update Description attribute to make it protected", async () => { await page.getByTestId("edit-metadata-button").click(); await page.getByLabel("Kind").first().click(); - await page.getByRole("option", { name: "Account" }).click(); + await page.getByRole("option", { name: "Account" }).first().click(); await page.getByLabel("Account").click(); await page.getByRole("option", { name: "Admin" }).click(); await page.getByLabel("is protected *").check(); @@ -39,10 +39,5 @@ test.describe("Getting started with Infrahub - Data lineage and metadata", () => await expect(page.getByText("Is protectedTrue")).toBeVisible(); }); - - await test.step("Not allowed to updated description because user is not admin", async () => { - await page.getByTestId("edit-button").click(); - await expect(page.getByLabel("Description")).toBeDisabled(); - }); }); }); diff --git a/frontend/app/tests/e2e/tutorial/tutorial-3_schema.spec.ts b/frontend/app/tests/e2e/tutorial/tutorial-3_schema.spec.ts index 74bbb4cb97..9c0efe5b5a 100644 --- a/frontend/app/tests/e2e/tutorial/tutorial-3_schema.spec.ts +++ b/frontend/app/tests/e2e/tutorial/tutorial-3_schema.spec.ts @@ -15,7 +15,8 @@ test.describe("Getting started with Infrahub - Data lineage and metadata", () => test("1. Visualize the active schema", async ({ page }) => { await page.goto("/"); - await page.getByTestId("sidebar-menu").getByRole("link", { name: "Schema" }).click(); + await page.getByRole("button", { name: "Unified Storage" }).click(); + await page.getByRole("menuitem", { name: "Schema" }).click(); await expect(page.getByText("Artifact Check")).toBeVisible(); await saveScreenshotForDocs(page, "tutorial_3_schema"); }); diff --git a/frontend/app/tests/e2e/tutorial/tutorial-4_integration-with-git.spec.ts b/frontend/app/tests/e2e/tutorial/tutorial-4_integration-with-git.spec.ts index 6288edc7a8..d90ba44fca 100644 --- a/frontend/app/tests/e2e/tutorial/tutorial-4_integration-with-git.spec.ts +++ b/frontend/app/tests/e2e/tutorial/tutorial-4_integration-with-git.spec.ts @@ -17,21 +17,24 @@ test.describe("Getting started with Infrahub - Integration with Git", () => { await page.goto("/"); await test.step(" Create a new branch update-ethernet1", async () => { + await page.getByTestId("branch-selector-trigger").click(); await page.getByTestId("create-branch-button").click(); await page.getByLabel("New branch name").fill("update-ethernet1"); await page.getByLabel("Sync with Git").click(); await saveScreenshotForDocs(page, "tutorial_6_branch_creation"); await page.getByRole("button", { name: "Create" }).click(); - await expect(page.getByTestId("branch-select-menu")).toContainText("update-ethernet1"); + await expect(page.getByTestId("branch-selector-trigger")).toContainText("update-ethernet1"); }); await test.step("go to interface Ethernet 1 for atl1-edge1", async () => { - await page.getByRole("link", { name: "All Device(s)" }).click(); + await page.getByTestId("sidebar").getByRole("button", { name: "Device Management" }).click(); + await page.getByRole("menuitem", { name: "Device", exact: true }).click(); + await expect(page.getByText("Generic Device object")).toBeVisible(); await page.getByRole("link", { name: "atl1-edge1" }).click(); await page.getByText("Interfaces15").click(); - await page.getByRole("link", { name: "Connected to atl1-edge2 Ethernet1" }).click(); + await page.getByRole("link", { name: "Connected to atl1-edge2::Ethernet1" }).click(); }); await test.step("Update the interface Ethernet 1 for atl1-edge1", async () => { diff --git a/frontend/app/tests/fixtures/config.json b/frontend/app/tests/fixtures/config.json index 2dd55adad9..be5994847d 100644 --- a/frontend/app/tests/fixtures/config.json +++ b/frontend/app/tests/fixtures/config.json @@ -1,7 +1,12 @@ { "main": { - "default_branch": "another-branch", - "internal_address": "http://infrahub-server:8000" + "docs_index_path": "/opt/infrahub/docs/build/search-index.json", + "internal_address": "http://infrahub-server:8000", + "allow_anonymous_access": false, + "telemetry_optout": false, + "telemetry_endpoint": "https://telemetry.opsmill.cloud/infrahub", + "telemetry_interval": 86400, + "permission_backends": ["infrahub.permissions.LocalPermissionBackend"] }, "logging": { "remote": { @@ -18,6 +23,10 @@ }, "experimental_features": { "pull_request": false, - "paginated": false + "graphql_enums": false + }, + "sso": { + "enabled": false, + "providers": [] } } diff --git a/frontend/app/tests/fixtures/menu.json b/frontend/app/tests/fixtures/menu.json index 2b8ec906c6..226ffe47e8 100644 --- a/frontend/app/tests/fixtures/menu.json +++ b/frontend/app/tests/fixtures/menu.json @@ -1,231 +1,434 @@ -[ - { - "title": "Objects", - "path": "", - "icon": "", - "children": [ +{ + "sections": { + "internal": [ { - "title": "AutonomousSystem", - "path": "/objects/InfraAutonomousSystem", - "icon": "", - "children": [] - }, - { - "title": "BGPPeerGroup", - "path": "/objects/InfraBGPPeerGroup", - "icon": "", - "children": [] - }, - { - "title": "BGPSession", - "path": "/objects/InfraBGPSession", - "icon": "", - "children": [] - }, - { - "title": "Circuit", - "path": "/objects/InfraCircuit", - "icon": "", - "children": [] - }, - { - "title": "CircuitEndpoint", - "path": "/objects/InfraCircuitEndpoint", - "icon": "", - "children": [] - }, - { - "title": "Criticality", - "path": "/objects/BuiltinCriticality", - "icon": "", - "children": [] + "identifier": "Builtin:ObjectManagement", + "label": "Object Management", + "path": "", + "icon": "mdi:cube-outline", + "kind": "CoreMenuItem", + "order_weight": 1000, + "section": "internal", + "children": [ + { + "identifier": "Builtin:Groups", + "label": "Groups", + "path": "/objects/CoreGroup", + "icon": "mdi:group", + "kind": "CoreMenuItem", + "order_weight": 1000, + "section": "internal", + "children": [] + }, + { + "identifier": "Builtin:Profiles", + "label": "Profiles", + "path": "/objects/CoreProfile", + "icon": "mdi:shape-plus-outline", + "kind": "CoreMenuItem", + "order_weight": 2000, + "section": "internal", + "children": [] + }, + { + "identifier": "Builtin:ResourceManager", + "label": "Resource Manager", + "path": "/resource-manager", + "icon": "mdi:view-grid-outline", + "kind": "CoreMenuItem", + "order_weight": 3000, + "section": "internal", + "children": [] + } + ] }, { - "title": "Device", - "path": "/objects/InfraDevice", - "icon": "", - "children": [] + "identifier": "Builtin:ChangeControl", + "label": "Change Control", + "path": "", + "icon": "mdi:compare-vertical", + "kind": "CoreMenuItem", + "order_weight": 2000, + "section": "internal", + "children": [ + { + "identifier": "Builtin:Branches", + "label": "Branches", + "path": "/branches", + "icon": "mdi:layers-triple", + "kind": "CoreMenuItem", + "order_weight": 1000, + "section": "internal", + "children": [] + }, + { + "identifier": "Builtin:ProposedChanges", + "label": "Proposed Changes", + "path": "/proposed-changes", + "icon": "mdi:file-replace-outline", + "kind": "CoreMenuItem", + "order_weight": 2000, + "section": "internal", + "children": [] + }, + { + "identifier": "Builtin:CheckDefinition", + "label": "Check Definition", + "path": "/objects/CoreCheckDefinition", + "icon": "mdi:check-all", + "kind": "CoreMenuItem", + "order_weight": 3000, + "section": "internal", + "children": [] + }, + { + "identifier": "Builtin:Tasks", + "label": "Tasks", + "path": "/tasks", + "icon": "mdi:shield-check", + "kind": "CoreMenuItem", + "order_weight": 3000, + "section": "internal", + "children": [] + } + ] }, { - "title": "Endpoint", - "path": "/objects/InfraEndpoint", - "icon": "", - "children": [] + "identifier": "Builtin:UnifiedStorage", + "label": "Unified Storage", + "path": "", + "icon": "mdi:archive-arrow-down-outline", + "kind": "CoreMenuItem", + "order_weight": 3000, + "section": "internal", + "children": [ + { + "identifier": "Builtin:Schema", + "label": "Schema", + "path": "/schema", + "icon": "mdi:file-code", + "kind": "CoreMenuItem", + "order_weight": 1000, + "section": "internal", + "children": [] + }, + { + "identifier": "Builtin:Repository", + "label": "Repository", + "path": "/objects/CoreGenericRepository", + "icon": "mdi:source-repository", + "kind": "CoreMenuItem", + "order_weight": 2000, + "section": "internal", + "children": [] + }, + { + "identifier": "Builtin:GraphqlQuery", + "label": "GraphQL Query", + "path": "/objects/CoreGraphQLQuery", + "icon": "mdi:graphql", + "kind": "CoreMenuItem", + "order_weight": 3000, + "section": "internal", + "children": [] + } + ] }, { - "title": "IPAddress", - "path": "/objects/InfraIPAddress", - "icon": "", - "children": [] - }, + "identifier": "Builtin:Admin", + "label": "Admin", + "path": "", + "icon": "mdi:settings-outline", + "kind": "CoreMenuItem", + "order_weight": 3000, + "section": "internal", + "children": [ + { + "identifier": "Builtin:RoleManagement", + "label": "Role Management", + "path": "/role-management", + "icon": "mdi:user-key", + "kind": "CoreMenuItem", + "order_weight": 1000, + "section": "internal", + "children": [] + }, + { + "identifier": "Builtin:Credentials", + "label": "Credentials", + "path": "/objects/CoreCredential", + "icon": "mdi:key-variant", + "kind": "CoreMenuItem", + "order_weight": 2000, + "section": "internal", + "children": [] + }, + { + "identifier": "Builtin:Webhooks", + "label": "Webhooks", + "path": "", + "icon": "mdi:cog-outline", + "kind": "CoreMenuItem", + "order_weight": 3000, + "section": "internal", + "children": [ + { + "identifier": "Builtin:WebhookStandard", + "label": "Webhook", + "path": "/objects/CoreStandardWebhook", + "icon": "mdi:webhook", + "kind": "CoreMenuItem", + "order_weight": 1000, + "section": "internal", + "children": [] + }, + { + "identifier": "Builtin:WebhookCustom", + "label": "Custom Webhook", + "path": "/objects/CoreCustomWebhook", + "icon": "mdi:cog-outline", + "kind": "CoreMenuItem", + "order_weight": 2000, + "section": "internal", + "children": [] + } + ] + } + ] + } + ], + "object": [ { - "title": "Interfaces", + "identifier": "Builtin:Other", + "label": "Other", "path": "", "icon": "", + "kind": "CoreMenuItem", + "order_weight": 2000, + "section": "object", "children": [ { - "title": "Interface L2", - "path": "/objects/InfraInterfaceL2", - "icon": "", + "identifier": "Builtin:Tag", + "label": "Tag", + "path": "/objects/BuiltinTag", + "icon": "mdi:tag-multiple", + "kind": "BuiltinTag", + "order_weight": 5000, + "section": "object", "children": [] }, { - "title": "Interface L3", - "path": "/objects/InfraInterfaceL3", - "icon": "", + "identifier": "Infra:AutonomousSystem", + "label": "Autonomous System", + "path": "/objects/InfraAutonomousSystem", + "icon": "mdi:bank-circle-outline", + "kind": "InfraAutonomousSystem", + "order_weight": 5000, + "section": "object", + "children": [] + }, + { + "identifier": "Infra:BGPPeerGroup", + "label": "BGP Peer Group", + "path": "/objects/InfraBGPPeerGroup", + "icon": "mdi:view-grid-plus-outline", + "kind": "InfraBGPPeerGroup", + "order_weight": 5000, + "section": "object", + "children": [] + }, + { + "identifier": "Infra:BGPSession", + "label": "BGP Session", + "path": "/objects/InfraBGPSession", + "icon": "mdi:router", + "kind": "InfraBGPSession", + "order_weight": 5000, + "section": "object", + "children": [] + }, + { + "identifier": "Infra:BackBoneService", + "label": "Backbone Service", + "path": "/objects/InfraBackBoneService", + "icon": "carbon:container-services", + "kind": "InfraBackBoneService", + "order_weight": 5000, + "section": "object", "children": [] }, { - "title": "Interface", + "identifier": "Infra:Circuit", + "label": "Circuit", + "path": "/objects/InfraCircuit", + "icon": "mdi:cable-data", + "kind": "InfraCircuit", + "order_weight": 5000, + "section": "object", + "children": [] + }, + { + "identifier": "Infra:Device", + "label": "Device", + "path": "/objects/InfraDevice", + "icon": "mdi:server", + "kind": "InfraDevice", + "order_weight": 5000, + "section": "object", + "children": [] + }, + { + "identifier": "Infra:MlagDomain", + "label": "MLAG Domain", + "path": "/objects/InfraMlagDomain", + "icon": "eos-icons:cluster-management", + "kind": "InfraMlagDomain", + "order_weight": 5000, + "section": "object", + "children": [] + }, + { + "identifier": "Infra:Platform", + "label": "Platform", + "path": "/objects/InfraPlatform", + "icon": "mdi:application-cog-outline", + "kind": "InfraPlatform", + "order_weight": 5000, + "section": "object", + "children": [] + }, + { + "identifier": "Infra:VLAN", + "label": "VLAN", + "path": "/objects/InfraVLAN", + "icon": "mdi:lan-pending", + "kind": "InfraVLAN", + "order_weight": 5000, + "section": "object", + "children": [] + }, + { + "identifier": "Location:Continent", + "label": "Continent", + "path": "/objects/LocationContinent", + "icon": "jam:world", + "kind": "LocationContinent", + "order_weight": 5000, + "section": "object", + "children": [] + }, + { + "identifier": "Location:Country", + "label": "Country", + "path": "/objects/LocationCountry", + "icon": "gis:search-country", + "kind": "LocationCountry", + "order_weight": 5000, + "section": "object", + "children": [] + }, + { + "identifier": "Location:Rack", + "label": "Rack", + "path": "/objects/LocationRack", + "icon": "clarity:rack-server-solid", + "kind": "LocationRack", + "order_weight": 5000, + "section": "object", + "children": [] + }, + { + "identifier": "Location:Site", + "label": "Site", + "path": "/objects/LocationSite", + "icon": "ri:building-line", + "kind": "LocationSite", + "order_weight": 5000, + "section": "object", + "children": [] + }, + { + "identifier": "Organization:Manufacturer", + "label": "Manufacturer", + "path": "/objects/OrganizationManufacturer", + "icon": "mdi:domain", + "kind": "OrganizationManufacturer", + "order_weight": 5000, + "section": "object", + "children": [] + }, + { + "identifier": "Organization:Provider", + "label": "Provider", + "path": "/objects/OrganizationProvider", + "icon": "mdi:domain", + "kind": "OrganizationProvider", + "order_weight": 5000, + "section": "object", + "children": [] + }, + { + "identifier": "Organization:Tenant", + "label": "Tenant", + "path": "/objects/OrganizationTenant", + "icon": "mdi:domain", + "kind": "OrganizationTenant", + "order_weight": 5000, + "section": "object", + "children": [] + }, + { + "identifier": "Infra:Interface", + "label": "Interface", "path": "/objects/InfraInterface", + "icon": "mdi:ethernet", + "kind": "InfraInterface", + "order_weight": 5000, + "section": "object", + "children": [] + }, + { + "identifier": "Infra:MlagInterface", + "label": "MLAG Interface", + "path": "/objects/InfraMlagInterface", + "icon": "mdi:ethernet", + "kind": "InfraMlagInterface", + "order_weight": 5000, + "section": "object", + "children": [] + }, + { + "identifier": "Infra:Service", + "label": "Service", + "path": "/objects/InfraService", "icon": "", + "kind": "InfraService", + "order_weight": 5000, + "section": "object", + "children": [] + }, + { + "identifier": "Location:Generic", + "label": "Location", + "path": "/objects/LocationGeneric", + "icon": "mingcute:location-line", + "kind": "LocationGeneric", + "order_weight": 5000, + "section": "object", + "children": [] + }, + { + "identifier": "Organization:Generic", + "label": "Organization", + "path": "/objects/OrganizationGeneric", + "icon": "mdi:domain", + "kind": "OrganizationGeneric", + "order_weight": 5000, + "section": "object", "children": [] } ] - }, - { - "title": "Location", - "path": "/objects/BuiltinLocation", - "icon": "", - "children": [] - }, - { - "title": "Organization", - "path": "/objects/CoreOrganization", - "icon": "", - "children": [] - }, - { - "title": "Platform", - "path": "/objects/InfraPlatform", - "icon": "", - "children": [] - }, - { - "title": "Role", - "path": "/objects/BuiltinRole", - "icon": "", - "children": [] - }, - { - "title": "Status", - "path": "/objects/BuiltinStatus", - "icon": "", - "children": [] - }, - { - "title": "Tag", - "path": "/objects/BuiltinTag", - "icon": "", - "children": [] - }, - { - "title": "VLAN", - "path": "/objects/InfraVLAN", - "icon": "", - "children": [] - } - ] - }, - { - "title": "Groups", - "path": "", - "icon": "", - "children": [ - { - "title": "StandardGroup", - "path": "/objects/CoreStandardGroup", - "icon": "", - "children": [] - } - ] - }, - { - "title": "Unified Storage", - "path": "", - "icon": "", - "children": [ - { - "title": "Schema", - "path": "/schema", - "icon": "", - "children": [] - }, - { - "title": "Repository", - "path": "/objects/CoreCheckDefinition", - "icon": "", - "children": [] - }, - { - "title": "GraphQL Query", - "path": "/objects/CoreGraphQLQuery", - "icon": "", - "children": [] - } - ] - }, - { - "title": "Change Control", - "path": "", - "icon": "", - "children": [ - { - "title": "Branches", - "path": "/branches", - "icon": "", - "children": [] - }, - { - "title": "Proposed Changes", - "path": "/proposed-changes", - "icon": "", - "children": [] - }, - { - "title": "Check Definition", - "path": "/objects/CoreCheckDefinition", - "icon": "", - "children": [] - } - ] - }, - { - "title": "Deployment", - "path": "", - "icon": "", - "children": [ - { - "title": "Artifact", - "path": "/objects/CoreArtifact", - "icon": "", - "children": [] - }, - { - "title": "Artifact Definition", - "path": "/objects/CoreArtifactDefinition", - "icon": "", - "children": [] - }, - { - "title": "Transformation", - "path": "/objects/CoreTransformation", - "icon": "", - "children": [] - } - ] - }, - { - "title": "Admin", - "path": "", - "icon": "", - "children": [ - { - "title": "Accounts", - "path": "objects/CoreGenericAccount", - "icon": "", - "children": [] } ] } -] +} diff --git a/frontend/app/tests/fixtures/schema.json b/frontend/app/tests/fixtures/schema.json index 94fb27fa83..b157d6b315 100644 --- a/frontend/app/tests/fixtures/schema.json +++ b/frontend/app/tests/fixtures/schema.json @@ -75,36 +75,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": "type__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 100000 }, { @@ -119,36 +89,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 - } - ], "order_weight": 101000 }, { @@ -163,36 +103,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 - } - ], "order_weight": 102000 }, { @@ -207,50 +117,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": "speed__value", - "kind": "Number", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "mtu__value", - "kind": "Number", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "enabled__value", - "kind": "Boolean", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 103000 }, { @@ -265,36 +131,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": "asn__value", - "kind": "Number", - "enum": null, - "object_kind": null, - "description": null - }, - { - "name": "description__value", - "kind": "Text", - "enum": null, - "object_kind": null, - "description": null - } - ], "order_weight": 104000 }, { @@ -309,101 +145,13 @@ "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": 105000 } ], "label": "Device", "inherit_from": [], "groups": [], - "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": "site__id", - "kind": "Object", - "enum": null, - "object_kind": "Location", - "description": null - }, - { - "name": "status__id", - "kind": "Object", - "enum": null, - "object_kind": "Status", - "description": null - }, - { - "name": "role__id", - "kind": "Object", - "enum": null, - "object_kind": "Role", - "description": null - }, - { - "name": "asn__id", - "kind": "Object", - "enum": null, - "object_kind": "AutonomousSystem", - "description": null - }, - { - "name": "tags__id", - "kind": "Object", - "enum": null, - "object_kind": "Tag", - "description": null - } - ] + "branch": "aware" } ] } diff --git a/frontend/app/tests/integrations/lib/badge.cy.tsx b/frontend/app/tests/integrations/lib/badge.cy.tsx index 6fd0ced733..e109bb029d 100644 --- a/frontend/app/tests/integrations/lib/badge.cy.tsx +++ b/frontend/app/tests/integrations/lib/badge.cy.tsx @@ -1,6 +1,3 @@ -/// - -import React from "react"; import { BADGE_TYPES, Badge } from "../../../src/components/display/badge"; describe("Badge component", () => { diff --git a/frontend/app/tests/integrations/lib/button.cy.tsx b/frontend/app/tests/integrations/lib/button.cy.tsx index 8c7ba6b30e..81ad3c2795 100644 --- a/frontend/app/tests/integrations/lib/button.cy.tsx +++ b/frontend/app/tests/integrations/lib/button.cy.tsx @@ -1,6 +1,3 @@ -/// - -import React from "react"; import { BUTTON_TYPES, Button } from "../../../src/components/buttons/button"; describe("Button component", () => { diff --git a/frontend/app/tests/integrations/lib/pill.cy.tsx b/frontend/app/tests/integrations/lib/pill.cy.tsx index 0a943f2ed4..4137bf148c 100644 --- a/frontend/app/tests/integrations/lib/pill.cy.tsx +++ b/frontend/app/tests/integrations/lib/pill.cy.tsx @@ -1,6 +1,3 @@ -/// - -import React from "react"; import { PILL_TYPES, Pill } from "../../../src/components/display/pill"; describe("Pill component", () => { diff --git a/frontend/app/tests/integrations/screens/account.cy.tsx b/frontend/app/tests/integrations/screens/account.cy.tsx index 70356b49e6..9493e6b13e 100644 --- a/frontend/app/tests/integrations/screens/account.cy.tsx +++ b/frontend/app/tests/integrations/screens/account.cy.tsx @@ -1,11 +1,9 @@ /// import { MockedProvider } from "@apollo/client/testing"; -import React from "react"; -import { initials } from "../../../src/components/display/avatar"; -import { ACCESS_TOKEN_KEY } from "../../../src/config/constants"; +import { AccountMenu } from "../../../src/components/account-menu"; +import { ACCESS_TOKEN_KEY } from "../../../src/config/localStorage"; import { AuthProvider } from "../../../src/hooks/useAuth"; -import Header from "../../../src/screens/layout/header"; import { genericsState } from "../../../src/state/atoms/schema.atom"; import { encodeJwt } from "../../../src/utils/common"; import { accountDetailsMocksSchema } from "../../mocks/data/account"; @@ -21,7 +19,7 @@ const mocks = [ { request: { query: profileDetailsMocksQuery, - variables: { offset: 0, limit: 10 }, + variables: {}, }, result: { data: profileDetailsMocksData, @@ -31,7 +29,7 @@ const mocks = [ const AuthHeader = () => ( -
+ ); @@ -56,9 +54,6 @@ describe("List screen", () => { ); - cy.get(".h-12").should( - "have.text", - initials(profileDetailsMocksData.AccountProfile.display_label) - ); + cy.contains(profileDetailsMocksData.AccountProfile.display_label); }); }); diff --git a/frontend/app/tests/integrations/screens/app-init.cy.tsx b/frontend/app/tests/integrations/screens/app-init.cy.tsx index 923eb0ea7f..22e469d407 100644 --- a/frontend/app/tests/integrations/screens/app-init.cy.tsx +++ b/frontend/app/tests/integrations/screens/app-init.cy.tsx @@ -1,9 +1,8 @@ /// import { MockedProvider } from "@apollo/client/testing"; -import React from "react"; -import { App } from "../../../src/App"; import { mount } from "cypress/react18"; +import { App } from "../../../src/App"; describe("Config fetch", () => { beforeEach(function () { @@ -33,11 +32,11 @@ describe("Config fetch", () => { ); - cy.contains("Sign in to your account"); + cy.contains("Log in to your account"); cy.contains("label", "Username").parent().next().clear({ force: true }); cy.contains("label", "Username").parent().next().type("test"); cy.contains("label", "Password").parent().next().type("test"); - cy.contains("button", "Sign in").click(); + cy.contains("button", "Log in").click(); cy.wait("@login").then(({ response }) => { expect(response?.body?.access_token).to.exist; @@ -54,10 +53,7 @@ describe("Config fetch", () => { }); cy.wait("@getMenu").then(() => { - // Check if the Objects menu is existing - cy.get("[data-cy='sidebar-menu']").within(() => { - cy.contains("Objects").should("exist"); - }); + cy.contains("Object Management").should("exist"); }); }); }); diff --git a/frontend/app/tests/integrations/screens/artifact-diff.cy.tsx b/frontend/app/tests/integrations/screens/artifact-diff.cy.tsx index ccd950b826..9eee02f5d5 100644 --- a/frontend/app/tests/integrations/screens/artifact-diff.cy.tsx +++ b/frontend/app/tests/integrations/screens/artifact-diff.cy.tsx @@ -3,7 +3,7 @@ import { gql } from "@apollo/client"; import { MockedProvider } from "@apollo/client/testing"; import { Route, Routes } from "react-router-dom"; -import { ACCESS_TOKEN_KEY } from "../../../src/config/constants"; +import { ACCESS_TOKEN_KEY } from "../../../src/config/localStorage"; import { withSchemaContext } from "../../../src/decorators/withSchemaContext"; import { AuthProvider } from "../../../src/hooks/useAuth"; import { ArtifactsDiff } from "../../../src/screens/diff/artifact-diff/artifacts-diff"; @@ -12,6 +12,8 @@ import { schemaState } from "../../../src/state/atoms/schema.atom"; import { encodeJwt } from "../../../src/utils/common"; import { accountDetailsMocksSchema } from "../../mocks/data/account"; import { + artifactPermissionsData, + artifactPermissionsQuery, artifactThreadMockData, artifactThreadMockQuery, artifactThreadSchema, @@ -39,6 +41,17 @@ const mocks = [ data: artifactThreadMockData, }, }, + { + request: { + query: gql` + ${artifactPermissionsQuery} + `, + variables: { offset: 0, limit: 10 }, + }, + result: { + data: artifactPermissionsData, + }, + }, ]; const SchemaArtifactsDiff = withSchemaContext(ArtifactsDiff); @@ -55,7 +68,8 @@ const ArtifactsDiffProvider = ({ loggedIn }: { loggedIn: boolean }) => { initialValues={[ [schemaState, [...artifactThreadSchema, ...accountDetailsMocksSchema]], [proposedChangedState, proposedChangesDetails], - ]}> + ]} + > {loggedIn ? : } ); @@ -168,9 +182,6 @@ describe("Artifact Diff", () => { before(() => { const data = { sub: profileId, - user_claims: { - role: "admin", - }, }; const token = encodeJwt(data); diff --git a/frontend/app/tests/integrations/screens/conversations.cy.tsx b/frontend/app/tests/integrations/screens/conversations.cy.tsx index 248717298d..e4179ad304 100644 --- a/frontend/app/tests/integrations/screens/conversations.cy.tsx +++ b/frontend/app/tests/integrations/screens/conversations.cy.tsx @@ -2,7 +2,6 @@ import { gql } from "@apollo/client"; import { MockedProvider } from "@apollo/client/testing"; -import React from "react"; import { Route, Routes } from "react-router-dom"; import { Conversations } from "../../../src/screens/proposed-changes/conversations"; import { proposedChangedState } from "../../../src/state/atoms/proposedChanges.atom"; @@ -42,7 +41,8 @@ const ConversationsProvider = () => { initialValues={[ [schemaState, [...conversationMocksSchema, ...accountDetailsMocksSchema]], [proposedChangedState, proposedChangesDetails], - ]}> + ]} + > ); diff --git a/frontend/app/tests/integrations/screens/object-details-relationships.cy.tsx b/frontend/app/tests/integrations/screens/object-details-relationships.cy.tsx index 8e8f3c3bec..3a42039e3c 100644 --- a/frontend/app/tests/integrations/screens/object-details-relationships.cy.tsx +++ b/frontend/app/tests/integrations/screens/object-details-relationships.cy.tsx @@ -2,8 +2,8 @@ import { gql } from "@apollo/client"; import { MockedProvider } from "@apollo/client/testing"; -import React from "react"; import { Route, Routes } from "react-router-dom"; +import { ObjectDetailsPage } from "../../../src/pages/objects/object-details"; import { genericsState, schemaState } from "../../../src/state/atoms/schema.atom"; import { deviceDetailsInterfacesMocksData, @@ -13,12 +13,13 @@ import { deviceDetailsMocksId, deviceDetailsMocksQuery, deviceDetailsMocksSchema, + getPermissionsData, + getPermissionsQuery, interfaceDescription, interfaceLabelName, interfacesArrayCount, } from "../../mocks/data/devices"; import { TestProvider } from "../../mocks/jotai/atom"; -import { ObjectDetailsPage } from "../../../src/pages/objects/object-details"; // URL for the current view const graphqlQueryItemsUrl = `/objects/InfraDevice/${deviceDetailsMocksId}`; @@ -63,6 +64,30 @@ const mocks: any[] = [ data: deviceDetailsMocksData, }, }, + // Permissions + { + request: { + query: gql` + ${getPermissionsQuery} + `, + variables: { offset: 0, limit: 10 }, + }, + result: { + data: getPermissionsData, + }, + }, + // Permissions + { + request: { + query: gql` + ${getPermissionsQuery} + `, + variables: { offset: 0, limit: 10 }, + }, + result: { + data: getPermissionsData, + }, + }, // Relationships view { request: { @@ -75,6 +100,18 @@ const mocks: any[] = [ data: deviceDetailsInterfacesMocksData, }, }, + // Permissions + { + request: { + query: gql` + ${getPermissionsQuery} + `, + variables: { offset: 0, limit: 10 }, + }, + result: { + data: getPermissionsData, + }, + }, { request: { query: gql` @@ -95,7 +132,8 @@ const ObjectDetailsProvider = () => { initialValues={[ [schemaState, deviceDetailsMocksSchema], [genericsState, deviceDetailsMocksGenerics], - ]}> + ]} + > ); diff --git a/frontend/app/tests/integrations/screens/object-details.cy.tsx b/frontend/app/tests/integrations/screens/object-details.cy.tsx index e2454405aa..3e9d3fd37b 100644 --- a/frontend/app/tests/integrations/screens/object-details.cy.tsx +++ b/frontend/app/tests/integrations/screens/object-details.cy.tsx @@ -2,8 +2,8 @@ import { gql } from "@apollo/client"; import { MockedProvider } from "@apollo/client/testing"; -import React from "react"; import { Route, Routes } from "react-router-dom"; +import { ObjectDetailsPage } from "../../../src/pages/objects/object-details"; import { schemaState } from "../../../src/state/atoms/schema.atom"; import { deviceDetailsMocksASNName, @@ -13,9 +13,10 @@ import { deviceDetailsMocksQuery, deviceDetailsMocksSchema, deviceDetailsMocksTagName, + getPermissionsData, + getPermissionsQuery, } from "../../mocks/data/devices"; import { TestProvider } from "../../mocks/jotai/atom"; -import { ObjectDetailsPage } from "../../../src/pages/objects/object-details"; // URL for the current view const graphqlQueryItemsUrl = `/objects/InfraDevice/${deviceDetailsMocksId}`; @@ -36,6 +37,18 @@ const mocks: any[] = [ data: deviceDetailsMocksData, }, }, + // Permissions + { + request: { + query: gql` + ${getPermissionsQuery} + `, + variables: { offset: 0, limit: 10 }, + }, + result: { + data: getPermissionsData, + }, + }, ]; // Provide the initial value for jotai diff --git a/frontend/app/tests/integrations/screens/object-fields.cy.tsx b/frontend/app/tests/integrations/screens/object-fields.cy.tsx index daa75f28be..353442330c 100644 --- a/frontend/app/tests/integrations/screens/object-fields.cy.tsx +++ b/frontend/app/tests/integrations/screens/object-fields.cy.tsx @@ -1,19 +1,18 @@ /// import { MockedProvider } from "@apollo/client/testing"; -import React from "react"; import { Route, Routes } from "react-router-dom"; import { schemaState } from "../../../src/state/atoms/schema.atom"; import { gql } from "@apollo/client"; -import { ACCESS_TOKEN_KEY } from "../../../src/config/constants"; +import { ACCESS_TOKEN_KEY } from "../../../src/config/localStorage"; import { AuthProvider } from "../../../src/hooks/useAuth"; import { encodeJwt } from "../../../src/utils/common"; import { accountDetailsMocksSchema } from "../../mocks/data/account"; import { profileDetailsMocksData, - profilesDetailsMocksQuery, profileId, + profilesDetailsMocksQuery, } from "../../mocks/data/account-profile"; import { taskMocksData as taskMocksData1, @@ -47,8 +46,10 @@ import { taskMocksSchema as taskMocksSchema5, } from "../../mocks/data/task_5"; +import { ObjectItemsPage } from "../../../src/pages/objects/object-items"; import { ipamIpAddressMocksSchema } from "../../mocks/data/ip-address"; import { ipPrefixMocksSchema } from "../../mocks/data/ip-prefix"; +import { numberPoolData, numberPoolQuery } from "../../mocks/data/number-pool"; import { taskMocksData as taskMocksData6, taskMocksQuery as taskMocksQuery6, @@ -60,8 +61,6 @@ import { taskMocksSchema as taskMocksSchema7, } from "../../mocks/data/task_7"; import { TestProvider } from "../../mocks/jotai/atom"; -import { ObjectItemsPage } from "../../../src/pages/objects/object-items"; -import { numberPoolData, numberPoolQuery } from "../../mocks/data/number-pool"; // URL for the current view const mockedUrl = "/objects/TestTask"; @@ -196,7 +195,8 @@ describe("Object list", () => { const ObjectItemsProvider = () => { return ( + initialValues={[[schemaState, [...accountDetailsMocksSchema, ...taskMocksSchema1]]]} + > ); @@ -238,7 +238,8 @@ describe("Object list", () => { + ]} + > ); @@ -280,7 +281,8 @@ describe("Object list", () => { + ]} + > ); @@ -320,7 +322,8 @@ describe("Object list", () => { const ObjectItemsProvider = () => { return ( + initialValues={[[schemaState, [...accountDetailsMocksSchema, ...taskMocksSchema2]]]} + > ); @@ -362,7 +365,8 @@ describe("Object list", () => { + ]} + > ); @@ -404,7 +408,8 @@ describe("Object list", () => { + ]} + > ); @@ -444,7 +449,8 @@ describe("Object list", () => { const ObjectItemsProvider = () => { return ( + initialValues={[[schemaState, [...accountDetailsMocksSchema, ...taskMocksSchema3]]]} + > ); @@ -484,7 +490,8 @@ describe("Object list", () => { const ObjectItemsProvider = () => { return ( + initialValues={[[schemaState, [...accountDetailsMocksSchema, ...taskMocksSchema3]]]} + > ); @@ -529,7 +536,8 @@ describe("Object list", () => { + ]} + > ); @@ -571,7 +579,8 @@ describe("Object list", () => { + ]} + > ); @@ -611,7 +620,8 @@ describe("Object list", () => { const ObjectItemsProvider = () => { return ( + initialValues={[[schemaState, [...accountDetailsMocksSchema, ...taskMocksSchema4]]]} + > ); @@ -651,7 +661,8 @@ describe("Object list", () => { const ObjectItemsProvider = () => { return ( + initialValues={[[schemaState, [...accountDetailsMocksSchema, ...taskMocksSchema5]]]} + > ); @@ -701,7 +712,8 @@ describe("Object list", () => { ipamIpAddressMocksSchema, ], ], - ]}> + ]} + > ); @@ -737,7 +749,8 @@ describe("Object list", () => { const ObjectItemsProvider = () => { return ( + initialValues={[[schemaState, [...accountDetailsMocksSchema, ...taskMocksSchema7]]]} + > ); diff --git a/frontend/app/tests/integrations/screens/object-items-deletion.cy.tsx b/frontend/app/tests/integrations/screens/object-items-deletion.cy.tsx index b1b7455d96..b516e973bc 100644 --- a/frontend/app/tests/integrations/screens/object-items-deletion.cy.tsx +++ b/frontend/app/tests/integrations/screens/object-items-deletion.cy.tsx @@ -2,10 +2,10 @@ import { gql } from "@apollo/client"; import { MockedProvider } from "@apollo/client/testing"; -import React from "react"; import { Route, Routes } from "react-router-dom"; -import { ACCESS_TOKEN_KEY } from "../../../src/config/constants"; +import { ACCESS_TOKEN_KEY } from "../../../src/config/localStorage"; import { AuthProvider } from "../../../src/hooks/useAuth"; +import { ObjectItemsPage } from "../../../src/pages/objects/object-items"; import { configState } from "../../../src/state/atoms/config.atom"; import { schemaState } from "../../../src/state/atoms/schema.atom"; import { mockedToken } from "../../fixtures/auth"; @@ -18,7 +18,6 @@ import { } from "../../mocks/data/graphqlQueries"; import { schemaMocks } from "../../mocks/data/schema"; import { TestProvider } from "../../mocks/jotai/atom"; -import { ObjectItemsPage } from "../../../src/pages/objects/object-items"; // URL for the current view const mockedUrl = "/objects/CoreGraphQLQuery"; @@ -67,7 +66,8 @@ const ObjectItemsProvider = () => { initialValues={[ [schemaState, schemaMocks], [configState, configMocks], - ]}> + ]} + > ); diff --git a/frontend/app/tests/integrations/screens/object-items.cy.tsx b/frontend/app/tests/integrations/screens/object-items.cy.tsx index 89633dee07..6efeb2c9e6 100644 --- a/frontend/app/tests/integrations/screens/object-items.cy.tsx +++ b/frontend/app/tests/integrations/screens/object-items.cy.tsx @@ -2,8 +2,8 @@ import { gql } from "@apollo/client"; import { MockedProvider } from "@apollo/client/testing"; -import React from "react"; import { Route, Routes } from "react-router-dom"; +import { ObjectItemsPage } from "../../../src/pages/objects/object-items"; import { genericsState, schemaState } from "../../../src/state/atoms/schema.atom"; import { graphqlQueriesMocksData, @@ -12,7 +12,6 @@ import { } from "../../mocks/data/graphqlQueries"; import { schemaMocks } from "../../mocks/data/schema"; import { TestProvider } from "../../mocks/jotai/atom"; -import { ObjectItemsPage } from "../../../src/pages/objects/object-items"; // URL for the current view const mockedUrl = "/objects/CoreGraphQLQuery"; @@ -111,7 +110,8 @@ describe("List screen", () => { initialValues={[ [schemaState, []], [genericsState, schemaMocks], - ]}> + ]} + > ); diff --git a/frontend/app/tests/mocks/data/account.ts b/frontend/app/tests/mocks/data/account.ts index 8ad73e2749..09f496d896 100644 --- a/frontend/app/tests/mocks/data/account.ts +++ b/frontend/app/tests/mocks/data/account.ts @@ -1,5 +1,3 @@ -export const accountId = "bfb5c658-d606-47b1-b614-d2e44e6d3e67"; - export const accountDetailsMocksSchema = [ { id: "17e3078a-8ef3-b130-2781-179f619825a0", @@ -871,178 +869,3 @@ export const accountDetailsMocksSchema = [ hash: "97aba0a736b3e673e0fdd33b32757c1c", }, ]; - -export const accountDetailsMocksQuery = `query CoreAccount { - CoreAccount (ids: ["${accountId}"] ) { - edges { - node { - id - display_label - profiles { - edges { - node { - display_label - id - } - } - } - name { - value - updated_at - is_from_profile - is_protected - is_visible - source { - id - display_label - __typename - } - owner { - id - display_label - __typename - } - } - label { - value - updated_at - is_from_profile - is_protected - is_visible - source { - id - display_label - __typename - } - owner { - id - display_label - __typename - } - } - description { - value - updated_at - is_from_profile - is_protected - is_visible - source { - id - display_label - __typename - } - owner { - id - display_label - __typename - } - } - account_type { - value - updated_at - is_from_profile - is_protected - is_visible - source { - id - display_label - __typename - } - owner { - id - display_label - __typename - } - } - role { - value - updated_at - is_from_profile - is_protected - is_visible - source { - id - display_label - __typename - } - owner { - id - display_label - __typename - } - } - } - } - } -} -`; - -export const accountDetailsMocksData = { - CoreAccount: { - edges: [ - { - node: { - id: accountId, - display_label: "Admin", - name: { - value: "admin", - updated_at: "2023-07-03T06:51:06.645925+00:00", - is_protected: false, - is_visible: true, - source: null, - owner: null, - __typename: "TextAttribute", - }, - password: { - value: "$2b$12$9/3ivk9fIDWah40iXsCn1ubiwkCKNIuyOlUww1wVJ6CuQ2Q2u8wAS", - updated_at: "2023-07-03T06:51:06.645925+00:00", - is_protected: false, - is_visible: true, - source: null, - owner: null, - __typename: "TextAttribute", - }, - label: { - value: "Admin", - updated_at: "2023-07-03T06:51:06.645925+00:00", - is_protected: false, - is_visible: true, - source: null, - owner: null, - __typename: "TextAttribute", - }, - description: { - value: null, - updated_at: "2023-07-03T06:51:06.645925+00:00", - is_protected: false, - is_visible: true, - source: null, - owner: null, - __typename: "TextAttribute", - }, - account_type: { - value: "User", - updated_at: "2023-07-03T06:51:06.645925+00:00", - is_protected: false, - is_visible: true, - source: null, - owner: null, - __typename: "TextAttribute", - }, - role: { - value: "admin", - updated_at: "2023-07-03T06:51:06.645925+00:00", - is_protected: false, - is_visible: true, - source: null, - owner: null, - __typename: "TextAttribute", - }, - __typename: "CoreAccount", - }, - __typename: "EdgedCoreAccount", - }, - ], - __typename: "PaginatedCoreAccount", - }, -}; diff --git a/frontend/app/tests/mocks/data/accountToken.ts b/frontend/app/tests/mocks/data/accountToken.ts deleted file mode 100644 index bb4ca30b9f..0000000000 --- a/frontend/app/tests/mocks/data/accountToken.ts +++ /dev/null @@ -1,1316 +0,0 @@ -import { iNodeSchema } from "../../../src/state/atoms/schema.atom"; - -export const accountTokenId = "bfb5c658-d606-47b1-b614-d2e44e6d3e67"; -export const accountTokenNewDate = "2023-07-14T22:00:00.000Z"; - -export const accountTokenDetailsMocksSchema: iNodeSchema[] = [ - { - id: accountTokenId, - name: "AccountToken", - namespace: "Internal", - description: "Token for User Account", - default_filter: "token__value", - order_by: undefined, - display_labels: ["token__value"], - attributes: [ - { - id: "a860d35c-76e0-4e07-a76c-ce36948a6464", - name: "name", - kind: "Text", - namespace: "Attribute", - label: "Name", - description: undefined, - default_value: undefined, - enum: undefined, - regex: undefined, - max_length: undefined, - min_length: undefined, - inherited: false, - unique: false, - branch: true, - optional: true, - order_weight: 1000, - }, - { - id: "dc040126-b39b-4522-a147-9cbd138f4464", - name: "token", - kind: "Text", - namespace: "Attribute", - label: "Token", - description: undefined, - default_value: undefined, - enum: undefined, - regex: undefined, - max_length: undefined, - min_length: undefined, - inherited: false, - unique: true, - branch: true, - optional: false, - order_weight: 2000, - }, - { - id: "5f156988-5e99-4dff-b3ee-30120a95d344", - name: "expiration", - kind: "DateTime", - namespace: "Attribute", - label: "Expiration", - description: undefined, - default_value: undefined, - enum: undefined, - regex: undefined, - max_length: undefined, - min_length: undefined, - inherited: false, - unique: false, - branch: true, - optional: true, - order_weight: 3000, - }, - ], - relationships: [ - { - id: "8ed7a95f-5b49-4df4-b901-c8cd1d9e6430", - name: "account", - peer: "CoreAccount", - kind: "Generic", - label: "Account", - description: undefined, - identifier: "coreaccount__internalaccounttoken", - inherited: false, - cardinality: "one", - branch: true, - optional: true, - filters: [ - { - name: "id", - kind: "Text", - enum: undefined, - object_kind: undefined, - description: undefined, - }, - { - name: "name__value", - kind: "Text", - enum: undefined, - object_kind: undefined, - description: undefined, - }, - { - name: "label__value", - kind: "Text", - enum: undefined, - object_kind: undefined, - description: undefined, - }, - { - name: "description__value", - kind: "Text", - enum: undefined, - object_kind: undefined, - description: undefined, - }, - { - name: "type__value", - kind: "Text", - enum: ["User", "Script", "Bot", "Git"], - object_kind: undefined, - description: undefined, - }, - { - name: "role__value", - kind: "Text", - enum: ["admin", "read-only", "read-write"], - object_kind: undefined, - description: undefined, - }, - ], - order_weight: 4000, - }, - { - id: "78a226e0-7670-4e6b-aac1-77aacfb406d0", - name: "member_of_groups", - peer: "CoreGroup", - kind: "Group", - label: "Member Of Groups", - description: undefined, - identifier: "group_member", - inherited: false, - cardinality: "many", - branch: true, - optional: true, - filters: [ - { - name: "id", - kind: "Text", - enum: undefined, - object_kind: undefined, - description: undefined, - }, - { - name: "name__value", - kind: "Text", - enum: undefined, - object_kind: undefined, - description: undefined, - }, - { - name: "label__value", - kind: "Text", - enum: undefined, - object_kind: undefined, - description: undefined, - }, - { - name: "description__value", - kind: "Text", - enum: undefined, - object_kind: undefined, - description: undefined, - }, - ], - order_weight: 5000, - }, - { - id: "ee77fcc3-d6fc-4091-a17f-739c3039795f", - name: "subscriber_of_groups", - peer: "CoreGroup", - kind: "Group", - label: "Subscriber Of Groups", - description: undefined, - identifier: "group_subscriber", - inherited: false, - cardinality: "many", - branch: true, - optional: true, - filters: [ - { - name: "id", - kind: "Text", - enum: undefined, - object_kind: undefined, - description: undefined, - }, - { - name: "name__value", - kind: "Text", - enum: undefined, - object_kind: undefined, - description: undefined, - }, - { - name: "label__value", - kind: "Text", - enum: undefined, - object_kind: undefined, - description: undefined, - }, - { - name: "description__value", - kind: "Text", - enum: undefined, - object_kind: undefined, - description: undefined, - }, - ], - order_weight: 6000, - }, - { - name: "related_nodes", - label: "Related nodes", - cardinality: "many", - peer: "TestNode2", - optional: false, - kind: "Generic", - }, - { - name: "related_nodes_2", - label: "Related nodes", - cardinality: "many", - peer: "TestNode2", - optional: false, - kind: "Component", - }, - ], - label: "Account Token", - inherit_from: [], - groups: [], - branch: true, - filters: [ - { - name: "ids", - kind: "Text", - enum: undefined, - object_kind: undefined, - description: undefined, - }, - { - name: "name__value", - kind: "Text", - enum: undefined, - object_kind: undefined, - description: undefined, - }, - { - name: "token__value", - kind: "Text", - enum: undefined, - object_kind: undefined, - description: undefined, - }, - ], - kind: "InternalAccountToken", - }, -]; - -// Same schema but with a different name to be allowed to test it even if in the MENU_EXCLUDELIST constant -export const accountTokenDetailsMocksSchemaBIS: iNodeSchema[] = [ - { - id: accountTokenId, - name: "AccountTokenBis", - namespace: "Internal", - description: "Token for User Account", - default_filter: "token__value", - order_by: undefined, - display_labels: ["token__value"], - attributes: [ - { - id: "a860d35c-76e0-4e07-a76c-ce36948a6464", - name: "name", - kind: "Text", - namespace: "Attribute", - label: "Name", - description: undefined, - default_value: undefined, - enum: undefined, - regex: undefined, - max_length: undefined, - min_length: undefined, - inherited: false, - unique: false, - branch: true, - optional: true, - order_weight: 1000, - }, - { - id: "dc040126-b39b-4522-a147-9cbd138f4464", - name: "token", - kind: "Text", - namespace: "Attribute", - label: "Token", - description: undefined, - default_value: undefined, - enum: undefined, - regex: undefined, - max_length: undefined, - min_length: undefined, - inherited: false, - unique: true, - branch: true, - optional: false, - order_weight: 2000, - }, - { - id: "5f156988-5e99-4dff-b3ee-30120a95d344", - name: "expiration", - kind: "DateTime", - namespace: "Attribute", - label: "Expiration", - description: undefined, - default_value: undefined, - enum: undefined, - regex: undefined, - max_length: undefined, - min_length: undefined, - inherited: false, - unique: false, - branch: true, - optional: true, - order_weight: 3000, - }, - ], - relationships: [ - { - id: "8ed7a95f-5b49-4df4-b901-c8cd1d9e6430", - name: "account", - peer: "CoreAccount", - kind: "Generic", - label: "Account", - description: undefined, - identifier: "coreaccount__internalaccounttoken", - inherited: false, - cardinality: "one", - branch: true, - optional: false, - filters: [ - { - name: "id", - kind: "Text", - enum: undefined, - object_kind: undefined, - description: undefined, - }, - { - name: "name__value", - kind: "Text", - enum: undefined, - object_kind: undefined, - description: undefined, - }, - { - name: "label__value", - kind: "Text", - enum: undefined, - object_kind: undefined, - description: undefined, - }, - { - name: "description__value", - kind: "Text", - enum: undefined, - object_kind: undefined, - description: undefined, - }, - { - name: "type__value", - kind: "Text", - enum: ["User", "Script", "Bot", "Git"], - object_kind: undefined, - description: undefined, - }, - { - name: "role__value", - kind: "Text", - enum: ["admin", "read-only", "read-write"], - object_kind: undefined, - description: undefined, - }, - ], - order_weight: 4000, - }, - { - id: "78a226e0-7670-4e6b-aac1-77aacfb406d0", - name: "member_of_groups", - peer: "CoreGroup", - kind: "Group", - label: "Member Of Groups", - description: undefined, - identifier: "group_member", - inherited: false, - cardinality: "many", - branch: true, - optional: true, - filters: [ - { - name: "id", - kind: "Text", - enum: undefined, - object_kind: undefined, - description: undefined, - }, - { - name: "name__value", - kind: "Text", - enum: undefined, - object_kind: undefined, - description: undefined, - }, - { - name: "label__value", - kind: "Text", - enum: undefined, - object_kind: undefined, - description: undefined, - }, - { - name: "description__value", - kind: "Text", - enum: undefined, - object_kind: undefined, - description: undefined, - }, - ], - order_weight: 5000, - }, - { - id: "ee77fcc3-d6fc-4091-a17f-739c3039795f", - name: "subscriber_of_groups", - peer: "CoreGroup", - kind: "Group", - label: "Subscriber Of Groups", - description: undefined, - identifier: "group_subscriber", - inherited: false, - cardinality: "many", - branch: true, - optional: true, - filters: [ - { - name: "id", - kind: "Text", - enum: undefined, - object_kind: undefined, - description: undefined, - }, - { - name: "name__value", - kind: "Text", - enum: undefined, - object_kind: undefined, - description: undefined, - }, - { - name: "label__value", - kind: "Text", - enum: undefined, - object_kind: undefined, - description: undefined, - }, - { - name: "description__value", - kind: "Text", - enum: undefined, - object_kind: undefined, - description: undefined, - }, - ], - order_weight: 6000, - }, - ], - label: "Account Token", - inherit_from: [], - groups: [], - branch: true, - filters: [ - { - name: "ids", - kind: "Text", - enum: undefined, - object_kind: undefined, - description: undefined, - }, - { - name: "name__value", - kind: "Text", - enum: undefined, - object_kind: undefined, - description: undefined, - }, - { - name: "token__value", - kind: "Text", - enum: undefined, - object_kind: undefined, - description: undefined, - }, - ], - kind: "InternalAccountTokenBis", - }, -]; - -export const accountTokenDetailsMocksQuery = ` -query InternalAccountToken { - InternalAccountToken (ids: ["${accountTokenId}"]) { - edges { - node { - id - display_label - - name { - value - updated_at - is_protected - is_visible - source { - id - display_label - __typename - } - owner { - id - display_label - __typename - } - } - token { - value - updated_at - is_protected - is_visible - source { - id - display_label - __typename - } - owner { - id - display_label - __typename - } - } - expiration { - value - updated_at - is_protected - is_visible - source { - id - display_label - __typename - } - owner { - id - display_label - __typename - } - } - - - } - } - } -} -`; - -export const accountTokenDetailsMocksQueryBis = ` -query InternalAccountTokenBis { - InternalAccountTokenBis (ids: ["${accountTokenId}"]) { - edges { - node { - id - display_label - - name { - value - updated_at - is_protected - is_visible - source { - id - display_label - __typename - } - owner { - id - display_label - __typename - } - } - token { - value - updated_at - is_protected - is_visible - source { - id - display_label - __typename - } - owner { - id - display_label - __typename - } - } - expiration { - value - updated_at - is_protected - is_visible - source { - id - display_label - __typename - } - owner { - id - display_label - __typename - } - } - account { - node { - id - display_label - __typename - } - properties { - updated_at - is_protected - is_visible - source { - id - display_label - __typename - } - owner { - id - display_label - __typename - } - __typename - } - } - } - } - } - InfrahubTask(related_node__ids: ["${accountTokenId}"]) { - count - } -} -`; - -export const accountTokenDetailsMocksData = { - InternalAccountToken: { - edges: [ - { - node: { - id: accountTokenId, - display_label: "06438eb2-8019-4776-878c-0941b1f1d1ec", - name: { - value: null, - updated_at: "2023-07-12T15:22:03.351221+00:00", - is_protected: false, - is_visible: true, - source: null, - owner: null, - __typename: "TextAttribute", - }, - token: { - value: "06438eb2-8019-4776-878c-0941b1f1d1ec", - updated_at: "2023-07-12T15:22:03.351221+00:00", - is_protected: false, - is_visible: true, - source: null, - owner: null, - __typename: "TextAttribute", - }, - expiration: { - value: "", - updated_at: "2023-07-13T06:42:11.613885+00:00", - is_protected: false, - is_visible: true, - source: null, - owner: null, - __typename: "TextAttribute", - }, - __typename: "InternalAccountToken", - }, - __typename: "EdgedInternalAccountToken", - }, - ], - __typename: "PaginatedInternalAccountToken", - }, -}; - -export const accountTokenDetailsMocksDataBis = { - InternalAccountTokenBis: { - edges: [ - { - node: { - id: accountTokenId, - display_label: "06438eb2-8019-4776-878c-0941b1f1d1ec", - name: { - value: null, - updated_at: "2023-07-12T15:22:03.351221+00:00", - is_protected: false, - is_visible: true, - source: null, - owner: null, - __typename: "TextAttribute", - }, - token: { - value: "06438eb2-8019-4776-878c-0941b1f1d1ec", - updated_at: "2023-07-12T15:22:03.351221+00:00", - is_protected: false, - is_visible: true, - source: null, - owner: null, - __typename: "TextAttribute", - }, - expiration: { - value: "", - updated_at: "2023-07-13T06:42:11.613885+00:00", - is_protected: false, - is_visible: true, - source: null, - owner: null, - __typename: "TextAttribute", - }, - __typename: "InternalAccountTokenBis", - }, - __typename: "EdgedInternalAccountToken", - }, - ], - __typename: "PaginatedInternalAccountToken", - }, -}; - -export const accountTokenDetailsMocksDataWithDate = { - InternalAccountToken: { - edges: [ - { - node: { - id: accountTokenId, - display_label: "06438eb2-8019-4776-878c-0941b1f1d1ec", - name: { - value: null, - updated_at: "2023-07-12T15:22:03.351221+00:00", - is_protected: false, - is_visible: true, - source: null, - owner: null, - __typename: "TextAttribute", - }, - token: { - value: "06438eb2-8019-4776-878c-0941b1f1d1ec", - updated_at: "2023-07-12T15:22:03.351221+00:00", - is_protected: false, - is_visible: true, - source: null, - owner: null, - __typename: "TextAttribute", - }, - expiration: { - value: accountTokenNewDate, - updated_at: "2023-07-13T06:42:11.613885+00:00", - is_protected: false, - is_visible: true, - source: null, - owner: null, - __typename: "TextAttribute", - }, - __typename: "InternalAccountToken", - }, - __typename: "EdgedInternalAccountToken", - }, - ], - __typename: "PaginatedInternalAccountToken", - }, -}; - -export const accountTokenDetailsMocksDataWithDateBis = { - InternalAccountTokenBis: { - edges: [ - { - node: { - id: accountTokenId, - display_label: "06438eb2-8019-4776-878c-0941b1f1d1ec", - name: { - value: null, - updated_at: "2023-07-12T15:22:03.351221+00:00", - is_protected: false, - is_visible: true, - source: null, - owner: null, - __typename: "TextAttribute", - }, - token: { - value: "06438eb2-8019-4776-878c-0941b1f1d1ec", - updated_at: "2023-07-12T15:22:03.351221+00:00", - is_protected: false, - is_visible: true, - source: null, - owner: null, - __typename: "TextAttribute", - }, - expiration: { - value: accountTokenNewDate, - updated_at: "2023-07-13T06:42:11.613885+00:00", - is_protected: false, - is_visible: true, - source: null, - owner: null, - __typename: "TextAttribute", - }, - __typename: "InternalAccountTokenBis", - }, - __typename: "EdgedInternalAccountToken", - }, - ], - __typename: "PaginatedInternalAccountToken", - }, -}; - -export const accountTokenFormStructure = [ - { - name: "name", - kind: "Text", - type: "text", - label: "Name", - value: null, - options: [], - config: {}, - isOptional: true, - isProtected: false, - isReadOnly: undefined, - isUnique: false, - }, - { - name: "token", - kind: "Text", - type: "text", - label: "Token", - value: "06438eb2-8019-4776-878c-0941b1f1d1ec", - options: [], - config: {}, - isOptional: false, - isProtected: false, - isReadOnly: undefined, - isUnique: true, - }, - { - name: "expiration", - kind: "DateTime", - type: "datepicker", - label: "Expiration", - value: "2023-07-14T22:00:00.000Z", - options: [], - config: {}, - isOptional: true, - isProtected: false, - isReadOnly: undefined, - isUnique: false, - }, - { - name: "account", - kind: "String", - peer: "CoreAccount", - type: "select", - label: "Account", - value: "", - options: [], - config: {}, - isOptional: true, - isProtected: false, - }, - { - name: "related_nodes", - kind: "String", - peer: "TestNode2", - type: "multiselect", - label: "Related nodes", - value: "", - options: [], - config: {}, - isOptional: false, - isProtected: false, - isInherited: false, - parent: false, - }, - { - name: "related_nodes_2", - kind: "String", - peer: "TestNode2", - type: "multiselect", - label: "Related nodes", - value: "", - options: [], - config: {}, - isOptional: false, - isProtected: false, - isInherited: false, - parent: false, - }, -]; - -export const accountTokenDetailsUpdateDataMocksData = { - name: "New name", - token: "06438eb2-8019-4776-878c-0941b1f1d1ec", - expiration: "2023-07-15T22:00:00.000Z", - account: { id: "95b04b43-91de-4e29-844d-5655abe696b5" }, -}; - -export const accountTokenDetailsUpdatesMocksData = { - name: { value: "New name" }, - expiration: { value: "2023-07-15T22:00:00.000Z" }, - account: { id: "95b04b43-91de-4e29-844d-5655abe696b5" }, -}; - -export const accountTokenMocksMutation = ` -mutation InternalAccountTokenUpdate { - InternalAccountTokenUpdate (data: { - id: "${accountTokenId}", - name: { - value: "New name" - }, - expiration: { - value: "2023-07-15T22:00:00.000Z" - }, - account: { - id: "95b04b43-91de-4e29-844d-5655abe696b5" - } -}) { - ok - } -} -`; - -export const accountTokenMocksMutationBis = ` -mutation InternalAccountTokenBisUpdate { - InternalAccountTokenBisUpdate (data: { - id: "${accountTokenId}", - name: { - value: "New name" - }, - expiration: { - value: "2023-07-15T22:00:00.000Z" - }, - account: { - id: "95b04b43-91de-4e29-844d-5655abe696b5" - } -}) { - ok - } -} -`; - -export const accountTokenEditMocksQuery = ` -query InternalAccountToken { - InternalAccountToken (ids: ["${accountTokenId}"]) { - edges { - node { - id - display_label - name { - value - updated_at - is_protected - is_visible - source { - id - display_label - __typename - } - owner { - id - display_label - __typename - } - } - token { - value - updated_at - is_protected - is_visible - source { - id - display_label - __typename - } - owner { - id - display_label - __typename - } - } - expiration { - value - updated_at - is_protected - is_visible - source { - id - display_label - __typename - } - owner { - id - display_label - __typename - } - } - } - } - } - CoreAccount { - edges { - node { - id - display_label - } - } - } - CoreGroup { - edges { - node { - id - display_label - } - } - } - CoreGroup { - edges { - node { - id - display_label - } - } - } -} -`; - -export const accountTokenEditMocksQueryBis = ` -query InternalAccountTokenBis { - InternalAccountTokenBis(ids: ["${accountTokenId}"]) { - edges { - node { - id - display_label - name { - value - updated_at - is_protected - is_visible - source { - id - display_label - __typename - } - owner { - id - display_label - __typename - } - } - token { - value - updated_at - is_protected - is_visible - source { - id - display_label - __typename - } - owner { - id - display_label - __typename - } - } - expiration { - value - updated_at - is_protected - is_visible - source { - id - display_label - __typename - } - owner { - id - display_label - __typename - } - } - account { - node { - id - display_label - __typename - } - properties { - updated_at - is_protected - is_visible - source { - id - display_label - __typename - } - owner { - id - display_label - __typename - } - __typename - } - } - } - } - } -} -`; - -export const accountTokenEditMocksData = { - InternalAccountToken: { - edges: [ - { - node: { - id: accountTokenId, - display_label: "06438eb2-8019-4776-878c-0941b1f1d1ec", - name: { - value: null, - updated_at: "2023-07-12T15:22:03.351221+00:00", - is_protected: false, - is_visible: true, - source: null, - owner: null, - __typename: "TextAttribute", - }, - token: { - value: "06438eb2-8019-4776-878c-0941b1f1d1ec", - updated_at: "2023-07-12T15:22:03.351221+00:00", - is_protected: false, - is_visible: true, - source: null, - owner: null, - __typename: "TextAttribute", - }, - expiration: { - value: "", - updated_at: "2023-07-13T06:42:11.613885+00:00", - is_protected: false, - is_visible: true, - source: null, - owner: null, - __typename: "TextAttribute", - }, - __typename: "InternalAccountToken", - }, - __typename: "EdgedInternalAccountToken", - }, - ], - __typename: "PaginatedInternalAccountToken", - }, -}; - -export const accountTokenEditMocksDataBis = { - InternalAccountTokenBisDetailsAndPeers: { - edges: [ - { - node: { - id: accountTokenId, - display_label: "06438eb2-8019-4776-878c-0941b1f1d1ec", - name: { - value: null, - updated_at: "2023-07-12T15:22:03.351221+00:00", - is_protected: false, - is_visible: true, - source: null, - owner: null, - __typename: "TextAttribute", - }, - token: { - value: "06438eb2-8019-4776-878c-0941b1f1d1ec", - updated_at: "2023-07-12T15:22:03.351221+00:00", - is_protected: false, - is_visible: true, - source: null, - owner: null, - __typename: "TextAttribute", - }, - expiration: { - value: "", - updated_at: "2023-07-13T06:42:11.613885+00:00", - is_protected: false, - is_visible: true, - source: null, - owner: null, - __typename: "TextAttribute", - }, - __typename: "InternalAccountTokenBis", - }, - __typename: "EdgedInternalAccountToken", - }, - ], - __typename: "PaginatedInternalAccountToken", - }, -}; - -export const accountsDropdownOptionsQuery = ` -query DropdownOptions { - CoreAccount { - count - edges { - node { - id - display_label - __typename - } - } - } -} -`; - -export const accountsDropdownOptionsData = { - CoreAccount: { - edges: [ - { - node: { - id: "c75a43b4-1df8-4d8b-894e-9fb684b62f8e", - display_label: "Architecture Team", - __typename: "CoreAccount", - }, - __typename: "EdgedCoreAccount", - }, - { - node: { - id: "4e4ac1bf-3e5c-4c42-808e-c2fdfd684512", - display_label: "Crm Synchronization", - __typename: "CoreAccount", - }, - __typename: "EdgedCoreAccount", - }, - { - node: { - id: "8540d34a-a525-4765-b62e-6ca746e15077", - display_label: "Chloe O'Brian", - __typename: "CoreAccount", - }, - __typename: "EdgedCoreAccount", - }, - { - node: { - id: "68246241-9162-4156-beee-7ba4ed4563e3", - display_label: "David Palmer", - __typename: "CoreAccount", - }, - __typename: "EdgedCoreAccount", - }, - { - node: { - id: "86cdbffb-6bb5-4fcd-808b-fd9ea020fce7", - display_label: "Engineering Team", - __typename: "CoreAccount", - }, - __typename: "EdgedCoreAccount", - }, - { - node: { - id: "65e55704-ba5b-4876-9707-afc5d049424d", - display_label: "Jack Bauer", - __typename: "CoreAccount", - }, - __typename: "EdgedCoreAccount", - }, - { - node: { - id: "f858c0ee-84aa-4f66-a003-2481ca1fd106", - display_label: "Operation Team", - __typename: "CoreAccount", - }, - __typename: "EdgedCoreAccount", - }, - { - node: { - id: "d7f866a8-6b26-4c37-bd79-9082450ca16c", - display_label: "Administrator", - __typename: "CoreAccount", - }, - __typename: "EdgedCoreAccount", - }, - { - node: { - id: "c3412415-707e-4f38-b12a-3a9814483c9f", - display_label: "Pop-Builder", - __typename: "CoreAccount", - }, - __typename: "EdgedCoreAccount", - }, - ], - __typename: "PaginatedCoreAccount", - }, -}; diff --git a/frontend/app/tests/mocks/data/artifact.ts b/frontend/app/tests/mocks/data/artifact.ts index 30f3a49dca..02fcd8ae41 100644 --- a/frontend/app/tests/mocks/data/artifact.ts +++ b/frontend/app/tests/mocks/data/artifact.ts @@ -1,3 +1,5 @@ +import { permissionsAllow } from "./permissions"; + export const artifactThreadSchema = [ { id: "17a28ecc-a776-f84c-3873-c512f0a25a7c", @@ -1476,3 +1478,29 @@ export const artifactWithoutThreadMockData = { __typename: "PaginatedCoreArtifactThread", }, }; + +export const artifactPermissionsQuery = ` +query getObjectPermissions { + CoreThreadComment { + permissions { + edges { + node { + kind + view + create + update + delete + } + } + } + } +} +`; + +export const artifactPermissionsData = { + CoreArtifactThread: { + count: 0, + edges: [], + permissions: permissionsAllow, + }, +}; diff --git a/frontend/app/tests/mocks/data/config.ts b/frontend/app/tests/mocks/data/config.ts index 62bd4f5680..53630e91aa 100644 --- a/frontend/app/tests/mocks/data/config.ts +++ b/frontend/app/tests/mocks/data/config.ts @@ -1,7 +1,12 @@ export const configMocks = { main: { - default_branch: "main", + docs_index_path: "/opt/infrahub/docs/build/search-index.json", internal_address: "http://infrahub-server:8000", + allow_anonymous_access: true, + telemetry_optout: false, + telemetry_endpoint: "https://telemetry.opsmill.cloud/infrahub", + telemetry_interval: 86400, + permission_backends: ["infrahub.permissions.LocalPermissionBackend"], }, logging: { remote: { @@ -18,6 +23,10 @@ export const configMocks = { }, experimental_features: { pull_request: false, - paginated: true, + graphql_enums: false, + }, + sso: { + providers: [], + enabled: false, }, }; diff --git a/frontend/app/tests/mocks/data/data-changes.ts b/frontend/app/tests/mocks/data/data-changes.ts deleted file mode 100644 index 0b67073a2a..0000000000 --- a/frontend/app/tests/mocks/data/data-changes.ts +++ /dev/null @@ -1,1272 +0,0 @@ -export const objectThreadSchema = { - id: "17a53fb0-8191-7969-2d03-c51b08c9b696", - name: "ObjectThread", - namespace: "Core", - description: "A thread related to an object on a proposed change", - default_filter: null, - branch: "agnostic", - order_by: null, - display_labels: null, - attributes: [ - { - id: "17a53fb0-87c9-e1fd-2d03-c5155a15ff42", - name: "object_path", - kind: "Text", - label: "Object Path", - description: null, - default_value: null, - enum: null, - regex: null, - max_length: null, - min_length: null, - read_only: false, - inherited: false, - unique: false, - branch: "agnostic", - optional: false, - order_weight: 1000, - choices: null, - }, - { - id: "17a53fb0-88a1-592b-2d05-c5153ecc3abe", - name: "label", - kind: "Text", - label: "Label", - description: null, - default_value: null, - enum: null, - regex: null, - max_length: null, - min_length: null, - read_only: false, - inherited: true, - unique: false, - branch: "agnostic", - optional: true, - order_weight: 2000, - choices: null, - }, - { - id: "17a53fb0-89bb-c2db-2d00-c5177efd3b50", - name: "resolved", - kind: "Boolean", - label: "Resolved", - description: null, - default_value: false, - enum: null, - regex: null, - max_length: null, - min_length: null, - read_only: false, - inherited: true, - unique: false, - branch: "agnostic", - optional: true, - order_weight: 3000, - choices: null, - }, - { - id: "17a53fb0-8af7-2523-2d0c-c51f25e2d550", - name: "created_at", - kind: "DateTime", - label: "Created At", - description: null, - default_value: null, - enum: null, - regex: null, - max_length: null, - min_length: null, - read_only: false, - inherited: true, - unique: false, - branch: "agnostic", - optional: true, - order_weight: 4000, - choices: null, - }, - ], - relationships: [ - { - id: "17a53fb0-8bd3-600b-2d0e-c5154b38eac6", - name: "change", - peer: "CoreProposedChange", - kind: "Parent", - direction: "bidirectional", - label: "Change", - description: null, - identifier: "proposedchange__thread", - inherited: true, - cardinality: "one", - branch: "agnostic", - optional: false, - 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: "name__values", kind: "Text", enum: null, object_kind: null, description: null }, - { - name: "name__is_visible", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - name: "name__is_protected", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - name: "name__source__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - name: "name__owner__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - name: "source_branch__value", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - name: "source_branch__values", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - name: "source_branch__is_visible", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - name: "source_branch__is_protected", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - name: "source_branch__source__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - name: "source_branch__owner__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - name: "destination_branch__value", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - name: "destination_branch__values", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - name: "destination_branch__is_visible", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - name: "destination_branch__is_protected", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - name: "destination_branch__source__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - name: "destination_branch__owner__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - name: "state__value", - kind: "Text", - enum: ["open", "merged", "closed", "canceled"], - object_kind: null, - description: null, - }, - { name: "state__values", kind: "Text", enum: null, object_kind: null, description: null }, - { - name: "state__is_visible", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - name: "state__is_protected", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - name: "state__source__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - name: "state__owner__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - ], - order_weight: 5000, - }, - { - id: "17a53fb0-8c82-44ab-2d06-c51be086e749", - name: "comments", - peer: "CoreThreadComment", - kind: "Component", - direction: "bidirectional", - label: "Comments", - description: null, - identifier: "thread__threadcomment", - inherited: true, - cardinality: "many", - branch: "agnostic", - optional: true, - filters: [{ name: "ids", kind: "Text", enum: null, object_kind: null, description: null }], - order_weight: 6000, - }, - { - id: "17a53fb0-8cec-f6a2-2d05-c518429232f0", - name: "created_by", - peer: "CoreAccount", - kind: "Generic", - direction: "bidirectional", - label: "Created By", - description: null, - identifier: "coreaccount__corethread", - inherited: true, - cardinality: "one", - branch: "agnostic", - optional: true, - 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: "name__values", kind: "Text", enum: null, object_kind: null, description: null }, - { - name: "name__is_visible", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - name: "name__is_protected", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - name: "name__source__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - name: "name__owner__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { name: "label__value", kind: "Text", enum: null, object_kind: null, description: null }, - { name: "label__values", kind: "Text", enum: null, object_kind: null, description: null }, - { - name: "label__is_visible", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - name: "label__is_protected", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - name: "label__source__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - name: "label__owner__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - name: "description__value", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - name: "description__values", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - name: "description__is_visible", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - name: "description__is_protected", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - name: "description__source__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - name: "description__owner__id", - 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: "type__values", kind: "Text", enum: null, object_kind: null, description: null }, - { - name: "type__is_visible", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - name: "type__is_protected", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - name: "type__source__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - name: "type__owner__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - name: "role__value", - kind: "Text", - enum: ["admin", "read-only", "read-write"], - object_kind: null, - description: null, - }, - { name: "role__values", kind: "Text", enum: null, object_kind: null, description: null }, - { - name: "role__is_visible", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - name: "role__is_protected", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - name: "role__source__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - name: "role__owner__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - ], - order_weight: 7000, - }, - { - id: "17a53fb0-8d5c-751d-2d04-c517647f7a8e", - name: "member_of_groups", - peer: "CoreGroup", - kind: "Group", - direction: "bidirectional", - label: "Member Of Groups", - description: null, - identifier: "group_member", - inherited: false, - cardinality: "many", - branch: "aware", - optional: true, - 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: "name__values", kind: "Text", enum: null, object_kind: null, description: null }, - { - name: "name__is_visible", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - name: "name__is_protected", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - name: "name__source__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - name: "name__owner__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { name: "label__value", kind: "Text", enum: null, object_kind: null, description: null }, - { name: "label__values", kind: "Text", enum: null, object_kind: null, description: null }, - { - name: "label__is_visible", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - name: "label__is_protected", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - name: "label__source__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - name: "label__owner__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - name: "description__value", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - name: "description__values", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - name: "description__is_visible", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - name: "description__is_protected", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - name: "description__source__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - name: "description__owner__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - ], - order_weight: 8000, - }, - { - id: "17a53fb0-8dc4-f2a0-2d00-c517e9f46163", - name: "subscriber_of_groups", - peer: "CoreGroup", - kind: "Group", - direction: "bidirectional", - label: "Subscriber Of Groups", - description: null, - identifier: "group_subscriber", - inherited: false, - cardinality: "many", - branch: "aware", - optional: true, - 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: "name__values", kind: "Text", enum: null, object_kind: null, description: null }, - { - name: "name__is_visible", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - name: "name__is_protected", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - name: "name__source__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - name: "name__owner__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { name: "label__value", kind: "Text", enum: null, object_kind: null, description: null }, - { name: "label__values", kind: "Text", enum: null, object_kind: null, description: null }, - { - name: "label__is_visible", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - name: "label__is_protected", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - name: "label__source__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - name: "label__owner__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - name: "description__value", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - name: "description__values", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - name: "description__is_visible", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - name: "description__is_protected", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - name: "description__source__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - name: "description__owner__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - ], - order_weight: 9000, - }, - ], - 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: "object_path__values", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - name: "object_path__is_visible", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - name: "object_path__is_protected", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - name: "object_path__source__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - name: "object_path__owner__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { name: "label__value", kind: "Text", enum: null, object_kind: null, description: null }, - { name: "label__values", kind: "Text", enum: null, object_kind: null, description: null }, - { - name: "label__is_visible", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - name: "label__is_protected", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { name: "label__source__id", kind: "Text", enum: null, object_kind: null, description: null }, - { name: "label__owner__id", kind: "Text", enum: null, object_kind: null, description: null }, - { - name: "resolved__value", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { name: "resolved__values", kind: "Text", enum: null, object_kind: null, description: null }, - { - name: "resolved__is_visible", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - name: "resolved__is_protected", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - name: "resolved__source__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - name: "resolved__owner__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { name: "any__value", kind: "Text", enum: null, object_kind: null, description: null }, - { - name: "any__is_visible", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - name: "any__is_protected", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { name: "any__source__id", kind: "Text", enum: null, object_kind: null, description: null }, - { name: "any__owner__id", kind: "Text", enum: null, object_kind: null, description: null }, - { - name: "change__ids", - kind: "Text", - enum: null, - object_kind: "CoreProposedChange", - description: null, - }, - { - name: "change__name__value", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - name: "change__name__values", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - name: "change__name__is_visible", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - name: "change__name__is_protected", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - name: "change__name__source__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - name: "change__name__owner__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - name: "change__source_branch__value", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - name: "change__source_branch__values", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - name: "change__source_branch__is_visible", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - name: "change__source_branch__is_protected", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - name: "change__source_branch__source__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - name: "change__source_branch__owner__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - name: "change__destination_branch__value", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - name: "change__destination_branch__values", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - name: "change__destination_branch__is_visible", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - name: "change__destination_branch__is_protected", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - name: "change__destination_branch__source__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - name: "change__destination_branch__owner__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - name: "change__state__value", - kind: "Text", - enum: ["open", "merged", "closed", "canceled"], - object_kind: null, - description: null, - }, - { - name: "change__state__values", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - name: "change__state__is_visible", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - name: "change__state__is_protected", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - name: "change__state__source__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - name: "change__state__owner__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - ], - include_in_menu: false, - menu_placement: null, - icon: null, - label: "Thread - Object", - inherit_from: ["CoreThread"], - groups: [], - kind: "CoreObjectThread", - hash: "5a92f8a14bf7c4dd08da69e168d1c4b7", -}; - -export const DataDiffProposedChangesState = { - id: "17a54304-4b2a-264a-2d02-c51eae9cdde6", - display_label: "pc-data-changes", - __typename: "CoreProposedChange", - _updated_at: "2023-12-29T09:21:59.750848+00:00", - name: { - value: "pc-data-changes", - __typename: "TextAttribute", - }, - source_branch: { - value: "branch-data-changes", - __typename: "TextAttribute", - }, - destination_branch: { - value: "main", - __typename: "TextAttribute", - }, - state: { - value: "open", - __typename: "TextAttribute", - }, - approved_by: { - edges: [], - __typename: "NestedPaginatedCoreAccount", - }, - reviewers: { - edges: [], - __typename: "NestedPaginatedCoreAccount", - }, - created_by: { - node: { - id: "17a53fb1-512b-ae9e-2d0d-c51fade6639c", - display_label: "Admin", - __typename: "CoreAccount", - }, - __typename: "NestedEdgedCoreAccount", - }, -}; - -export const getAllCoreObjectThreadMockQuery = `query getThreadsAndChecksForCoreObjectThread { - CoreObjectThread( - change__ids: "1cec1fe9-fcc4-4f5b-af30-9d661de65bd8" - ) { - count - edges { - node { - __typename - id - object_path { - value - } - comments { - count - } - } - } - } -}`; - -export const getAllCoreObjectThreadMockData = { - data: { - CoreObjectThread: { - count: 1, - edges: [ - { - node: { - __typename: "CoreObjectThread", - id: "17a54a5c-55d1-0620-2d0a-c51d250954a9", - object_path: { - value: "data/17a53fbd-e27b-784b-393c-c5127a1b1be3", - __typename: "TextAttribute", - }, - comments: { - count: 1, - __typename: "NestedPaginatedCoreThreadComment", - }, - }, - __typename: "EdgedCoreObjectThread", - }, - ], - __typename: "PaginatedCoreObjectThread", - }, - }, -}; - -export const getCoreObjectThreadMockQuery = `query getProposedChangesThreadsForCoreObjectThread { - CoreObjectThread( - change__ids: "1cec1fe9-fcc4-4f5b-af30-9d661de65bd8" - object_path__value: "data/17a53fbd-e27b-784b-393c-c5127a1b1be3" - ) { - count - edges { - node { - __typename - id - comments { - count - } - } - } - } -}`; - -export const getCoreObjectThreadMockData = { - data: { - CoreObjectThread: { - count: 1, - edges: [ - { - node: { - __typename: "CoreObjectThread", - id: "17a54a5c-55d1-0620-2d0a-c51d250954a9", - object_path: { - value: "data/17a53fbd-e27b-784b-393c-c5127a1b1be3", - __typename: "TextAttribute", - }, - comments: { - count: 1, - __typename: "NestedPaginatedCoreThreadComment", - }, - }, - __typename: "EdgedCoreObjectThread", - }, - ], - __typename: "PaginatedCoreObjectThread", - }, - }, -}; - -export const getCoreObjectWithoutThreadMockData = { - data: { - CoreObjectThread: { - count: 0, - edges: [], - __typename: "PaginatedCoreObjectThread", - }, - }, -}; - -export const getProposedChangesCommentsMockQuery = `query getProposedChangesObjectThreadCommentsForCoreObjectThread{ - CoreObjectThread( - change__ids: "1cec1fe9-fcc4-4f5b-af30-9d661de65bd8" - object_path__value: "data/17a53fbd-e27b-784b-393c-c5127a1b1be3" - ) { - count - edges { - node { - __typename - id - display_label - resolved { - value - } - created_by { - node { - display_label - } - } - comments { - count - edges { - node { - id - display_label - created_by { - node { - display_label - } - } - created_at { - value - } - text { - value - } - } - } - } - } - } - } -}`; - -export const createThreadMockData = { - data: { - CoreObjectThreadCreate: { - object: { - id: "17a55dee-acc3-3df9-2d04-c51c5bd2161d", - display_label: "CoreObjectThread(ID: 17a55dee-acc3-3df9-2d04-c51c5bd2161d)", - __typename: "CoreObjectThread", - }, - ok: true, - __typename: "CoreObjectThreadCreate", - }, - }, -}; - -export const getProposedChangesCommentsMockData = { - data: { - CoreObjectThread: { - count: 1, - edges: [ - { - node: { - __typename: "CoreObjectThread", - id: "17a55fb5-7d2f-f712-2d0e-c5154fe1bf17", - display_label: "CoreObjectThread(ID: 17a55fb5-7d2f-f712-2d0e-c5154fe1bf17)", - resolved: { - value: false, - __typename: "CheckboxAttribute", - }, - created_by: { - node: null, - __typename: "NestedEdgedCoreAccount", - }, - comments: { - count: 1, - edges: [ - { - node: { - id: "17a55fb5-82a1-197c-2d0e-c512237d7fa5", - display_label: "new comment", - created_by: { - node: { - display_label: "Admin", - __typename: "CoreAccount", - }, - __typename: "NestedEdgedCoreAccount", - }, - created_at: { - value: "2023-12-29T19:07:47+01:00", - __typename: "TextAttribute", - }, - text: { - value: "new comment", - __typename: "TextAttribute", - }, - __typename: "CoreThreadComment", - }, - __typename: "NestedEdgedCoreThreadComment", - }, - ], - __typename: "NestedPaginatedCoreThreadComment", - }, - }, - __typename: "EdgedCoreObjectThread", - }, - ], - __typename: "PaginatedCoreObjectThread", - }, - }, -}; - -export const createThreadCommentMockQuery = `mutation CoreThreadCommentCreate { - CoreThreadCommentCreate( - data: {text: {value: "new reply"}, thread: {id: "17a55fb5-7d2f-f712-2d0e-c5154fe1bf17"}, created_by: {id: "d07bb58e-8394-4053-a198-9cef84e7d6c0"}, created_at: {value: "2023-12-24T12:24:36+01:00"}} - ) { - object { - id - display_label - __typename - } - ok - __typename - } -}`; diff --git a/frontend/app/tests/mocks/data/devices.ts b/frontend/app/tests/mocks/data/devices.ts index f3ab414537..95648b5eb5 100644 --- a/frontend/app/tests/mocks/data/devices.ts +++ b/frontend/app/tests/mocks/data/devices.ts @@ -1,3 +1,5 @@ +import { permissionsAllow } from "./permissions"; + export const deviceDetailsMocksId = "bd3110b9-5923-45e9-b643-776b8151c074"; export const deviceSiteMocksId = "06c3ab9e-535e-41af-bf4b-ec9134cc4353"; export const deviceSiteOwnerMocksId = "1790adb9-7030-259c-35c7-d8e28044d715"; @@ -1348,6 +1350,17 @@ query InfraDevice { } } } + permissions { + edges { + node { + kind + view + create + update + delete + } + } + } } InfrahubTask(related_node__ids: ["${deviceDetailsMocksId}"]) { count @@ -1564,6 +1577,7 @@ export const deviceDetailsMocksData = { __typename: "EdgedInfraDevice", }, ], + permissions: permissionsAllow, __typename: "PaginatedInfraDevice", }, }; @@ -1722,3 +1736,43 @@ mutation InfraDeviceUpdate { } } `; + +export const getPermissionsQuery = `query getObjectPermissions { + InfraDevice { + permissions { + edges { + node { + kind + view + create + update + delete + } + } + } + } +}`; + +export const getPermissionsData = { + data: { + InfraDevice: { + permissions: { + edges: [ + { + node: { + kind: "InfraDevice", + view: "ALLOW", + create: "ALLOW", + update: "ALLOW", + delete: "ALLOW", + __typename: "ObjectPermission", + }, + __typename: "ObjectPermissionNode", + }, + ], + __typename: "PaginatedObjectPermission", + }, + __typename: "PaginatedInfraDevice", + }, + }, +}; diff --git a/frontend/app/tests/mocks/data/generics.ts b/frontend/app/tests/mocks/data/generics.ts deleted file mode 100644 index 582065a6be..0000000000 --- a/frontend/app/tests/mocks/data/generics.ts +++ /dev/null @@ -1,257 +0,0 @@ -import { iGenericSchema } from "../../../src/state/atoms/schema.atom"; -import { deviceDetailsMocksGenerics } from "./devices"; - -export const genericsMocks: iGenericSchema[] = [ - { - id: "da5ab3da-621b-4e90-b543-f6f8eb9860a2", - name: "Endpoint", - namespace: "Infra", - description: undefined, - default_filter: undefined, - order_by: undefined, - display_labels: undefined, - attributes: [], - relationships: [ - { - id: "41b42400-daf4-4d07-a270-bde920061c01", - name: "connected_endpoint", - peer: "InfraEndpoint", - kind: "Attribute", - label: "Connected Endpoint", - description: undefined, - identifier: "connected__endpoint", - inherited: false, - cardinality: "one", - branch: true, - optional: true, - filters: [], - order_weight: 1000, - }, - ], - branch: true, - label: "Endpoint", - used_by: ["InfraCircuitEndpoint", "InfraInterfaceL2", "InfraInterfaceL3"], - kind: "InfraEndpoint", - }, - { - id: "76c153b2-d580-43ec-b4de-50ec67ee1881", - name: "Group", - namespace: "Core", - description: undefined, - default_filter: "name__value", - order_by: ["name__value"], - display_labels: ["label__value"], - attributes: [ - { - id: "001e93e4-b02a-4b3a-ba0c-626915395358", - name: "name", - kind: "Text", - namespace: "Attribute", - label: "Name", - description: undefined, - default_value: undefined, - enum: undefined, - regex: undefined, - max_length: undefined, - min_length: undefined, - inherited: false, - unique: true, - branch: true, - optional: false, - order_weight: 1000, - }, - { - id: "93a01cde-b121-43ec-ad66-92aa868e6c0d", - name: "label", - kind: "Text", - namespace: "Attribute", - label: "Label", - description: undefined, - default_value: undefined, - enum: undefined, - regex: undefined, - max_length: undefined, - min_length: undefined, - inherited: false, - unique: false, - branch: true, - optional: true, - order_weight: 2000, - }, - { - id: "58d1777f-13bc-4937-a3ca-7d889fd7def4", - name: "description", - kind: "Text", - namespace: "Attribute", - label: "Description", - description: undefined, - default_value: undefined, - enum: undefined, - regex: undefined, - max_length: undefined, - min_length: undefined, - inherited: false, - unique: false, - branch: true, - optional: true, - order_weight: 3000, - }, - ], - relationships: [ - { - id: "7fdb61ff-95d4-40d0-bb6a-8e6a87bb77db", - name: "members", - peer: "CoreNode", - kind: "Generic", - label: "Members", - description: undefined, - identifier: "group_member", - inherited: false, - cardinality: "many", - branch: true, - optional: true, - filters: [], - order_weight: 4000, - }, - { - id: "f3b41911-e110-4cf7-b7a9-6708702d0764", - name: "subscribers", - peer: "CoreNode", - kind: "Generic", - label: "Subscribers", - description: undefined, - identifier: "group_subscriber", - inherited: false, - cardinality: "many", - branch: true, - optional: true, - filters: [], - order_weight: 5000, - }, - ], - branch: true, - label: "Group", - used_by: ["CoreStandardGroup"], - kind: "CoreGroup", - }, - deviceDetailsMocksGenerics[0], - { - id: "f069caa4-0f8b-425b-bc18-ef57d69e1ce2", - name: "Node", - namespace: "Core", - description: undefined, - default_filter: undefined, - order_by: undefined, - display_labels: undefined, - attributes: [], - relationships: [], - branch: true, - label: "Node", - used_by: [], - kind: "CoreNode", - }, - { - id: "757c1d52-ba8a-4686-9e2d-f09ab41336c6", - name: "Owner", - namespace: "Lineage", - description: undefined, - default_filter: undefined, - order_by: undefined, - display_labels: ["name__value"], - attributes: [ - { - id: "2f4d3921-3ea6-43af-bb5c-45b7019c32ba", - name: "name", - kind: "Text", - namespace: "Attribute", - label: "Name", - description: undefined, - default_value: undefined, - enum: undefined, - regex: undefined, - max_length: undefined, - min_length: undefined, - inherited: false, - unique: true, - branch: true, - optional: false, - order_weight: 1000, - }, - { - id: "b2aa80d8-2a9f-4493-a7aa-e6f4689a21be", - name: "description", - kind: "Text", - namespace: "Attribute", - label: "Description", - description: undefined, - default_value: undefined, - enum: undefined, - regex: undefined, - max_length: undefined, - min_length: undefined, - inherited: false, - unique: false, - branch: true, - optional: true, - order_weight: 2000, - }, - ], - relationships: [], - branch: true, - label: "Owner", - used_by: ["CoreAccount", "CoreRepository"], - kind: "LineageOwner", - }, - { - id: "1260ab98-5d35-4479-96dd-3d7ad261a19c", - name: "Source", - namespace: "Lineage", - description: "Any Entities that stores or produces data.", - default_filter: undefined, - order_by: undefined, - display_labels: ["name__value"], - attributes: [ - { - id: "3fa53678-7023-4413-8cb5-e9bae1ef7fe0", - name: "name", - kind: "Text", - namespace: "Attribute", - label: "Name", - description: undefined, - default_value: undefined, - enum: undefined, - regex: undefined, - max_length: undefined, - min_length: undefined, - inherited: false, - unique: true, - branch: true, - optional: false, - order_weight: 1000, - }, - { - id: "739d1b9e-6605-498d-b2c3-cb5aaba39789", - name: "description", - kind: "Text", - namespace: "Attribute", - label: "Description", - description: undefined, - default_value: undefined, - enum: undefined, - regex: undefined, - max_length: undefined, - min_length: undefined, - inherited: false, - unique: false, - branch: true, - optional: true, - order_weight: 2000, - }, - ], - relationships: [], - branch: true, - label: "Source", - used_by: ["CoreAccount", "CoreRepository"], - kind: "LineageSource", - }, -]; diff --git a/frontend/app/tests/mocks/data/graphqlQueries.ts b/frontend/app/tests/mocks/data/graphqlQueries.ts index 3d29b9ad5f..0d8d94457e 100644 --- a/frontend/app/tests/mocks/data/graphqlQueries.ts +++ b/frontend/app/tests/mocks/data/graphqlQueries.ts @@ -1,3 +1,5 @@ +import { permissionsAllow } from "./permissions"; + export const graphqlQueriesMocksQuery = ` query CoreGraphQLQuery($offset: Int, $limit: Int) { CoreGraphQLQuery(offset: $offset,limit: $limit) { @@ -29,6 +31,17 @@ query CoreGraphQLQuery($offset: Int, $limit: Int) { } } } + permissions { + edges { + node { + kind + view + create + update + delete + } + } + } } } `; @@ -64,6 +77,17 @@ query CoreGraphQLQuery($offset: Int, $limit: Int) { } } } + permissions { + edges { + node { + kind + view + create + update + delete + } + } + } } } `; @@ -603,6 +627,7 @@ export const graphqlQueriesMocksData = { __typename: "EdgedGraphQLQuery", }, ], + permissions: permissionsAllow, __typename: "PaginatedGraphQLQuery", }, }; @@ -1091,6 +1116,7 @@ export const graphqlQueriesMocksDataDeleted = { __typename: "EdgedGraphQLQuery", }, ], + permissions: permissionsAllow, __typename: "PaginatedGraphQLQuery", }, }; diff --git a/frontend/app/tests/mocks/data/ip-address.ts b/frontend/app/tests/mocks/data/ip-address.ts index 905db8e082..321774a595 100644 --- a/frontend/app/tests/mocks/data/ip-address.ts +++ b/frontend/app/tests/mocks/data/ip-address.ts @@ -1,1232 +1,3 @@ -export const ipAddressMocksSchema = { - id: "17d341e9-2da6-9005-2e36-c51e869c589a", - state: "present", - name: "IPAddress", - namespace: "Builtin", - description: "IPv6 or IPv4 address", - label: "IP Address", - branch: "aware", - default_filter: "address__value", - human_friendly_id: null, - display_labels: ["address__value"], - include_in_menu: false, - menu_placement: null, - icon: "mdi:ip-outline", - order_by: ["address__version", "address__binary_address"], - uniqueness_constraints: null, - documentation: null, - filters: [ - { - id: null, - state: "present", - name: "ids", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "description__value", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "description__values", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "description__is_visible", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "description__is_protected", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "description__source__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "description__owner__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "any__value", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "any__is_visible", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "any__is_protected", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "any__source__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "any__owner__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - ], - attributes: [ - { - id: "17d341e9-3642-67c7-2e3a-c51a0386c4b6", - state: "present", - name: "address", - kind: "IPHost", - enum: null, - choices: null, - regex: null, - max_length: null, - min_length: null, - label: "Address", - description: null, - read_only: false, - unique: false, - optional: false, - branch: "aware", - order_weight: 1000, - default_value: null, - inherited: false, - allow_override: "any", - }, - { - id: "17d341e9-52aa-5f50-2e3e-c51f833e62e1", - state: "present", - name: "description", - kind: "Text", - enum: null, - choices: null, - regex: null, - max_length: null, - min_length: null, - label: "Description", - description: null, - read_only: false, - unique: false, - optional: true, - branch: "aware", - order_weight: 2000, - default_value: null, - inherited: false, - allow_override: "any", - }, - ], - relationships: [ - { - id: "17d341e9-5367-30e1-2e3b-c513fa8e87a6", - state: "present", - name: "ip_namespace", - peer: "BuiltinIPNamespace", - kind: "Generic", - label: "IP Namespace", - description: null, - identifier: "ip_namespace__ip_address", - cardinality: "one", - min_count: 0, - max_count: 1, - order_weight: 3000, - optional: true, - branch: "aware", - inherited: false, - direction: "bidirectional", - hierarchical: null, - filters: [ - { - id: null, - state: "present", - name: "ids", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "name__value", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "name__values", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "name__is_visible", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "name__is_protected", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "name__source__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "name__owner__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "description__value", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "description__values", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "description__is_visible", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "description__is_protected", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "description__source__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "description__owner__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - ], - on_delete: "no-action", - allow_override: "none", - read_only: false, - }, - { - id: "17d341e9-5413-cb14-2e3c-c51f14a85af6", - state: "present", - name: "ip_prefix", - peer: "BuiltinIPPrefix", - kind: "Generic", - label: "IP Prefix", - description: null, - identifier: "ip_prefix__ip_address", - cardinality: "one", - min_count: 0, - max_count: 1, - order_weight: 4000, - optional: true, - branch: "aware", - inherited: false, - direction: "bidirectional", - hierarchical: null, - filters: [ - { - id: null, - state: "present", - name: "ids", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "description__value", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "description__values", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "description__is_visible", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "description__is_protected", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "description__source__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "description__owner__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "member_type__value", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "member_type__values", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "member_type__is_visible", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "member_type__is_protected", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "member_type__source__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "member_type__owner__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "is_pool__value", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "is_pool__values", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "is_pool__is_visible", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "is_pool__is_protected", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "is_pool__source__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "is_pool__owner__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "is_top_level__value", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "is_top_level__values", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "is_top_level__is_visible", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "is_top_level__is_protected", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "is_top_level__source__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "is_top_level__owner__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "utilization__value", - kind: "Number", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "utilization__values", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "utilization__is_visible", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "utilization__is_protected", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "utilization__source__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "utilization__owner__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "netmask__value", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "netmask__values", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "netmask__is_visible", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "netmask__is_protected", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "netmask__source__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "netmask__owner__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "hostmask__value", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "hostmask__values", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "hostmask__is_visible", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "hostmask__is_protected", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "hostmask__source__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "hostmask__owner__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "network_address__value", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "network_address__values", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "network_address__is_visible", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "network_address__is_protected", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "network_address__source__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "network_address__owner__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "broadcast_address__value", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "broadcast_address__values", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "broadcast_address__is_visible", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "broadcast_address__is_protected", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "broadcast_address__source__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "broadcast_address__owner__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - ], - on_delete: "no-action", - allow_override: "none", - read_only: true, - }, - { - id: "17d341e9-54cd-423e-2e3f-c513c6025372", - state: "present", - name: "member_of_groups", - peer: "CoreGroup", - kind: "Group", - label: "Member Of Groups", - description: null, - identifier: "group_member", - cardinality: "many", - min_count: 0, - max_count: 0, - order_weight: 5000, - optional: true, - branch: "aware", - inherited: false, - direction: "bidirectional", - hierarchical: null, - filters: [ - { - id: null, - state: "present", - name: "ids", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "name__value", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "name__values", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "name__is_visible", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "name__is_protected", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "name__source__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "name__owner__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "label__value", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "label__values", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "label__is_visible", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "label__is_protected", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "label__source__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "label__owner__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "description__value", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "description__values", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "description__is_visible", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "description__is_protected", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "description__source__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "description__owner__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - ], - on_delete: "no-action", - allow_override: "any", - read_only: false, - }, - { - id: "17d341e9-5585-ae7c-2e3f-c515f713fd81", - state: "present", - name: "subscriber_of_groups", - peer: "CoreGroup", - kind: "Group", - label: "Subscriber Of Groups", - description: null, - identifier: "group_subscriber", - cardinality: "many", - min_count: 0, - max_count: 0, - order_weight: 6000, - optional: true, - branch: "aware", - inherited: false, - direction: "bidirectional", - hierarchical: null, - filters: [ - { - id: null, - state: "present", - name: "ids", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "name__value", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "name__values", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "name__is_visible", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "name__is_protected", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "name__source__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "name__owner__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "label__value", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "label__values", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "label__is_visible", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "label__is_protected", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "label__source__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "label__owner__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "description__value", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "description__values", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "description__is_visible", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "description__is_protected", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "description__source__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - { - id: null, - state: "present", - name: "description__owner__id", - kind: "Text", - enum: null, - object_kind: null, - description: null, - }, - ], - on_delete: "no-action", - allow_override: "any", - read_only: false, - }, - ], - hierarchical: false, - used_by: ["IpamIPAddress"], - kind: "BuiltinIPAddress", - hash: "925227d93c202e6c3348bbc466634deb", -}; - export const ipamIpAddressMocksSchema = { id: "17d341ee-6777-7f5e-2e3f-c51f119691e0", state: "present", diff --git a/frontend/app/tests/mocks/data/permissions.ts b/frontend/app/tests/mocks/data/permissions.ts new file mode 100644 index 0000000000..f4a2bc33f6 --- /dev/null +++ b/frontend/app/tests/mocks/data/permissions.ts @@ -0,0 +1,15 @@ +export const permissionsAllow = { + edges: [ + { + node: { + kind: "InfraDevice", + view: "ALLOW", + create: "ALLOW", + update: "ALLOW", + delete: "ALLOW", + __typename: "ObjectPermission", + }, + __typename: "ObjectPermissionNode", + }, + ], +}; diff --git a/frontend/app/tests/mocks/data/task.ts b/frontend/app/tests/mocks/data/task.ts deleted file mode 100644 index 0f7a092a62..0000000000 --- a/frontend/app/tests/mocks/data/task.ts +++ /dev/null @@ -1,183 +0,0 @@ -export const taskMocksSchema = [ - { - id: "8a4e2579-c300-48e1-b703-022bf6d224df", - name: "Task", - namespace: "Test", - description: "Issue tracker", - default_filter: "name__value", - order_by: ["name__value"], - display_labels: ["name__value"], - attributes: [ - { - id: "30d6f53c-7c97-473e-b0cb-b8f1e1d02f2e", - name: "name", - kind: "Text", - namespace: "Attribute", - label: "Name", - description: null, - default_value: null, - enum: null, - regex: null, - max_length: null, - min_length: null, - inherited: false, - unique: true, - branch: true, - optional: false, - order_weight: 1000, - }, - { - id: "ad1a60f6-efce-445b-9995-b760a6f73f8c", - name: "description", - kind: "TextArea", - namespace: "Attribute", - label: "Description", - description: null, - default_value: null, - enum: null, - regex: null, - max_length: null, - min_length: null, - inherited: false, - unique: false, - branch: true, - optional: true, - order_weight: 2000, - }, - { - id: "374125ad-4bfe-49ac-b6d2-059b7bba19ce", - name: "completed", - kind: "Boolean", - namespace: "Attribute", - label: "Completed", - description: null, - default_value: false, - enum: null, - regex: null, - max_length: null, - min_length: null, - inherited: false, - unique: false, - branch: true, - optional: false, - order_weight: 3000, - }, - ], - relationships: [ - { - id: "60872203-876d-4ac2-82fe-ff31394f0578", - name: "member_of_groups", - peer: "CoreGroup", - kind: "Group", - label: "Member Of Groups", - description: null, - identifier: "group_member", - inherited: false, - cardinality: "many", - branch: true, - 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, - }, - { - id: "1e8d92b9-e101-4445-9b47-92fa17d5f15d", - name: "subscriber_of_groups", - peer: "CoreGroup", - kind: "Group", - label: "Subscriber Of Groups", - description: null, - identifier: "group_subscriber", - inherited: false, - cardinality: "many", - branch: true, - 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: "Task", - inherit_from: [], - groups: [], - branch: true, - 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: "completed__value", - kind: "Boolean", - enum: null, - object_kind: null, - description: null, - }, - ], - kind: "TestTask", - }, -]; - -export const taskMocksQuery = ` -query TestTask { - TestTask(offset: 0,limit: 10) { - count - edges { - node { - id - display_label - __typename - - name { - value - } - description { - value - } - completed { - value - } - - } - } - } -} -`; - -export const taskMocksData = { - TestTask: { - count: 1, - edges: [ - { - node: { - id: "c5b3043d-bb86-46ac-8790-227a61de3305", - display_label: "aze", - __typename: "TestTask", - name: { value: "aze", __typename: "TextAttribute" }, - description: { value: null, __typename: "TextAttribute" }, - completed: { value: false, __typename: "CheckboxAttribute" }, - }, - __typename: "EdgedTestTask", - }, - ], - __typename: "PaginatedTestTask", - }, -}; diff --git a/frontend/app/tests/mocks/data/task_1.ts b/frontend/app/tests/mocks/data/task_1.ts index ac59a2e61c..d0d501be3f 100644 --- a/frontend/app/tests/mocks/data/task_1.ts +++ b/frontend/app/tests/mocks/data/task_1.ts @@ -1,3 +1,5 @@ +import { permissionsAllow } from "./permissions"; + export const taskMocksSchema = [ { id: "8a4e2579-c300-48e1-b703-022bf6d224df", @@ -131,6 +133,17 @@ query TestTask($offset: Int, $limit: Int) { } } } + permissions { + edges { + node { + kind + view + create + update + delete + } + } + } } } `; @@ -139,6 +152,7 @@ export const taskMocksData = { TestTask: { count: 0, edges: [], + permissions: permissionsAllow, __typename: "PaginatedTestTask", }, }; diff --git a/frontend/app/tests/mocks/data/task_2.ts b/frontend/app/tests/mocks/data/task_2.ts index 9505c038a8..072375a821 100644 --- a/frontend/app/tests/mocks/data/task_2.ts +++ b/frontend/app/tests/mocks/data/task_2.ts @@ -1,3 +1,5 @@ +import { permissionsAllow } from "./permissions"; + export const taskMocksSchema = [ { id: "8a4e2579-c300-48e1-b703-022bf6d224df", @@ -128,6 +130,17 @@ query TestTask($offset: Int, $limit: Int) { } } } + permissions { + edges { + node { + kind + view + create + update + delete + } + } + } } } `; @@ -136,6 +149,7 @@ export const taskMocksData = { TestTask: { count: 0, edges: [], + permissions: permissionsAllow, __typename: "PaginatedTestTask", }, }; diff --git a/frontend/app/tests/mocks/data/task_3.ts b/frontend/app/tests/mocks/data/task_3.ts index 4e5ec31285..ebea9ac185 100644 --- a/frontend/app/tests/mocks/data/task_3.ts +++ b/frontend/app/tests/mocks/data/task_3.ts @@ -1,3 +1,5 @@ +import { permissionsAllow } from "./permissions"; + export const taskMocksSchema = [ { id: "8a4e2579-c300-48e1-b703-022bf6d224df", @@ -128,6 +130,17 @@ query TestTask($offset: Int, $limit: Int) { } } } + permissions { + edges { + node { + kind + view + create + update + delete + } + } + } } } `; @@ -136,6 +149,7 @@ export const taskMocksData = { TestTask: { count: 0, edges: [], + permissions: permissionsAllow, __typename: "PaginatedTestTask", }, }; diff --git a/frontend/app/tests/mocks/data/task_4.ts b/frontend/app/tests/mocks/data/task_4.ts index cccc684268..2e24344119 100644 --- a/frontend/app/tests/mocks/data/task_4.ts +++ b/frontend/app/tests/mocks/data/task_4.ts @@ -1,3 +1,5 @@ +import { permissionsAllow } from "./permissions"; + export const taskMocksSchema = [ { id: "8a4e2579-c300-48e1-b703-022bf6d224df", @@ -50,6 +52,17 @@ query TestTask($offset: Int, $limit: Int) { } } } + permissions { + edges { + node { + kind + view + create + update + delete + } + } + } } } `; @@ -58,6 +71,7 @@ export const taskMocksData = { TestTask: { count: 0, edges: [], + permissions: permissionsAllow, __typename: "PaginatedTestTask", }, }; diff --git a/frontend/app/tests/mocks/data/task_5.ts b/frontend/app/tests/mocks/data/task_5.ts index 58035d389c..56486cd0f0 100644 --- a/frontend/app/tests/mocks/data/task_5.ts +++ b/frontend/app/tests/mocks/data/task_5.ts @@ -1,3 +1,5 @@ +import { permissionsAllow } from "./permissions"; + export const taskMocksSchema = [ { id: "8a4e2579-c300-48e1-b703-022bf6d224df", @@ -45,6 +47,17 @@ query TestTask($offset: Int, $limit: Int) { } } } + permissions { + edges { + node { + kind + view + create + update + delete + } + } + } } } `; @@ -53,6 +66,7 @@ export const taskMocksData = { TestTask: { count: 0, edges: [], + permissions: permissionsAllow, __typename: "PaginatedTestTask", }, }; diff --git a/frontend/app/tests/mocks/data/task_6.ts b/frontend/app/tests/mocks/data/task_6.ts index 27cbcd1986..ba6d9b50a3 100644 --- a/frontend/app/tests/mocks/data/task_6.ts +++ b/frontend/app/tests/mocks/data/task_6.ts @@ -1,3 +1,5 @@ +import { permissionsAllow } from "./permissions"; + export const taskMocksSchema = [ { id: "8a4e2579-c300-48e1-b703-022bf6d224df", @@ -45,6 +47,17 @@ query TestTask($offset: Int, $limit: Int) { } } } + permissions { + edges { + node { + kind + view + create + update + delete + } + } + } } } `; @@ -53,6 +66,7 @@ export const taskMocksData = { TestTask: { count: 0, edges: [], + permissions: permissionsAllow, __typename: "PaginatedTestTask", }, }; diff --git a/frontend/app/tests/mocks/data/task_7.ts b/frontend/app/tests/mocks/data/task_7.ts index 9cb3efb261..e4e2a0dbb6 100644 --- a/frontend/app/tests/mocks/data/task_7.ts +++ b/frontend/app/tests/mocks/data/task_7.ts @@ -1,3 +1,5 @@ +import { permissionsAllow } from "./permissions"; + export const taskMocksSchema = [ { id: "8a4e2579-c300-48e1-b703-022bf6d224df", @@ -46,6 +48,17 @@ query TestTask($offset: Int, $limit: Int) { } } } + permissions { + edges { + node { + kind + view + create + update + delete + } + } + } } } `; @@ -54,6 +67,7 @@ export const taskMocksData = { TestTask: { count: 0, edges: [], + permissions: permissionsAllow, __typename: "PaginatedTestTask", }, }; diff --git a/frontend/app/tests/mocks/e2e/accounts.ts b/frontend/app/tests/mocks/e2e/accounts.ts deleted file mode 100644 index 4758a53b8c..0000000000 --- a/frontend/app/tests/mocks/e2e/accounts.ts +++ /dev/null @@ -1,9 +0,0 @@ -export const MAIN_BRANCH_NAME = "main"; -export const NEW_BRANCH_NAME = "cr1234"; -export const ADMIN_ACCOUNT_NAME = "admin"; -export const ADMIN_ACCOUNT_LABEL = "Admin"; -export const NEW_ADMIN_ACCOUNT_LABEL = "Administrator"; -export const NEW_ACCOUNT = { - name: "New Account", - password: "test", -}; diff --git a/frontend/app/tests/mocks/e2e/artifacts.ts b/frontend/app/tests/mocks/e2e/artifacts.ts deleted file mode 100644 index 9114eb22c5..0000000000 --- a/frontend/app/tests/mocks/e2e/artifacts.ts +++ /dev/null @@ -1,2 +0,0 @@ -export const ARTIFACT_DEFINITION_NAME = "startup-config"; -export const ARTIFACT_DEFINITION_FULL_NAME = "Startup Config for Edge devices"; diff --git a/frontend/app/tests/mocks/e2e/organizations.ts b/frontend/app/tests/mocks/e2e/organizations.ts deleted file mode 100644 index 56519278d8..0000000000 --- a/frontend/app/tests/mocks/e2e/organizations.ts +++ /dev/null @@ -1,5 +0,0 @@ -export const MAIN_BRANCH_NAME = "main"; -export const NEW_BRANCH_NAME = "cr1234"; -export const ORGANIZATION_NAME = "my-first-org"; -export const ORGANIZATION_DESCRIPTION = "Testing Infrahub"; -export const NEW_ORGANIZATION_DESCRIPTION = "Changes from branch cr1234"; diff --git a/frontend/app/tests/mocks/e2e/proposed-changes.ts b/frontend/app/tests/mocks/e2e/proposed-changes.ts deleted file mode 100644 index 7489511e82..0000000000 --- a/frontend/app/tests/mocks/e2e/proposed-changes.ts +++ /dev/null @@ -1,6 +0,0 @@ -export const PROPOSED_CHANGES_NAME = "New changes"; -export const PROPOSED_CHANGES_BRANCH = "ord1-add-upstream"; -export const PROPOSED_CHANGES_BRANCH_CONFLICT = "platform-conflict"; -export const PROPOSED_CHANGE_COMMENT_1 = "First comment here, a new thread will be created"; -export const PROPOSED_CHANGE_COMMENT_2 = "Second comment here, to reply to the first one"; -export const PROPOSED_CHANGE_COMMENT_3 = "Last comment to close this wonderful thread"; diff --git a/frontend/app/tests/mocks/e2e/validators.ts b/frontend/app/tests/mocks/e2e/validators.ts deleted file mode 100644 index 38538ea055..0000000000 --- a/frontend/app/tests/mocks/e2e/validators.ts +++ /dev/null @@ -1,7 +0,0 @@ -export const PROPOSED_CHANGES_NAME_FAIL = "New changes with failed validators"; - -export const PROPOSED_CHANGES_NAME_WARNING = "New changes with warning"; - -export const PROPOSED_CHANGES_BRANCH_CLEAN = "ord1-add-upstream"; - -export const PROPOSED_CHANGES_BRANCH_CONFLICT = "platform-conflict"; diff --git a/frontend/app/tests/unit/components/branch-list.test.tsx b/frontend/app/tests/unit/components/branch-list.test.tsx index 6e686d1168..84ea1101f2 100644 --- a/frontend/app/tests/unit/components/branch-list.test.tsx +++ b/frontend/app/tests/unit/components/branch-list.test.tsx @@ -33,7 +33,8 @@ describe("Branch list", () => { options={{ searchStringToObject: queryString.parse, objectToSearchString: queryString.stringify, - }}> + }} + > diff --git a/frontend/app/tests/unit/components/branch-selector.test.tsx b/frontend/app/tests/unit/components/branch-selector.test.tsx index af3df9a775..f737c9e8ec 100644 --- a/frontend/app/tests/unit/components/branch-selector.test.tsx +++ b/frontend/app/tests/unit/components/branch-selector.test.tsx @@ -18,7 +18,8 @@ describe("Branch selector", () => { options={{ searchStringToObject: queryString.parse, objectToSearchString: queryString.stringify, - }}> + }} + > diff --git a/frontend/app/tests/unit/components/filters/getFiltersFromFormData.test.ts b/frontend/app/tests/unit/components/filters/getFiltersFromFormData.test.ts index 707903dd25..475790377b 100644 --- a/frontend/app/tests/unit/components/filters/getFiltersFromFormData.test.ts +++ b/frontend/app/tests/unit/components/filters/getFiltersFromFormData.test.ts @@ -1,6 +1,6 @@ -import { describe, expect } from "vitest"; -import { FormFieldValue } from "@/components/form/type"; import { getFiltersFromFormData } from "@/components/filters/utils/getFiltersFromFormData"; +import { FormFieldValue } from "@/components/form/type"; +import { describe, expect } from "vitest"; describe("getFiltersFromFormData - test", () => { it("returns an attribute value correctly", () => { diff --git a/frontend/app/tests/unit/components/filters/getObjectFromFilters.test.ts b/frontend/app/tests/unit/components/filters/getObjectFromFilters.test.ts index ea04c53b8c..7ff9ec361a 100644 --- a/frontend/app/tests/unit/components/filters/getObjectFromFilters.test.ts +++ b/frontend/app/tests/unit/components/filters/getObjectFromFilters.test.ts @@ -1,7 +1,7 @@ -import { describe, expect } from "vitest"; import { getObjectFromFilters } from "@/components/filters/utils/getObjectFromFilters"; import { Filter } from "@/hooks/useFilters"; import { IModelSchema } from "@/state/atoms/schema.atom"; +import { describe, expect } from "vitest"; import { buildRelationshipSchema } from "../form/utils/getFormFieldsFromSchema.test"; describe("getObjectFromFilters - test", () => { diff --git a/frontend/app/tests/unit/components/form/utils/getCreateMutationFromFormData.test.ts b/frontend/app/tests/unit/components/form/utils/getCreateMutationFromFormData.test.ts index c26536840e..479c534aab 100644 --- a/frontend/app/tests/unit/components/form/utils/getCreateMutationFromFormData.test.ts +++ b/frontend/app/tests/unit/components/form/utils/getCreateMutationFromFormData.test.ts @@ -1,11 +1,11 @@ -import { describe, expect } from "vitest"; import { + AttributeValueFromProfile, DynamicFieldProps, FormAttributeValue, FormRelationshipValue, - AttributeValueFromProfile, } from "@/components/form/type"; import { getCreateMutationFromFormData } from "@/components/form/utils/mutations/getCreateMutationFromFormData"; +import { describe, expect } from "vitest"; export const buildField = (override?: Partial): DynamicFieldProps => { return { diff --git a/frontend/app/tests/unit/components/form/utils/getFieldDefaultValue.test.ts b/frontend/app/tests/unit/components/form/utils/getFieldDefaultValue.test.ts index 45cbee01dc..5c3b6262a7 100644 --- a/frontend/app/tests/unit/components/form/utils/getFieldDefaultValue.test.ts +++ b/frontend/app/tests/unit/components/form/utils/getFieldDefaultValue.test.ts @@ -1,11 +1,11 @@ -import { describe, expect, it } from "vitest"; +import { ProfileData } from "@/components/form/object-form"; import { GetFieldDefaultValue, getFieldDefaultValue, } from "@/components/form/utils/getFieldDefaultValue"; -import { ProfileData } from "@/components/form/object-form"; -import { buildAttributeSchema, buildRelationshipSchema } from "./getFormFieldsFromSchema.test"; import { AttributeType } from "@/utils/getObjectItemDisplayValue"; +import { describe, expect, it } from "vitest"; +import { buildAttributeSchema, buildRelationshipSchema } from "./getFormFieldsFromSchema.test"; describe("getFieldDefaultValue", () => { describe("when source is the user", () => { diff --git a/frontend/app/tests/unit/components/form/utils/getFormFieldsFromSchema.test.ts b/frontend/app/tests/unit/components/form/utils/getFormFieldsFromSchema.test.ts index 2e367f24f2..e50bac0fea 100644 --- a/frontend/app/tests/unit/components/form/utils/getFormFieldsFromSchema.test.ts +++ b/frontend/app/tests/unit/components/form/utils/getFormFieldsFromSchema.test.ts @@ -1,9 +1,12 @@ -import { describe, expect, it } from "vitest"; import { getFormFieldsFromSchema } from "@/components/form/utils/getFormFieldsFromSchema"; -import { IModelSchema } from "@/state/atoms/schema.atom"; import { AuthContextType } from "@/hooks/useAuth"; -import { AttributeType } from "@/utils/getObjectItemDisplayValue"; import { components } from "@/infraops"; +import { RelationshipSchema } from "@/screens/schema/types"; +import { store } from "@/state"; +import { currentBranchAtom } from "@/state/atoms/branches.atom"; +import { IModelSchema } from "@/state/atoms/schema.atom"; +import { AttributeType } from "@/utils/getObjectItemDisplayValue"; +import { describe, expect, it } from "vitest"; export const buildAttributeSchema = ( override?: Partial @@ -31,8 +34,8 @@ export const buildAttributeSchema = ( }); export const buildRelationshipSchema = ( - override?: Partial -): components["schemas"]["RelationshipSchema-Output"] => ({ + override?: Partial +): RelationshipSchema => ({ id: "17e2718c-73ed-3ffe-3402-c515757ff94f", state: "present", name: "tagone", @@ -267,16 +270,16 @@ describe("getFormFieldsFromSchema", () => { }, items: [ { - id: "prefix", + value: "prefix", + label: "Prefix", description: "Prefix serves as container for other prefixes", color: "#ed6a5a", - name: "Prefix", }, { - id: "address", + value: "address", + label: "Address", description: "Prefix serves as subnet for IP addresses", color: "#f4f1bb", - name: "Address", }, ], field: schema.attributes?.[0], @@ -316,11 +319,7 @@ describe("getFormFieldsFromSchema", () => { required: expect.any(Function), }, }, - items: [ - { id: 1, name: 1 }, - { id: 2, name: 2 }, - { id: 3, name: 3 }, - ], + items: [1, 2, 3], field: schema.attributes?.[0], schema, unique: false, @@ -356,8 +355,9 @@ describe("getFormFieldsFromSchema", () => { data: { sub: "1", }, - signIn: async () => {}, + login: async () => {}, signOut: () => {}, + setToken: () => {}, user: { id: "1", }, @@ -414,8 +414,9 @@ describe("getFormFieldsFromSchema", () => { data: { sub: "1", }, - signIn: async () => {}, + login: async () => {}, signOut: () => {}, + setToken: () => {}, user: { id: "1", }, @@ -442,4 +443,338 @@ describe("getFormFieldsFromSchema", () => { }, }); }); + + it("should disable a field if permission is DENY", () => { + // GIVEN + const schema = { + attributes: [buildAttributeSchema()], + } as IModelSchema; + + const initialObject: { field1: Partial } = { + field1: { + is_from_profile: false, + is_protected: true, + is_visible: true, + owner: { + id: "17dd42a7-d547-60af-3111-c51b4b2fc72e", + display_label: "Architecture Team", + }, + permissions: { + update_value: "DENY", + }, + source: null, + updated_at: "2024-07-15T09:32:01.363787+00:00", + value: "test-value", + __typename: "TextAttribute", + }, + }; + + // WHEN + const fields = getFormFieldsFromSchema({ schema, initialObject }); + + // THEN + expect(fields.length).to.equal(1); + expect(fields[0]).toEqual({ + defaultValue: { source: { type: "user" }, value: "test-value" }, + description: undefined, + disabled: true, + name: "field1", + label: "Field 1", + type: "Text", + unique: false, + rules: { + required: false, + validate: { + required: expect.any(Function), + }, + }, + }); + }); + + it("should enable a field if permission is ALLOW_ALL", () => { + // GIVEN + const schema = { + attributes: [buildAttributeSchema()], + } as IModelSchema; + + const initialObject: { field1: Partial } = { + field1: { + is_from_profile: false, + is_protected: true, + is_visible: true, + owner: { + id: "17dd42a7-d547-60af-3111-c51b4b2fc72e", + display_label: "Architecture Team", + }, + permissions: { + update_value: "ALLOW", + }, + source: null, + updated_at: "2024-07-15T09:32:01.363787+00:00", + value: "test-value", + __typename: "TextAttribute", + }, + }; + + // WHEN + const fields = getFormFieldsFromSchema({ schema, initialObject }); + + // THEN + expect(fields.length).to.equal(1); + expect(fields[0]).toEqual({ + defaultValue: { source: { type: "user" }, value: "test-value" }, + description: undefined, + disabled: false, + name: "field1", + label: "Field 1", + type: "Text", + unique: false, + rules: { + required: false, + validate: { + required: expect.any(Function), + }, + }, + }); + }); + + it("should enable a field if permission is ALLOW_DEFAULT and current branch is default", () => { + // GIVEN + const schema = { + attributes: [buildAttributeSchema()], + } as IModelSchema; + + const initialObject: { field1: Partial } = { + field1: { + is_from_profile: false, + is_protected: true, + is_visible: true, + owner: { + id: "17dd42a7-d547-60af-3111-c51b4b2fc72e", + display_label: "Architecture Team", + }, + permissions: { + update_value: "ALLOW_DEFAULT", + }, + source: null, + updated_at: "2024-07-15T09:32:01.363787+00:00", + value: "test-value", + __typename: "TextAttribute", + }, + }; + + store.set(currentBranchAtom, { + id: "18007869-b812-f080-2d60-c51d9e906226", + name: "mainnn", + description: "Default Branch", + origin_branch: "main", + branched_from: "2024-10-21T12:44:12.365354Z", + created_at: "2024-10-21T12:44:12.365371Z", + sync_with_git: true, + is_default: true, + has_schema_changes: false, + __typename: "Branch", + }); + + // WHEN + const fields = getFormFieldsFromSchema({ schema, initialObject }); + + // THEN + expect(fields.length).to.equal(1); + expect(fields[0]).toEqual({ + defaultValue: { source: { type: "user" }, value: "test-value" }, + description: undefined, + disabled: false, + name: "field1", + label: "Field 1", + type: "Text", + unique: false, + rules: { + required: false, + validate: { + required: expect.any(Function), + }, + }, + }); + }); + + it("should disable a field if permission is ALLOW_DEFAULT and current branch is not default", () => { + // GIVEN + const schema = { + attributes: [buildAttributeSchema()], + } as IModelSchema; + + const initialObject: { field1: Partial } = { + field1: { + is_from_profile: false, + is_protected: true, + is_visible: true, + owner: { + id: "17dd42a7-d547-60af-3111-c51b4b2fc72e", + display_label: "Architecture Team", + }, + permissions: { + update_value: "ALLOW_DEFAULT", + }, + source: null, + updated_at: "2024-07-15T09:32:01.363787+00:00", + value: "test-value", + __typename: "TextAttribute", + }, + }; + + store.set(currentBranchAtom, { + id: "18007869-b812-f080-2d60-c51d9e906226", + name: "other", + description: "other Branch", + origin_branch: "main", + branched_from: "2024-10-21T12:44:12.365354Z", + created_at: "2024-10-21T12:44:12.365371Z", + sync_with_git: true, + is_default: false, + has_schema_changes: false, + __typename: "Branch", + }); + + // WHEN + const fields = getFormFieldsFromSchema({ schema, initialObject }); + + // THEN + expect(fields.length).to.equal(1); + expect(fields[0]).toEqual({ + defaultValue: { source: { type: "user" }, value: "test-value" }, + description: undefined, + disabled: true, + name: "field1", + label: "Field 1", + type: "Text", + unique: false, + rules: { + required: false, + validate: { + required: expect.any(Function), + }, + }, + }); + }); + + it("should disable a field if permission is ALLOW_OTHER and current branch is default", () => { + // GIVEN + const schema = { + attributes: [buildAttributeSchema()], + } as IModelSchema; + + const initialObject: { field1: Partial } = { + field1: { + is_from_profile: false, + is_protected: true, + is_visible: true, + owner: { + id: "17dd42a7-d547-60af-3111-c51b4b2fc72e", + display_label: "Architecture Team", + }, + permissions: { + update_value: "ALLOW_OTHER", + }, + source: null, + updated_at: "2024-07-15T09:32:01.363787+00:00", + value: "test-value", + __typename: "TextAttribute", + }, + }; + + store.set(currentBranchAtom, { + id: "18007869-b812-f080-2d60-c51d9e906226", + name: "main", + description: "Default Branch", + origin_branch: "main", + branched_from: "2024-10-21T12:44:12.365354Z", + created_at: "2024-10-21T12:44:12.365371Z", + sync_with_git: true, + is_default: true, + has_schema_changes: false, + __typename: "Branch", + }); + + // WHEN + const fields = getFormFieldsFromSchema({ schema, initialObject }); + + // THEN + expect(fields.length).to.equal(1); + expect(fields[0]).toEqual({ + defaultValue: { source: { type: "user" }, value: "test-value" }, + description: undefined, + disabled: true, + name: "field1", + label: "Field 1", + type: "Text", + unique: false, + rules: { + required: false, + validate: { + required: expect.any(Function), + }, + }, + }); + }); + + it("should disable a field if permission is ALLOW_OTHER and current branch is not default", () => { + // GIVEN + const schema = { + attributes: [buildAttributeSchema()], + } as IModelSchema; + + const initialObject: { field1: Partial } = { + field1: { + is_from_profile: false, + is_protected: true, + is_visible: true, + owner: { + id: "17dd42a7-d547-60af-3111-c51b4b2fc72e", + display_label: "Architecture Team", + }, + permissions: { + update_value: "ALLOW_OTHER", + }, + source: null, + updated_at: "2024-07-15T09:32:01.363787+00:00", + value: "test-value", + __typename: "TextAttribute", + }, + }; + + store.set(currentBranchAtom, { + id: "18007869-b812-f080-2d60-c51d9e906226", + name: "other", + description: "other Branch", + origin_branch: "main", + branched_from: "2024-10-21T12:44:12.365354Z", + created_at: "2024-10-21T12:44:12.365371Z", + sync_with_git: true, + is_default: false, + has_schema_changes: false, + __typename: "Branch", + }); + + // WHEN + const fields = getFormFieldsFromSchema({ schema, initialObject }); + + // THEN + expect(fields.length).to.equal(1); + expect(fields[0]).toEqual({ + defaultValue: { source: { type: "user" }, value: "test-value" }, + description: undefined, + disabled: false, + name: "field1", + label: "Field 1", + type: "Text", + unique: false, + rules: { + required: false, + validate: { + required: expect.any(Function), + }, + }, + }); + }); }); diff --git a/frontend/app/tests/unit/components/form/utils/getRelationshipDefaultValue.test.ts b/frontend/app/tests/unit/components/form/utils/getRelationshipDefaultValue.test.ts index 0d2f419145..4e097063ae 100644 --- a/frontend/app/tests/unit/components/form/utils/getRelationshipDefaultValue.test.ts +++ b/frontend/app/tests/unit/components/form/utils/getRelationshipDefaultValue.test.ts @@ -1,7 +1,9 @@ -import { describe, expect, vi } from "vitest"; import { getRelationshipDefaultValue } from "@/components/form/utils/getRelationshipDefaultValue"; -import { RelationshipManyType, RelationshipOneType } from "@/utils/getObjectItemDisplayValue"; import { RESOURCE_GENERIC_KIND } from "@/screens/resource-manager/constants"; +import { store } from "@/state"; +import { iNodeSchema, schemaState } from "@/state/atoms/schema.atom"; +import { RelationshipManyType, RelationshipOneType } from "@/utils/getObjectItemDisplayValue"; +import { describe, expect } from "vitest"; const buildRelationshipOneData = (override: Partial): RelationshipOneType => ({ node: { @@ -53,12 +55,9 @@ describe("getRelationshipDefaultValue", () => { it("returns relationship from pool", () => { // GIVEN - vi.mock("jotai", () => ({ - atom: vi.fn(), - createStore: () => ({ - get: () => [{ kind: "FakeResourcePool", inherit_from: [RESOURCE_GENERIC_KIND] }], - }), - })); + store.set(schemaState, [ + { kind: "FakeResourcePool", inherit_from: [RESOURCE_GENERIC_KIND] } as iNodeSchema, + ]); const relationshipData = buildRelationshipOneData({ properties: { diff --git a/frontend/app/tests/unit/components/form/utils/getRelationshipsForForm.test.ts b/frontend/app/tests/unit/components/form/utils/getRelationshipsForForm.test.ts index 4509a6d363..8a5063a4c0 100644 --- a/frontend/app/tests/unit/components/form/utils/getRelationshipsForForm.test.ts +++ b/frontend/app/tests/unit/components/form/utils/getRelationshipsForForm.test.ts @@ -1,5 +1,5 @@ -import { describe, expect, it } from "vitest"; import { getRelationshipsForForm } from "@/components/form/utils/getRelationshipsForForm"; +import { describe, expect, it } from "vitest"; import { buildRelationshipSchema } from "./getFormFieldsFromSchema.test"; describe("getRelationshipsForForm", () => { diff --git a/frontend/app/tests/unit/components/form/utils/getUpdateMutationFromFormData.test.ts b/frontend/app/tests/unit/components/form/utils/getUpdateMutationFromFormData.test.ts index 5180f68111..6ff06524b6 100644 --- a/frontend/app/tests/unit/components/form/utils/getUpdateMutationFromFormData.test.ts +++ b/frontend/app/tests/unit/components/form/utils/getUpdateMutationFromFormData.test.ts @@ -1,10 +1,10 @@ -import { describe, expect } from "vitest"; import { DynamicFieldProps, FormAttributeValue, - RelationshipValueFormPool, + RelationshipValueFromPool, } from "@/components/form/type"; import { getUpdateMutationFromFormData } from "@/components/form/utils/mutations/getUpdateMutationFromFormData"; +import { describe, expect } from "vitest"; import { buildField } from "./getCreateMutationFromFormData.test"; describe("getUpdateMutationFromFormData - test", () => { @@ -111,7 +111,7 @@ describe("getUpdateMutationFromFormData - test", () => { defaultValue: { source: { type: "user" }, value: { id: "value1" } }, }), ]; - const formData: Record = { + const formData: Record = { field1: { source: { type: "pool", diff --git a/frontend/app/tests/unit/components/form/utils/isFieldDisabled.test.ts b/frontend/app/tests/unit/components/form/utils/isFieldDisabled.test.ts index 9d42929689..d958578d9c 100644 --- a/frontend/app/tests/unit/components/form/utils/isFieldDisabled.test.ts +++ b/frontend/app/tests/unit/components/form/utils/isFieldDisabled.test.ts @@ -1,5 +1,5 @@ +import { IsFieldDisabledParams, isFieldDisabled } from "@/components/form/utils/isFieldDisabled"; import { describe, expect, it } from "vitest"; -import { isFieldDisabled, IsFieldDisabledParams } from "@/components/form/utils/isFieldDisabled"; describe("isFieldDisabled", () => { it("returns true when field is read only", () => { @@ -81,20 +81,4 @@ describe("isFieldDisabled", () => { // THEN expect(disabled).to.equal(false); }); - - it("returns false if the auth is an admin", () => { - // GIVEN - const params: IsFieldDisabledParams = { - isProtected: true, - isReadOnly: false, - owner: { id: "not-admin" }, - auth: { permissions: { isAdmin: true } }, - }; - - // WHEN - const disabled = isFieldDisabled(params); - - // THEN - expect(disabled).to.equal(false); - }); }); diff --git a/frontend/app/tests/unit/components/tree/addItemsToTree.test.ts b/frontend/app/tests/unit/components/tree/addItemsToTree.test.ts index e836ace843..34d770f9ab 100644 --- a/frontend/app/tests/unit/components/tree/addItemsToTree.test.ts +++ b/frontend/app/tests/unit/components/tree/addItemsToTree.test.ts @@ -1,8 +1,8 @@ -import { describe, it, expect } from "vitest"; -import { addItemsToTree } from "@/screens/ipam/ipam-tree/utils"; -import { EMPTY_TREE } from "@/screens/ipam/ipam-tree/utils"; import { TreeProps } from "@/components/ui/tree"; import { TREE_ROOT_ID } from "@/screens/ipam/constants"; +import { addItemsToTree } from "@/screens/ipam/ipam-tree/utils"; +import { EMPTY_TREE } from "@/screens/ipam/ipam-tree/utils"; +import { describe, expect, it } from "vitest"; describe("Add items to tree", () => { it("should return the original tree when no items are provided", () => { diff --git a/frontend/app/tests/unit/data/peerDropdownOptionsFromApi.ts b/frontend/app/tests/unit/data/peerDropdownOptionsFromApi.ts deleted file mode 100644 index 90da436cf4..0000000000 --- a/frontend/app/tests/unit/data/peerDropdownOptionsFromApi.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { iPeerDropdownOptions } from "@/utils/dropdownOptionsForRelatedPeers"; - -export const C_PEER_DROPDOWN_OPTIONS: iPeerDropdownOptions = { - account: [ - { - id: "8e76ac89-45d5-43ea-9949-079261fc3590", - display_label: "David Palmer", - }, - { - id: "158b48a7-b1e0-4ef5-8aca-d1cfa67c1b7d", - display_label: "Chloe O'Brian", - }, - { - id: "9c37e51a-559e-473b-9d0e-88829968e25d", - display_label: "Jack Bauer", - }, - { - id: "e5b3354e-d062-4663-a891-84b3479c44b1", - display_label: "Crm Synchronization", - }, - { - id: "9660634f-17c2-422a-8b96-e3a618b06e10", - display_label: "Pop-Builder", - }, - { - id: "402144d7-70dc-456f-bc59-9ea0159decea", - display_label: "Admin", - }, - ], - group: [ - { - id: "fdad5ffe-3c12-4417-9131-73f35e1bfeee", - display_label: "Architecture Team", - }, - { - id: "0831f2dd-4138-4b00-9fdc-c366abee2bd0", - display_label: "Engineering Team", - }, - { - id: "389d85ef-0159-42d3-9c52-b587b80e48d9", - display_label: "Operation Team", - }, - { - id: "4f95f402-5a8c-45b5-b039-bb3dd6ffefab", - display_label: "Admin", - }, - ], - repository: [], -}; diff --git a/frontend/app/tests/unit/data/schemaKindNameMap.ts b/frontend/app/tests/unit/data/schemaKindNameMap.ts deleted file mode 100644 index 4db9fc6fec..0000000000 --- a/frontend/app/tests/unit/data/schemaKindNameMap.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { iSchemaKindNameMap } from "../../../src/state/atoms/schemaKindName.atom"; - -export const C_SchemaKindNameMap: iSchemaKindNameMap = { - Account: "account", - account: "Account", - Group: "group", - group: "Group", - Repository: "repository", - repository: "Repository", -}; diff --git a/frontend/app/tests/unit/diff/diff-tree/formatDiffNodesToDiffTree.test.ts b/frontend/app/tests/unit/diff/diff-tree/formatDiffNodesToDiffTree.test.ts index e57f39b5a6..e7b90b0743 100644 --- a/frontend/app/tests/unit/diff/diff-tree/formatDiffNodesToDiffTree.test.ts +++ b/frontend/app/tests/unit/diff/diff-tree/formatDiffNodesToDiffTree.test.ts @@ -1,4 +1,4 @@ -import { describe, it, expect } from "vitest"; +import { describe, expect, it } from "vitest"; import { formatDiffNodesToDiffTree } from "../../../../src/screens/diff/diff-tree"; import { DiffNode } from "../../../../src/screens/diff/node-diff/types"; import { TREE_ROOT_ID } from "../../../../src/screens/ipam/constants"; diff --git a/frontend/app/tests/unit/diff/diff-tree/generateRootCategoryNodeForDiffTree.test.ts b/frontend/app/tests/unit/diff/diff-tree/generateRootCategoryNodeForDiffTree.test.ts index 051d3cb6e9..6ca93572f7 100644 --- a/frontend/app/tests/unit/diff/diff-tree/generateRootCategoryNodeForDiffTree.test.ts +++ b/frontend/app/tests/unit/diff/diff-tree/generateRootCategoryNodeForDiffTree.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "vitest"; -import { generateRootCategoryNodeForDiffTree } from "../../../../src/screens/diff/diff-tree"; import { TreeProps } from "../../../../src/components/ui/tree"; +import { generateRootCategoryNodeForDiffTree } from "../../../../src/screens/diff/diff-tree"; import { TREE_ROOT_ID } from "../../../../src/screens/ipam/constants"; describe("Generate root category nodes for DiffTree", () => { diff --git a/frontend/app/tests/utils.ts b/frontend/app/tests/utils.ts index 3ff42043d5..816ed3ff02 100644 --- a/frontend/app/tests/utils.ts +++ b/frontend/app/tests/utils.ts @@ -11,10 +11,11 @@ export const saveScreenshotForDocs = async (page: Page, filename: string) => { }; export const createBranch = async (page: Page, branchName: string) => { + await page.getByTestId("branch-selector-trigger").click(); await page.getByTestId("create-branch-button").click(); await page.getByLabel("New branch name *").fill(branchName); await page.getByRole("button", { name: "Create a new branch" }).click(); - await expect(page.getByTestId("branch-list-display-button")).toContainText(branchName); + await expect(page.getByTestId("branch-selector-trigger")).toContainText(branchName); }; export const deleteBranch = async (page: Page, branchName: string) => { diff --git a/frontend/app/tsconfig.json b/frontend/app/tsconfig.json index e71872264a..fc9b09f9a5 100644 --- a/frontend/app/tsconfig.json +++ b/frontend/app/tsconfig.json @@ -1,21 +1,11 @@ { "compilerOptions": { "paths": { - "@/*": [ - "./src/*" - ] + "@/*": ["./src/*"] }, "target": "ESNext", - "lib": [ - "dom", - "dom.iterable", - "esnext" - ], - "types": [ - "vite/client", - "vitest/globals", - "cypress" - ], + "lib": ["dom", "dom.iterable", "esnext"], + "types": ["vite/client", "vitest/globals", "cypress"], "allowJs": false, "skipLibCheck": true, "esModuleInterop": false, @@ -45,5 +35,5 @@ "tests/unit/utils", "tests/unit/data", "tests/integrations/tests" - ], -} \ No newline at end of file + ] +} diff --git a/frontend/packages/plugins/template/package-lock.json b/frontend/packages/plugins/template/package-lock.json index f8116e011e..fcd2886d9a 100644 --- a/frontend/packages/plugins/template/package-lock.json +++ b/frontend/packages/plugins/template/package-lock.json @@ -392,13 +392,14 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", - "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", "cpu": [ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "aix" @@ -408,13 +409,14 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", - "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -424,13 +426,14 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", - "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -440,13 +443,14 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", - "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -456,13 +460,14 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz", - "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -472,13 +477,14 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", - "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -488,13 +494,14 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", - "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -504,13 +511,14 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", - "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -520,13 +528,14 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", - "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -536,13 +545,14 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", - "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -552,13 +562,14 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", - "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -568,13 +579,14 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", - "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", "cpu": [ "loong64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -584,13 +596,14 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", - "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", "cpu": [ "mips64el" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -600,13 +613,14 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", - "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", "cpu": [ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -616,13 +630,14 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", - "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", "cpu": [ "riscv64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -632,13 +647,14 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", - "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", "cpu": [ "s390x" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -648,13 +664,14 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", - "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -664,13 +681,14 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", - "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "netbsd" @@ -680,13 +698,14 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", - "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "openbsd" @@ -696,13 +715,14 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", - "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "sunos" @@ -712,13 +732,14 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", - "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -728,13 +749,14 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", - "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -744,13 +766,14 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", - "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -1030,208 +1053,224 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz", - "integrity": "sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.5.tgz", + "integrity": "sha512-SU5cvamg0Eyu/F+kLeMXS7GoahL+OoizlclVFX3l5Ql6yNlywJJ0OuqTzUx0v+aHhPHEB/56CT06GQrRrGNYww==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.0.tgz", - "integrity": "sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.22.5.tgz", + "integrity": "sha512-S4pit5BP6E5R5C8S6tgU/drvgjtYW76FBuG6+ibG3tMvlD1h9LHVF9KmlmaUBQ8Obou7hEyS+0w+IR/VtxwNMQ==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.0.tgz", - "integrity": "sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.22.5.tgz", + "integrity": "sha512-250ZGg4ipTL0TGvLlfACkIxS9+KLtIbn7BCZjsZj88zSg2Lvu3Xdw6dhAhfe/FjjXPVNCtcSp+WZjVsD3a/Zlw==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.0.tgz", - "integrity": "sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.22.5.tgz", + "integrity": "sha512-D8brJEFg5D+QxFcW6jYANu+Rr9SlKtTenmsX5hOSzNYVrK5oLAEMTUgKWYJP+wdKyCdeSwnapLsn+OVRFycuQg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.0.tgz", - "integrity": "sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.22.5.tgz", + "integrity": "sha512-PNqXYmdNFyWNg0ma5LdY8wP+eQfdvyaBAojAXgO7/gs0Q/6TQJVXAXe8gwW9URjbS0YAammur0fynYGiWsKlXw==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.0.tgz", - "integrity": "sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.22.5.tgz", + "integrity": "sha512-kSSCZOKz3HqlrEuwKd9TYv7vxPYD77vHSUvM2y0YaTGnFc8AdI5TTQRrM1yIp3tXCKrSL9A7JLoILjtad5t8pQ==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.0.tgz", - "integrity": "sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.22.5.tgz", + "integrity": "sha512-oTXQeJHRbOnwRnRffb6bmqmUugz0glXaPyspp4gbQOPVApdpRrY/j7KP3lr7M8kTfQTyrBUzFjj5EuHAhqH4/w==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.0.tgz", - "integrity": "sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.22.5.tgz", + "integrity": "sha512-qnOTIIs6tIGFKCHdhYitgC2XQ2X25InIbZFor5wh+mALH84qnFHvc+vmWUpyX97B0hNvwNUL4B+MB8vJvH65Fw==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.0.tgz", - "integrity": "sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.22.5.tgz", + "integrity": "sha512-TMYu+DUdNlgBXING13rHSfUc3Ky5nLPbWs4bFnT+R6Vu3OvXkTkixvvBKk8uO4MT5Ab6lC3U7x8S8El2q5o56w==", "cpu": [ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.0.tgz", - "integrity": "sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.22.5.tgz", + "integrity": "sha512-PTQq1Kz22ZRvuhr3uURH+U/Q/a0pbxJoICGSprNLAoBEkyD3Sh9qP5I0Asn0y0wejXQBbsVMRZRxlbGFD9OK4A==", "cpu": [ "riscv64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.0.tgz", - "integrity": "sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.22.5.tgz", + "integrity": "sha512-bR5nCojtpuMss6TDEmf/jnBnzlo+6n1UhgwqUvRoe4VIotC7FG1IKkyJbwsT7JDsF2jxR+NTnuOwiGv0hLyDoQ==", "cpu": [ "s390x" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz", - "integrity": "sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.22.5.tgz", + "integrity": "sha512-N0jPPhHjGShcB9/XXZQWuWBKZQnC1F36Ce3sDqWpujsGjDz/CQtOL9LgTrJ+rJC8MJeesMWrMWVLKKNR/tMOCA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.0.tgz", - "integrity": "sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.22.5.tgz", + "integrity": "sha512-uBa2e28ohzNNwjr6Uxm4XyaA1M/8aTgfF2T7UIlElLaeXkgpmIJ2EitVNQxjO9xLLLy60YqAgKn/AqSpCUkE9g==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.0.tgz", - "integrity": "sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.22.5.tgz", + "integrity": "sha512-RXT8S1HP8AFN/Kr3tg4fuYrNxZ/pZf1HemC5Tsddc6HzgGnJm0+Lh5rAHJkDuW3StI0ynNXukidROMXYl6ew8w==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.0.tgz", - "integrity": "sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.22.5.tgz", + "integrity": "sha512-ElTYOh50InL8kzyUD6XsnPit7jYCKrphmddKAe1/Ytt74apOxDq5YEcbsiKs0fR3vff3jEneMM+3I7jbqaMyBg==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz", - "integrity": "sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.22.5.tgz", + "integrity": "sha512-+lvL/4mQxSV8MukpkKyyvfwhH266COcWlXE/1qxwN08ajovta3459zrjLghYMgDerlzNwLAcFpvU+WWE5y6nAQ==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -1705,20 +1744,6 @@ "@esbuild/win32-x64": "0.18.20" } }, - "node_modules/@softarc/native-federation-esbuild/node_modules/rollup": { - "version": "2.79.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", - "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=10.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, "node_modules/@softarc/native-federation-esbuild/node_modules/rollup-plugin-node-externals": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/rollup-plugin-node-externals/-/rollup-plugin-node-externals-4.1.1.tgz", @@ -1783,9 +1808,10 @@ } }, "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "license": "MIT" }, "node_modules/@types/node": { "version": "20.14.0", @@ -2375,11 +2401,12 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "node_modules/esbuild": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", - "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", "dev": true, "hasInstallScript": true, + "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, @@ -2387,29 +2414,29 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.20.2", - "@esbuild/android-arm": "0.20.2", - "@esbuild/android-arm64": "0.20.2", - "@esbuild/android-x64": "0.20.2", - "@esbuild/darwin-arm64": "0.20.2", - "@esbuild/darwin-x64": "0.20.2", - "@esbuild/freebsd-arm64": "0.20.2", - "@esbuild/freebsd-x64": "0.20.2", - "@esbuild/linux-arm": "0.20.2", - "@esbuild/linux-arm64": "0.20.2", - "@esbuild/linux-ia32": "0.20.2", - "@esbuild/linux-loong64": "0.20.2", - "@esbuild/linux-mips64el": "0.20.2", - "@esbuild/linux-ppc64": "0.20.2", - "@esbuild/linux-riscv64": "0.20.2", - "@esbuild/linux-s390x": "0.20.2", - "@esbuild/linux-x64": "0.20.2", - "@esbuild/netbsd-x64": "0.20.2", - "@esbuild/openbsd-x64": "0.20.2", - "@esbuild/sunos-x64": "0.20.2", - "@esbuild/win32-arm64": "0.20.2", - "@esbuild/win32-ia32": "0.20.2", - "@esbuild/win32-x64": "0.20.2" + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" } }, "node_modules/escalade": { @@ -3275,10 +3302,11 @@ } }, "node_modules/micromatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", - "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, + "license": "MIT", "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" @@ -3319,6 +3347,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -3468,10 +3497,11 @@ } }, "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", - "dev": true + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", + "dev": true, + "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", @@ -3485,9 +3515,9 @@ } }, "node_modules/postcss": { - "version": "8.4.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", - "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", "dev": true, "funding": [ { @@ -3503,10 +3533,11 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.2.0" + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" @@ -3656,37 +3687,17 @@ } }, "node_modules/rollup": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.0.tgz", - "integrity": "sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==", - "dev": true, - "dependencies": { - "@types/estree": "1.0.5" - }, + "version": "2.79.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz", + "integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==", + "license": "MIT", "bin": { "rollup": "dist/bin/rollup" }, "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" + "node": ">=10.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.18.0", - "@rollup/rollup-android-arm64": "4.18.0", - "@rollup/rollup-darwin-arm64": "4.18.0", - "@rollup/rollup-darwin-x64": "4.18.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.18.0", - "@rollup/rollup-linux-arm-musleabihf": "4.18.0", - "@rollup/rollup-linux-arm64-gnu": "4.18.0", - "@rollup/rollup-linux-arm64-musl": "4.18.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.18.0", - "@rollup/rollup-linux-riscv64-gnu": "4.18.0", - "@rollup/rollup-linux-s390x-gnu": "4.18.0", - "@rollup/rollup-linux-x64-gnu": "4.18.0", - "@rollup/rollup-linux-x64-musl": "4.18.0", - "@rollup/rollup-win32-arm64-msvc": "4.18.0", - "@rollup/rollup-win32-ia32-msvc": "4.18.0", - "@rollup/rollup-win32-x64-msvc": "4.18.0", "fsevents": "~2.3.2" } }, @@ -3793,10 +3804,11 @@ } }, "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "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==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -4020,14 +4032,15 @@ "dev": true }, "node_modules/vite": { - "version": "5.2.12", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.12.tgz", - "integrity": "sha512-/gC8GxzxMK5ntBwb48pR32GGhENnjtY30G4A0jemunsBkiEZFw60s8InGpN8gkhHEkjnRK1aSAxeQgwvFhUHAA==", + "version": "5.4.8", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.8.tgz", + "integrity": "sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==", "dev": true, + "license": "MIT", "dependencies": { - "esbuild": "^0.20.1", - "postcss": "^8.4.38", - "rollup": "^4.13.0" + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" }, "bin": { "vite": "bin/vite.js" @@ -4046,6 +4059,7 @@ "less": "*", "lightningcss": "^1.21.0", "sass": "*", + "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" @@ -4063,6 +4077,9 @@ "sass": { "optional": true }, + "sass-embedded": { + "optional": true + }, "stylus": { "optional": true }, @@ -4074,6 +4091,42 @@ } } }, + "node_modules/vite/node_modules/rollup": { + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.22.5.tgz", + "integrity": "sha512-WoinX7GeQOFMGznEcWA1WrTQCd/tpEbMkc3nuMs9BT0CPjMdSjPMTVClwWd4pgSQwJdP65SK9mTCNvItlr5o7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.6" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.22.5", + "@rollup/rollup-android-arm64": "4.22.5", + "@rollup/rollup-darwin-arm64": "4.22.5", + "@rollup/rollup-darwin-x64": "4.22.5", + "@rollup/rollup-linux-arm-gnueabihf": "4.22.5", + "@rollup/rollup-linux-arm-musleabihf": "4.22.5", + "@rollup/rollup-linux-arm64-gnu": "4.22.5", + "@rollup/rollup-linux-arm64-musl": "4.22.5", + "@rollup/rollup-linux-powerpc64le-gnu": "4.22.5", + "@rollup/rollup-linux-riscv64-gnu": "4.22.5", + "@rollup/rollup-linux-s390x-gnu": "4.22.5", + "@rollup/rollup-linux-x64-gnu": "4.22.5", + "@rollup/rollup-linux-x64-musl": "4.22.5", + "@rollup/rollup-win32-arm64-msvc": "4.22.5", + "@rollup/rollup-win32-ia32-msvc": "4.22.5", + "@rollup/rollup-win32-x64-msvc": "4.22.5", + "fsevents": "~2.3.2" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -4396,163 +4449,163 @@ } }, "@esbuild/aix-ppc64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", - "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", "dev": true, "optional": true }, "@esbuild/android-arm": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", - "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", "dev": true, "optional": true }, "@esbuild/android-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", - "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", "dev": true, "optional": true }, "@esbuild/android-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", - "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", "dev": true, "optional": true }, "@esbuild/darwin-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz", - "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", "dev": true, "optional": true }, "@esbuild/darwin-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", - "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", "dev": true, "optional": true }, "@esbuild/freebsd-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", - "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", "dev": true, "optional": true }, "@esbuild/freebsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", - "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", "dev": true, "optional": true }, "@esbuild/linux-arm": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", - "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", "dev": true, "optional": true }, "@esbuild/linux-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", - "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", "dev": true, "optional": true }, "@esbuild/linux-ia32": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", - "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", "dev": true, "optional": true }, "@esbuild/linux-loong64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", - "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", "dev": true, "optional": true }, "@esbuild/linux-mips64el": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", - "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", "dev": true, "optional": true }, "@esbuild/linux-ppc64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", - "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", "dev": true, "optional": true }, "@esbuild/linux-riscv64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", - "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", "dev": true, "optional": true }, "@esbuild/linux-s390x": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", - "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", "dev": true, "optional": true }, "@esbuild/linux-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", - "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", "dev": true, "optional": true }, "@esbuild/netbsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", - "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", "dev": true, "optional": true }, "@esbuild/openbsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", - "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", "dev": true, "optional": true }, "@esbuild/sunos-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", - "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", + "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==", "dev": true, "optional": true }, "@esbuild/win32-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", - "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", "dev": true, "optional": true }, "@esbuild/win32-ia32": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", - "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", + "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==", "dev": true, "optional": true }, "@esbuild/win32-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", - "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", "dev": true, "optional": true }, @@ -4766,114 +4819,114 @@ } }, "@rollup/rollup-android-arm-eabi": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.0.tgz", - "integrity": "sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.5.tgz", + "integrity": "sha512-SU5cvamg0Eyu/F+kLeMXS7GoahL+OoizlclVFX3l5Ql6yNlywJJ0OuqTzUx0v+aHhPHEB/56CT06GQrRrGNYww==", "dev": true, "optional": true }, "@rollup/rollup-android-arm64": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.0.tgz", - "integrity": "sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.22.5.tgz", + "integrity": "sha512-S4pit5BP6E5R5C8S6tgU/drvgjtYW76FBuG6+ibG3tMvlD1h9LHVF9KmlmaUBQ8Obou7hEyS+0w+IR/VtxwNMQ==", "dev": true, "optional": true }, "@rollup/rollup-darwin-arm64": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.0.tgz", - "integrity": "sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.22.5.tgz", + "integrity": "sha512-250ZGg4ipTL0TGvLlfACkIxS9+KLtIbn7BCZjsZj88zSg2Lvu3Xdw6dhAhfe/FjjXPVNCtcSp+WZjVsD3a/Zlw==", "dev": true, "optional": true }, "@rollup/rollup-darwin-x64": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.0.tgz", - "integrity": "sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.22.5.tgz", + "integrity": "sha512-D8brJEFg5D+QxFcW6jYANu+Rr9SlKtTenmsX5hOSzNYVrK5oLAEMTUgKWYJP+wdKyCdeSwnapLsn+OVRFycuQg==", "dev": true, "optional": true }, "@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.0.tgz", - "integrity": "sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.22.5.tgz", + "integrity": "sha512-PNqXYmdNFyWNg0ma5LdY8wP+eQfdvyaBAojAXgO7/gs0Q/6TQJVXAXe8gwW9URjbS0YAammur0fynYGiWsKlXw==", "dev": true, "optional": true }, "@rollup/rollup-linux-arm-musleabihf": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.0.tgz", - "integrity": "sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.22.5.tgz", + "integrity": "sha512-kSSCZOKz3HqlrEuwKd9TYv7vxPYD77vHSUvM2y0YaTGnFc8AdI5TTQRrM1yIp3tXCKrSL9A7JLoILjtad5t8pQ==", "dev": true, "optional": true }, "@rollup/rollup-linux-arm64-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.0.tgz", - "integrity": "sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.22.5.tgz", + "integrity": "sha512-oTXQeJHRbOnwRnRffb6bmqmUugz0glXaPyspp4gbQOPVApdpRrY/j7KP3lr7M8kTfQTyrBUzFjj5EuHAhqH4/w==", "dev": true, "optional": true }, "@rollup/rollup-linux-arm64-musl": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.0.tgz", - "integrity": "sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.22.5.tgz", + "integrity": "sha512-qnOTIIs6tIGFKCHdhYitgC2XQ2X25InIbZFor5wh+mALH84qnFHvc+vmWUpyX97B0hNvwNUL4B+MB8vJvH65Fw==", "dev": true, "optional": true }, "@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.0.tgz", - "integrity": "sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.22.5.tgz", + "integrity": "sha512-TMYu+DUdNlgBXING13rHSfUc3Ky5nLPbWs4bFnT+R6Vu3OvXkTkixvvBKk8uO4MT5Ab6lC3U7x8S8El2q5o56w==", "dev": true, "optional": true }, "@rollup/rollup-linux-riscv64-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.0.tgz", - "integrity": "sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.22.5.tgz", + "integrity": "sha512-PTQq1Kz22ZRvuhr3uURH+U/Q/a0pbxJoICGSprNLAoBEkyD3Sh9qP5I0Asn0y0wejXQBbsVMRZRxlbGFD9OK4A==", "dev": true, "optional": true }, "@rollup/rollup-linux-s390x-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.0.tgz", - "integrity": "sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.22.5.tgz", + "integrity": "sha512-bR5nCojtpuMss6TDEmf/jnBnzlo+6n1UhgwqUvRoe4VIotC7FG1IKkyJbwsT7JDsF2jxR+NTnuOwiGv0hLyDoQ==", "dev": true, "optional": true }, "@rollup/rollup-linux-x64-gnu": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.0.tgz", - "integrity": "sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.22.5.tgz", + "integrity": "sha512-N0jPPhHjGShcB9/XXZQWuWBKZQnC1F36Ce3sDqWpujsGjDz/CQtOL9LgTrJ+rJC8MJeesMWrMWVLKKNR/tMOCA==", "dev": true, "optional": true }, "@rollup/rollup-linux-x64-musl": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.0.tgz", - "integrity": "sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.22.5.tgz", + "integrity": "sha512-uBa2e28ohzNNwjr6Uxm4XyaA1M/8aTgfF2T7UIlElLaeXkgpmIJ2EitVNQxjO9xLLLy60YqAgKn/AqSpCUkE9g==", "dev": true, "optional": true }, "@rollup/rollup-win32-arm64-msvc": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.0.tgz", - "integrity": "sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.22.5.tgz", + "integrity": "sha512-RXT8S1HP8AFN/Kr3tg4fuYrNxZ/pZf1HemC5Tsddc6HzgGnJm0+Lh5rAHJkDuW3StI0ynNXukidROMXYl6ew8w==", "dev": true, "optional": true }, "@rollup/rollup-win32-ia32-msvc": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.0.tgz", - "integrity": "sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.22.5.tgz", + "integrity": "sha512-ElTYOh50InL8kzyUD6XsnPit7jYCKrphmddKAe1/Ytt74apOxDq5YEcbsiKs0fR3vff3jEneMM+3I7jbqaMyBg==", "dev": true, "optional": true }, "@rollup/rollup-win32-x64-msvc": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.0.tgz", - "integrity": "sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.22.5.tgz", + "integrity": "sha512-+lvL/4mQxSV8MukpkKyyvfwhH266COcWlXE/1qxwN08ajovta3459zrjLghYMgDerlzNwLAcFpvU+WWE5y6nAQ==", "dev": true, "optional": true }, @@ -5121,14 +5174,6 @@ "@esbuild/win32-x64": "0.18.20" } }, - "rollup": { - "version": "2.79.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz", - "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==", - "requires": { - "fsevents": "~2.3.2" - } - }, "rollup-plugin-node-externals": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/rollup-plugin-node-externals/-/rollup-plugin-node-externals-4.1.1.tgz", @@ -5189,9 +5234,9 @@ } }, "@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==" }, "@types/node": { "version": "20.14.0", @@ -5589,34 +5634,34 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "esbuild": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", - "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", "dev": true, "requires": { - "@esbuild/aix-ppc64": "0.20.2", - "@esbuild/android-arm": "0.20.2", - "@esbuild/android-arm64": "0.20.2", - "@esbuild/android-x64": "0.20.2", - "@esbuild/darwin-arm64": "0.20.2", - "@esbuild/darwin-x64": "0.20.2", - "@esbuild/freebsd-arm64": "0.20.2", - "@esbuild/freebsd-x64": "0.20.2", - "@esbuild/linux-arm": "0.20.2", - "@esbuild/linux-arm64": "0.20.2", - "@esbuild/linux-ia32": "0.20.2", - "@esbuild/linux-loong64": "0.20.2", - "@esbuild/linux-mips64el": "0.20.2", - "@esbuild/linux-ppc64": "0.20.2", - "@esbuild/linux-riscv64": "0.20.2", - "@esbuild/linux-s390x": "0.20.2", - "@esbuild/linux-x64": "0.20.2", - "@esbuild/netbsd-x64": "0.20.2", - "@esbuild/openbsd-x64": "0.20.2", - "@esbuild/sunos-x64": "0.20.2", - "@esbuild/win32-arm64": "0.20.2", - "@esbuild/win32-ia32": "0.20.2", - "@esbuild/win32-x64": "0.20.2" + "@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" } }, "escalade": { @@ -6267,9 +6312,9 @@ "dev": true }, "micromatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", - "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "requires": { "braces": "^3.0.3", @@ -6405,9 +6450,9 @@ "dev": true }, "picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", "dev": true }, "picomatch": { @@ -6416,14 +6461,14 @@ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" }, "postcss": { - "version": "8.4.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", - "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", "dev": true, "requires": { "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.2.0" + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" } }, "prelude-ls": { @@ -6515,28 +6560,10 @@ } }, "rollup": { - "version": "4.18.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.0.tgz", - "integrity": "sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==", - "dev": true, + "version": "2.79.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz", + "integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==", "requires": { - "@rollup/rollup-android-arm-eabi": "4.18.0", - "@rollup/rollup-android-arm64": "4.18.0", - "@rollup/rollup-darwin-arm64": "4.18.0", - "@rollup/rollup-darwin-x64": "4.18.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.18.0", - "@rollup/rollup-linux-arm-musleabihf": "4.18.0", - "@rollup/rollup-linux-arm64-gnu": "4.18.0", - "@rollup/rollup-linux-arm64-musl": "4.18.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.18.0", - "@rollup/rollup-linux-riscv64-gnu": "4.18.0", - "@rollup/rollup-linux-s390x-gnu": "4.18.0", - "@rollup/rollup-linux-x64-gnu": "4.18.0", - "@rollup/rollup-linux-x64-musl": "4.18.0", - "@rollup/rollup-win32-arm64-msvc": "4.18.0", - "@rollup/rollup-win32-ia32-msvc": "4.18.0", - "@rollup/rollup-win32-x64-msvc": "4.18.0", - "@types/estree": "1.0.5", "fsevents": "~2.3.2" } }, @@ -6600,9 +6627,9 @@ "dev": true }, "source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "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==", "dev": true }, "sourcemap-codec": { @@ -6757,15 +6784,43 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "vite": { - "version": "5.2.12", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.12.tgz", - "integrity": "sha512-/gC8GxzxMK5ntBwb48pR32GGhENnjtY30G4A0jemunsBkiEZFw60s8InGpN8gkhHEkjnRK1aSAxeQgwvFhUHAA==", + "version": "5.4.8", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.8.tgz", + "integrity": "sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==", "dev": true, "requires": { - "esbuild": "^0.20.1", + "esbuild": "^0.21.3", "fsevents": "~2.3.3", - "postcss": "^8.4.38", - "rollup": "^4.13.0" + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "dependencies": { + "rollup": { + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.22.5.tgz", + "integrity": "sha512-WoinX7GeQOFMGznEcWA1WrTQCd/tpEbMkc3nuMs9BT0CPjMdSjPMTVClwWd4pgSQwJdP65SK9mTCNvItlr5o7w==", + "dev": true, + "requires": { + "@rollup/rollup-android-arm-eabi": "4.22.5", + "@rollup/rollup-android-arm64": "4.22.5", + "@rollup/rollup-darwin-arm64": "4.22.5", + "@rollup/rollup-darwin-x64": "4.22.5", + "@rollup/rollup-linux-arm-gnueabihf": "4.22.5", + "@rollup/rollup-linux-arm-musleabihf": "4.22.5", + "@rollup/rollup-linux-arm64-gnu": "4.22.5", + "@rollup/rollup-linux-arm64-musl": "4.22.5", + "@rollup/rollup-linux-powerpc64le-gnu": "4.22.5", + "@rollup/rollup-linux-riscv64-gnu": "4.22.5", + "@rollup/rollup-linux-s390x-gnu": "4.22.5", + "@rollup/rollup-linux-x64-gnu": "4.22.5", + "@rollup/rollup-linux-x64-musl": "4.22.5", + "@rollup/rollup-win32-arm64-msvc": "4.22.5", + "@rollup/rollup-win32-ia32-msvc": "4.22.5", + "@rollup/rollup-win32-x64-msvc": "4.22.5", + "@types/estree": "1.0.6", + "fsevents": "~2.3.2" + } + } } }, "which": { diff --git a/frontend/packages/ui/package-lock.json b/frontend/packages/ui/package-lock.json index f5b7e774b4..4dca63f0a8 100644 --- a/frontend/packages/ui/package-lock.json +++ b/frontend/packages/ui/package-lock.json @@ -2984,208 +2984,224 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.1.tgz", - "integrity": "sha512-lncuC4aHicncmbORnx+dUaAgzee9cm/PbIqgWz1PpXuwc+sa1Ct83tnqUDy/GFKleLiN7ZIeytM6KJ4cAn1SxA==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.5.tgz", + "integrity": "sha512-SU5cvamg0Eyu/F+kLeMXS7GoahL+OoizlclVFX3l5Ql6yNlywJJ0OuqTzUx0v+aHhPHEB/56CT06GQrRrGNYww==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.1.tgz", - "integrity": "sha512-F/tkdw0WSs4ojqz5Ovrw5r9odqzFjb5LIgHdHZG65dFI1lWTWRVy32KDJLKRISHgJvqUeUhdIvy43fX41znyDg==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.22.5.tgz", + "integrity": "sha512-S4pit5BP6E5R5C8S6tgU/drvgjtYW76FBuG6+ibG3tMvlD1h9LHVF9KmlmaUBQ8Obou7hEyS+0w+IR/VtxwNMQ==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.1.tgz", - "integrity": "sha512-vk+ma8iC1ebje/ahpxpnrfVQJibTMyHdWpOGZ3JpQ7Mgn/3QNHmPq7YwjZbIE7km73dH5M1e6MRRsnEBW7v5CQ==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.22.5.tgz", + "integrity": "sha512-250ZGg4ipTL0TGvLlfACkIxS9+KLtIbn7BCZjsZj88zSg2Lvu3Xdw6dhAhfe/FjjXPVNCtcSp+WZjVsD3a/Zlw==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.1.tgz", - "integrity": "sha512-IgpzXKauRe1Tafcej9STjSSuG0Ghu/xGYH+qG6JwsAUxXrnkvNHcq/NL6nz1+jzvWAnQkuAJ4uIwGB48K9OCGA==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.22.5.tgz", + "integrity": "sha512-D8brJEFg5D+QxFcW6jYANu+Rr9SlKtTenmsX5hOSzNYVrK5oLAEMTUgKWYJP+wdKyCdeSwnapLsn+OVRFycuQg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.1.tgz", - "integrity": "sha512-P9bSiAUnSSM7EmyRK+e5wgpqai86QOSv8BwvkGjLwYuOpaeomiZWifEos517CwbG+aZl1T4clSE1YqqH2JRs+g==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.22.5.tgz", + "integrity": "sha512-PNqXYmdNFyWNg0ma5LdY8wP+eQfdvyaBAojAXgO7/gs0Q/6TQJVXAXe8gwW9URjbS0YAammur0fynYGiWsKlXw==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.1.tgz", - "integrity": "sha512-5RnjpACoxtS+aWOI1dURKno11d7krfpGDEn19jI8BuWmSBbUC4ytIADfROM1FZrFhQPSoP+KEa3NlEScznBTyQ==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.22.5.tgz", + "integrity": "sha512-kSSCZOKz3HqlrEuwKd9TYv7vxPYD77vHSUvM2y0YaTGnFc8AdI5TTQRrM1yIp3tXCKrSL9A7JLoILjtad5t8pQ==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.1.tgz", - "integrity": "sha512-8mwmGD668m8WaGbthrEYZ9CBmPug2QPGWxhJxh/vCgBjro5o96gL04WLlg5BA233OCWLqERy4YUzX3bJGXaJgQ==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.22.5.tgz", + "integrity": "sha512-oTXQeJHRbOnwRnRffb6bmqmUugz0glXaPyspp4gbQOPVApdpRrY/j7KP3lr7M8kTfQTyrBUzFjj5EuHAhqH4/w==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.1.tgz", - "integrity": "sha512-dJX9u4r4bqInMGOAQoGYdwDP8lQiisWb9et+T84l2WXk41yEej8v2iGKodmdKimT8cTAYt0jFb+UEBxnPkbXEQ==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.22.5.tgz", + "integrity": "sha512-qnOTIIs6tIGFKCHdhYitgC2XQ2X25InIbZFor5wh+mALH84qnFHvc+vmWUpyX97B0hNvwNUL4B+MB8vJvH65Fw==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.1.tgz", - "integrity": "sha512-V72cXdTl4EI0x6FNmho4D502sy7ed+LuVW6Ym8aI6DRQ9hQZdp5sj0a2usYOlqvFBNKQnLQGwmYnujo2HvjCxQ==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.22.5.tgz", + "integrity": "sha512-TMYu+DUdNlgBXING13rHSfUc3Ky5nLPbWs4bFnT+R6Vu3OvXkTkixvvBKk8uO4MT5Ab6lC3U7x8S8El2q5o56w==", "cpu": [ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.1.tgz", - "integrity": "sha512-f+pJih7sxoKmbjghrM2RkWo2WHUW8UbfxIQiWo5yeCaCM0TveMEuAzKJte4QskBp1TIinpnRcxkquY+4WuY/tg==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.22.5.tgz", + "integrity": "sha512-PTQq1Kz22ZRvuhr3uURH+U/Q/a0pbxJoICGSprNLAoBEkyD3Sh9qP5I0Asn0y0wejXQBbsVMRZRxlbGFD9OK4A==", "cpu": [ "riscv64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.1.tgz", - "integrity": "sha512-qb1hMMT3Fr/Qz1OKovCuUM11MUNLUuHeBC2DPPAWUYYUAOFWaxInaTwTQmc7Fl5La7DShTEpmYwgdt2hG+4TEg==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.22.5.tgz", + "integrity": "sha512-bR5nCojtpuMss6TDEmf/jnBnzlo+6n1UhgwqUvRoe4VIotC7FG1IKkyJbwsT7JDsF2jxR+NTnuOwiGv0hLyDoQ==", "cpu": [ "s390x" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.1.tgz", - "integrity": "sha512-7O5u/p6oKUFYjRbZkL2FLbwsyoJAjyeXHCU3O4ndvzg2OFO2GinFPSJFGbiwFDaCFc+k7gs9CF243PwdPQFh5g==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.22.5.tgz", + "integrity": "sha512-N0jPPhHjGShcB9/XXZQWuWBKZQnC1F36Ce3sDqWpujsGjDz/CQtOL9LgTrJ+rJC8MJeesMWrMWVLKKNR/tMOCA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.1.tgz", - "integrity": "sha512-pDLkYITdYrH/9Cv/Vlj8HppDuLMDUBmgsM0+N+xLtFd18aXgM9Nyqupb/Uw+HeidhfYg2lD6CXvz6CjoVOaKjQ==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.22.5.tgz", + "integrity": "sha512-uBa2e28ohzNNwjr6Uxm4XyaA1M/8aTgfF2T7UIlElLaeXkgpmIJ2EitVNQxjO9xLLLy60YqAgKn/AqSpCUkE9g==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.1.tgz", - "integrity": "sha512-W2ZNI323O/8pJdBGil1oCauuCzmVd9lDmWBBqxYZcOqWD6aWqJtVBQ1dFrF4dYpZPks6F+xCZHfzG5hYlSHZ6g==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.22.5.tgz", + "integrity": "sha512-RXT8S1HP8AFN/Kr3tg4fuYrNxZ/pZf1HemC5Tsddc6HzgGnJm0+Lh5rAHJkDuW3StI0ynNXukidROMXYl6ew8w==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.1.tgz", - "integrity": "sha512-ELfEX1/+eGZYMaCIbK4jqLxO1gyTSOIlZr6pbC4SRYFaSIDVKOnZNMdoZ+ON0mrFDp4+H5MhwNC1H/AhE3zQLg==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.22.5.tgz", + "integrity": "sha512-ElTYOh50InL8kzyUD6XsnPit7jYCKrphmddKAe1/Ytt74apOxDq5YEcbsiKs0fR3vff3jEneMM+3I7jbqaMyBg==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.1.tgz", - "integrity": "sha512-yjk2MAkQmoaPYCSu35RLJ62+dz358nE83VfTePJRp8CG7aMg25mEJYpXFiD+NcevhX8LxD5OP5tktPXnXN7GDw==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.22.5.tgz", + "integrity": "sha512-+lvL/4mQxSV8MukpkKyyvfwhH266COcWlXE/1qxwN08ajovta3459zrjLghYMgDerlzNwLAcFpvU+WWE5y6nAQ==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -4295,10 +4311,11 @@ "dev": true }, "node_modules/@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "dev": true + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true, + "license": "MIT" }, "node_modules/@types/express": { "version": "4.17.21", @@ -5148,9 +5165,9 @@ } }, "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==", "dev": true, "license": "MIT", "dependencies": { @@ -5162,7 +5179,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" @@ -5635,11 +5652,10 @@ "dev": true }, "node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", "dev": true, - "license": "MIT", "engines": { "node": ">= 0.6" } @@ -5923,9 +5939,9 @@ "dev": true }, "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==", "dev": true, "license": "MIT", "engines": { @@ -6555,38 +6571,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.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", + "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", "dev": true, - "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": "0.7.1", "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", @@ -6712,14 +6727,14 @@ } }, "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==", "dev": true, "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", @@ -8144,11 +8159,14 @@ } }, "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==", "dev": true, - "license": "MIT" + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/merge-stream": { "version": "2.0.0", @@ -8176,10 +8194,11 @@ } }, "node_modules/micromatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", - "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, + "license": "MIT", "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" @@ -8933,9 +8952,9 @@ } }, "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==", "dev": true, "license": "MIT" }, @@ -8965,10 +8984,11 @@ } }, "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", - "dev": true + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", + "dev": true, + "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", @@ -9104,9 +9124,9 @@ } }, "node_modules/postcss": { - "version": "8.4.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.39.tgz", - "integrity": "sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==", + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", "dev": true, "funding": [ { @@ -9122,10 +9142,11 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.1", - "source-map-js": "^1.2.0" + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" @@ -9369,13 +9390,13 @@ } }, "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==", "dev": true, "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.0.4" + "side-channel": "^1.0.6" }, "engines": { "node": ">=0.6" @@ -9835,12 +9856,13 @@ } }, "node_modules/rollup": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.1.tgz", - "integrity": "sha512-Elx2UT8lzxxOXMpy5HWQGZqkrQOtrVDDa/bm9l10+U4rQnVzbL/LgZ4NOM1MPIDyHk69W4InuYDF5dzRh4Kw1A==", + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.22.5.tgz", + "integrity": "sha512-WoinX7GeQOFMGznEcWA1WrTQCd/tpEbMkc3nuMs9BT0CPjMdSjPMTVClwWd4pgSQwJdP65SK9mTCNvItlr5o7w==", "dev": true, + "license": "MIT", "dependencies": { - "@types/estree": "1.0.5" + "@types/estree": "1.0.6" }, "bin": { "rollup": "dist/bin/rollup" @@ -9850,22 +9872,22 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.18.1", - "@rollup/rollup-android-arm64": "4.18.1", - "@rollup/rollup-darwin-arm64": "4.18.1", - "@rollup/rollup-darwin-x64": "4.18.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.18.1", - "@rollup/rollup-linux-arm-musleabihf": "4.18.1", - "@rollup/rollup-linux-arm64-gnu": "4.18.1", - "@rollup/rollup-linux-arm64-musl": "4.18.1", - "@rollup/rollup-linux-powerpc64le-gnu": "4.18.1", - "@rollup/rollup-linux-riscv64-gnu": "4.18.1", - "@rollup/rollup-linux-s390x-gnu": "4.18.1", - "@rollup/rollup-linux-x64-gnu": "4.18.1", - "@rollup/rollup-linux-x64-musl": "4.18.1", - "@rollup/rollup-win32-arm64-msvc": "4.18.1", - "@rollup/rollup-win32-ia32-msvc": "4.18.1", - "@rollup/rollup-win32-x64-msvc": "4.18.1", + "@rollup/rollup-android-arm-eabi": "4.22.5", + "@rollup/rollup-android-arm64": "4.22.5", + "@rollup/rollup-darwin-arm64": "4.22.5", + "@rollup/rollup-darwin-x64": "4.22.5", + "@rollup/rollup-linux-arm-gnueabihf": "4.22.5", + "@rollup/rollup-linux-arm-musleabihf": "4.22.5", + "@rollup/rollup-linux-arm64-gnu": "4.22.5", + "@rollup/rollup-linux-arm64-musl": "4.22.5", + "@rollup/rollup-linux-powerpc64le-gnu": "4.22.5", + "@rollup/rollup-linux-riscv64-gnu": "4.22.5", + "@rollup/rollup-linux-s390x-gnu": "4.22.5", + "@rollup/rollup-linux-x64-gnu": "4.22.5", + "@rollup/rollup-linux-x64-musl": "4.22.5", + "@rollup/rollup-win32-arm64-msvc": "4.22.5", + "@rollup/rollup-win32-ia32-msvc": "4.22.5", + "@rollup/rollup-win32-x64-msvc": "4.22.5", "fsevents": "~2.3.2" } }, @@ -9940,9 +9962,9 @@ } }, "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==", "dev": true, "license": "MIT", "dependencies": { @@ -9981,6 +10003,16 @@ "dev": true, "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==", + "dev": true, + "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", @@ -9989,16 +10021,16 @@ "license": "MIT" }, "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==", "dev": true, "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" @@ -10113,10 +10145,11 @@ } }, "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "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==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -11124,14 +11157,15 @@ } }, "node_modules/vite": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.3.3.tgz", - "integrity": "sha512-NPQdeCU0Dv2z5fu+ULotpuq5yfCS1BzKUIPhNbP3YBfAMGJXbt2nS+sbTFu+qchaqWTD+H3JK++nRwr6XIcp6A==", + "version": "5.4.8", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.8.tgz", + "integrity": "sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==", "dev": true, + "license": "MIT", "dependencies": { "esbuild": "^0.21.3", - "postcss": "^8.4.39", - "rollup": "^4.13.0" + "postcss": "^8.4.43", + "rollup": "^4.20.0" }, "bin": { "vite": "bin/vite.js" @@ -11150,6 +11184,7 @@ "less": "*", "lightningcss": "^1.21.0", "sass": "*", + "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" @@ -11167,6 +11202,9 @@ "sass": { "optional": true }, + "sass-embedded": { + "optional": true + }, "stylus": { "optional": true }, diff --git a/helm/Chart.yaml b/helm/Chart.yaml index e00364bcb7..670a5277ab 100644 --- a/helm/Chart.yaml +++ b/helm/Chart.yaml @@ -2,6 +2,7 @@ apiVersion: v2 name: infrahub description: A Helm chart to deploy Infrahub on Kubernetes +icon: https://github.com/opsmill/infrahub/raw/develop/frontend/app/public/favicons/logo512.png # A chart can be either an 'application' or a 'library' chart. # # Application charts are a collection of templates that can be packaged into versioned archives @@ -14,16 +15,16 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 2.7.3 +version: 3.0.0 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "0.16.4" +appVersion: "1.0.0" dependencies: - name: neo4j - version: "5.19.0" + version: "5.20.0" repository: "https://helm.neo4j.com/neo4j/" condition: neo4j.enabled - name: common @@ -50,3 +51,7 @@ dependencies: version: "28.2.0" repository: "https://traefik.github.io/charts" condition: traefik.enabled + - name: prefect-server + version: "2024.7.25191224" + repository: "https://prefecthq.github.io/prefect-helm" + condition: prefect-server.enabled diff --git a/helm/templates/_env.tpl b/helm/templates/_env.tpl index c33bf831fe..d261f58a79 100644 --- a/helm/templates/_env.tpl +++ b/helm/templates/_env.tpl @@ -87,45 +87,45 @@ Define default env variables if required. {{- end }} {{- end }} -{{- define "infrahub-helm.infrahubGit.defaultEnv" -}} -{{- if not .Values.infrahubGit.infrahubGit.env.KUBERNETES_CLUSTER_DOMAIN }} +{{- define "infrahub-helm.infrahubTaskWorker.defaultEnv" -}} +{{- if not .Values.infrahubTaskWorker.infrahubTaskWorker.env.KUBERNETES_CLUSTER_DOMAIN }} - name: KUBERNETES_CLUSTER_DOMAIN value: {{ quote .Values.global.kubernetesClusterDomain }} {{- end }} -{{- if not .Values.infrahubGit.infrahubGit.env.INFRAHUB_ADDRESS }} +{{- if not .Values.infrahubTaskWorker.infrahubTaskWorker.env.INFRAHUB_ADDRESS }} - name: INFRAHUB_ADDRESS - value: http://{{ include "infrahub-helm.fullname" . }}-infrahub-server:8000 + value: http://{{ include "infrahub-helm.fullname" . }}-infrahub-server.{{ .Release.Namespace }}.svc.{{ .Values.global.kubernetesClusterDomain }}:8000 {{- end }} -{{- if not .Values.infrahubGit.infrahubGit.env.INFRAHUB_INTERNAL_ADDRESS }} +{{- if not .Values.infrahubTaskWorker.infrahubTaskWorker.env.INFRAHUB_INTERNAL_ADDRESS }} - name: INFRAHUB_INTERNAL_ADDRESS - value: "http://{{ include "infrahub-helm.fullname" . }}-infrahub-server:8000" + value: "http://{{ include "infrahub-helm.fullname" . }}-infrahub-server.{{ .Release.Namespace }}.svc.{{ .Values.global.kubernetesClusterDomain }}:8000" {{- end }} -{{- if not .Values.infrahubGit.infrahubGit.env.INFRAHUB_DB_ADDRESS }} +{{- if not .Values.infrahubTaskWorker.infrahubTaskWorker.env.INFRAHUB_DB_ADDRESS }} - name: INFRAHUB_DB_ADDRESS - value: "{{ include "infrahub-helm.fullname" . }}-database" + value: "{{ include "infrahub-helm.fullname" . }}-database.{{ .Release.Namespace }}.svc.{{ .Values.global.kubernetesClusterDomain }}" {{- end }} -{{- if not .Values.infrahubGit.infrahubGit.env.INFRAHUB_DB_PORT }} +{{- if not .Values.infrahubTaskWorker.infrahubTaskWorker.env.INFRAHUB_DB_PORT }} - name: INFRAHUB_DB_PORT value: "{{ .Values.neo4j.services.neo4j.ports.bolt.port }}" {{- end }} -{{- if not .Values.infrahubGit.infrahubGit.env.INFRAHUB_BROKER_ADDRESS }} +{{- if not .Values.infrahubTaskWorker.infrahubTaskWorker.env.INFRAHUB_BROKER_ADDRESS }} - name: INFRAHUB_BROKER_ADDRESS - value: "{{ include "infrahub-helm.fullname" . }}-message-queue" + value: "{{ include "infrahub-helm.fullname" . }}-message-queue.{{ .Release.Namespace }}.svc.{{ .Values.global.kubernetesClusterDomain }}" {{- end }} -{{- if not .Values.infrahubGit.infrahubGit.env.INFRAHUB_BROKER_USERNAME }} +{{- if not .Values.infrahubTaskWorker.infrahubTaskWorker.env.INFRAHUB_BROKER_USERNAME }} - name: INFRAHUB_BROKER_USERNAME value: {{ .Values.rabbitmq.auth.username | quote }} {{- end }} -{{- if not .Values.infrahubGit.infrahubGit.env.INFRAHUB_BROKER_PASSWORD }} +{{- if not .Values.infrahubTaskWorker.infrahubTaskWorker.env.INFRAHUB_BROKER_PASSWORD }} - name: INFRAHUB_BROKER_PASSWORD value: {{ .Values.rabbitmq.auth.password | quote }} {{- end }} -{{- if not .Values.infrahubGit.infrahubGit.env.INFRAHUB_CACHE_ADDRESS }} +{{- if not .Values.infrahubTaskWorker.infrahubTaskWorker.env.INFRAHUB_CACHE_ADDRESS }} - name: INFRAHUB_CACHE_ADDRESS - value: "{{ include "infrahub-helm.fullname" . }}-cache-master" + value: "{{ include "infrahub-helm.fullname" . }}-cache-master.{{ .Release.Namespace }}.svc.{{ .Values.global.kubernetesClusterDomain }}" {{- end }} -{{- if not .Values.infrahubGit.infrahubGit.env.INFRAHUB_CACHE_PORT }} +{{- if not .Values.infrahubTaskWorker.infrahubTaskWorker.env.INFRAHUB_CACHE_PORT }} - name: INFRAHUB_CACHE_PORT value: "{{ .Values.redis.master.service.ports.redis }}" {{- end }} -{{- end }} +{{- end }} \ No newline at end of file diff --git a/helm/templates/infrahub-demo-data-job.yaml b/helm/templates/infrahub-demo-data-job.yaml index eb940e45c1..e5c6cc8921 100644 --- a/helm/templates/infrahub-demo-data-job.yaml +++ b/helm/templates/infrahub-demo-data-job.yaml @@ -15,9 +15,7 @@ spec: spec: containers: - command: - - sh - - -c - - "infrahubctl schema load models/base --wait 30 || infrahubctl schema load models/infrastructure_base.yml && infrahubctl run models/infrastructure_edge.py && infrahubctl repository add demo-edge https://github.com/opsmill/infrahub-demo-edge --read-only" + {{- toYaml .Values.infrahubDemoData.command | nindent 12 }} env: {{- include "infrahub-helm.infrahubDemoData.defaultEnv" . | nindent 12 }} {{- with .Values.infrahubDemoData.env }} diff --git a/helm/templates/infrahub-git.yaml b/helm/templates/infrahub-git.yaml deleted file mode 100644 index 8faff1c918..0000000000 --- a/helm/templates/infrahub-git.yaml +++ /dev/null @@ -1,94 +0,0 @@ ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ include "infrahub-helm.fullname" . }}-infrahub-git - namespace: "{{ .Release.Namespace }}" - labels: - service: infrahub-git - {{- include "infrahub-helm.labels" . | nindent 4 }} - annotations: - {{- include "infrahub-helm.annotations" . | nindent 4 }} -spec: - replicas: {{ .Values.infrahubGit.replicas | default 1 }} - selector: - matchLabels: - service: infrahub-git - {{- include "infrahub-helm.selectorLabels" . | nindent 6 }} - template: - metadata: - labels: - service: infrahub-git - {{- include "infrahub-helm.selectorLabels" . | nindent 8 }} - spec: - {{- with .Values.infrahubGit.affinity }} - affinity: {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.infrahubGit.tolerations }} - tolerations: {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.infrahubGit.runtimeClassName }} - runtimeClassName: {{ . }} - {{- end }} - {{- with .Values.infrahubGit.podSecurityContext }} - securityContext: {{- toYaml . | nindent 8 }} - {{- end }} - containers: - - args: {{- toYaml .Values.infrahubGit.infrahubGit.args | nindent 12 }} - env: - {{- include "infrahub-helm.infrahubGit.defaultEnv" . | nindent 12 }} - {{- range $key, $value := .Values.infrahubGit.infrahubGit.env }} - - name: {{ $key }} - value: {{ $value | quote }} - {{- end }} - {{- with .Values.infrahubGit.infrahubGit.envFromExistingSecret }} - envFrom: - - secretRef: - name: {{ . }} - {{- end }} - image: {{ default .Values.global.imageRegistry .Values.infrahubGit.infrahubGit.imageRegistry }}/{{ .Values.global.infrahubRepository }}:{{ .Values.global.infrahubTag | default .Chart.AppVersion }} - imagePullPolicy: {{ default .Values.global.imagePullPolicy .Values.infrahubGit.infrahubGit.imagePullPolicy }} - name: infrahub-git - {{- with .Values.infrahubGit.resources }} - resources: {{- toYaml . | nindent 12 }} - {{- end }} - {{- with .Values.infrahubGit.infrahubGit.securityContext }} - securityContext: {{- toYaml . | nindent 12 }} - {{- end }} - tty: true - volumeMounts: - - name: git-data - mountPath: /opt/infrahub/git - restartPolicy: Always - volumes: - {{- if and .Values.infrahubGit.persistence.enabled }} - - name: git-data - persistentVolumeClaim: - claimName: {{ tpl (.Values.infrahubGit.persistence.existingClaim | default (printf "%s-%s" (include "infrahub-helm.fullname" .) "git-data")) . }} - {{- else }} - - name: git-data - emptyDir: {} - {{- end }} - -{{- if and .Values.infrahubGit.persistence.enabled (not .Values.infrahubGit.persistence.existingClaim) }} ---- -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: {{ include "infrahub-helm.fullname" . }}-git-data - namespace: "{{ .Release.Namespace }}" - labels: - service: git-data - {{- include "infrahub-helm.labels" . | nindent 4 }} - annotations: - {{- include "infrahub-helm.annotations" . | nindent 4 }} -spec: - accessModes: - - {{ .Values.infrahubGit.persistence.accessMode }} - resources: - requests: - storage: {{ .Values.infrahubGit.persistence.size | quote }} - {{- with .Values.infrahubGit.persistence.storageClassName }} - storageClassName: {{ . }} - {{- end }} -{{- end }} diff --git a/helm/templates/infrahub-server.yaml b/helm/templates/infrahub-server.yaml index d4bbe2ee2e..65117bcd24 100644 --- a/helm/templates/infrahub-server.yaml +++ b/helm/templates/infrahub-server.yaml @@ -19,7 +19,14 @@ spec: metadata: labels: service: infrahub-server + {{- with .Values.infrahubServer.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} {{- include "infrahub-helm.selectorLabels" . | nindent 8 }} + {{- with .Values.infrahubServer.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} spec: {{- with .Values.infrahubServer.affinity }} affinity: {{- toYaml . | nindent 8 }} @@ -128,4 +135,4 @@ spec: {{- with .Values.infrahubServer.persistence.storageClassName }} storageClassName: {{ . }} {{- end }} -{{- end }} +{{- end }} \ No newline at end of file diff --git a/helm/templates/infrahub-task-worker.yaml b/helm/templates/infrahub-task-worker.yaml new file mode 100644 index 0000000000..1132d09a94 --- /dev/null +++ b/helm/templates/infrahub-task-worker.yaml @@ -0,0 +1,99 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "infrahub-helm.fullname" . }}-infrahub-task-worker + namespace: "{{ .Release.Namespace }}" + labels: + service: infrahub-task-worker + {{- include "infrahub-helm.labels" . | nindent 4 }} + annotations: + {{- include "infrahub-helm.annotations" . | nindent 4 }} +spec: + replicas: {{ .Values.infrahubTaskWorker.replicas | default 2 }} + selector: + matchLabels: + service: infrahub-task-worker + {{- include "infrahub-helm.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + service: infrahub-task-worker + {{- with .Values.infrahubTaskWorker.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- include "infrahub-helm.selectorLabels" . | nindent 8 }} + {{- with .Values.infrahubTaskWorker.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.infrahubTaskWorker.affinity }} + affinity: {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.infrahubTaskWorker.tolerations }} + tolerations: {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.infrahubTaskWorker.runtimeClassName }} + runtimeClassName: {{ . }} + {{- end }} + {{- with .Values.infrahubTaskWorker.podSecurityContext }} + securityContext: {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: infrahub-task-worker + args: {{- toYaml .Values.infrahubTaskWorker.infrahubTaskWorker.args | nindent 12 }} + env: + {{- include "infrahub-helm.infrahubTaskWorker.defaultEnv" . | nindent 12 }} + {{- range $key, $value := .Values.infrahubTaskWorker.infrahubTaskWorker.env }} + - name: {{ $key }} + value: {{ $value | quote }} + {{- end }} + {{- with .Values.infrahubTaskWorker.infrahubTaskWorker.envFromExistingSecret }} + envFrom: + - secretRef: + name: {{ . }} + {{- end }} + image: {{ default .Values.global.imageRegistry .Values.infrahubTaskWorker.infrahubTaskWorker.imageRegistry }}/{{ .Values.global.infrahubRepository }}:{{ .Values.global.infrahubTag | default .Chart.AppVersion }} + imagePullPolicy: {{ default .Values.global.imagePullPolicy .Values.infrahubTaskWorker.infrahubTaskWorker.imagePullPolicy }} + {{- with .Values.infrahubTaskWorker.resources }} + resources: {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.infrahubTaskWorker.infrahubTaskWorker.securityContext }} + securityContext: {{- toYaml . | nindent 12 }} + {{- end }} + tty: true + volumeMounts: + - name: git-data + mountPath: /opt/infrahub/git + restartPolicy: Always + volumes: + {{- if and .Values.infrahubTaskWorker.persistence.enabled }} + - name: git-data + persistentVolumeClaim: + claimName: {{ tpl (.Values.infrahubTaskWorker.persistence.existingClaim | default (printf "%s-%s" (include "infrahub-helm.fullname" .) "git-data")) . }} + {{- else }} + - name: git-data + emptyDir: {} + {{- end }} + +{{- if and .Values.infrahubTaskWorker.persistence.enabled (not .Values.infrahubTaskWorker.persistence.existingClaim) }} +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "infrahub-helm.fullname" . }}-git-data + namespace: "{{ .Release.Namespace }}" + labels: + service: git-data + {{- include "infrahub-helm.labels" . | nindent 4 }} +spec: + accessModes: + - {{ .Values.infrahubTaskWorker.persistence.accessMode }} + resources: + requests: + storage: {{ .Values.infrahubTaskWorker.persistence.size | quote }} + {{- with .Values.infrahubTaskWorker.persistence.storageClassName }} + storageClassName: {{ . }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/helm/values.yaml b/helm/values.yaml index 4851144d9c..7af95650d3 100644 --- a/helm/values.yaml +++ b/helm/values.yaml @@ -1,3 +1,4 @@ +# yamllint disable rule:line-length --- global: kubernetesClusterDomain: cluster.local @@ -52,8 +53,8 @@ neo4j: dbms.security.procedures.unrestricted: apoc.* logInitialPassword: false -# ----------- Infrahub GIT ----------- -infrahubGit: +# ----------- Infrahub Task Worker ----------- +infrahubTaskWorker: replicas: 2 persistence: enabled: true @@ -61,20 +62,29 @@ infrahubGit: accessMode: ReadWriteMany storageClassName: nfs resources: {} - infrahubGit: + infrahubTaskWorker: args: - - infrahub - - git-agent + - prefect + - worker - start - - --debug + - --type + - infrahubasync + - --pool + - infrahub-worker + - --with-healthcheck env: INFRAHUB_CACHE_PORT: 6379 INFRAHUB_DB_TYPE: neo4j INFRAHUB_LOG_LEVEL: DEBUG INFRAHUB_PRODUCTION: "false" INFRAHUB_API_TOKEN: 06438eb2-8019-4776-878c-0941b1f1d1ec - INFRAHUB_TIMEOUT: "20" + INFRAHUB_TIMEOUT: "60" INFRAHUB_GIT_REPOSITORIES_DIRECTORY: "/opt/infrahub/git" + INFRAHUB_WORKFLOW_ADDRESS: prefect-server + INFRAHUB_WORKFLOW_PORT: 4200 + PREFECT_API_URL: "http://prefect-server:4200/api" + PREFECT_WORKER_QUERY_SECONDS: 3 + PREFECT_AGENT_QUERY_INTERVAL: 3 imagePullPolicy: Always imageRegistry: registry.opsmill.io @@ -110,6 +120,9 @@ infrahubServer: INFRAHUB_INITIAL_ADMIN_TOKEN: 06438eb2-8019-4776-878c-0941b1f1d1ec INFRAHUB_SECURITY_SECRET_KEY: 327f747f-efac-42be-9e73-999f08f86b92 INFRAHUB_GIT_REPOSITORIES_DIRECTORY: "/opt/infrahub/git" + INFRAHUB_WORKFLOW_ADDRESS: prefect-server + INFRAHUB_WORKFLOW_PORT: 4200 + PREFECT_API_URL: "http://prefect-server:4200/api" imagePullPolicy: Always imageRegistry: registry.opsmill.io ports: @@ -117,7 +130,6 @@ infrahubServer: port: 8000 targetPort: 8000 - infrahubServerDbInitJob: enabled: false backoffLimit: 0 @@ -136,6 +148,10 @@ infrahubDemoData: imageRegistry: registry.opsmill.io env: INFRAHUB_API_TOKEN: 06438eb2-8019-4776-878c-0941b1f1d1ec + command: + - sh + - -c + - "infrahubctl schema load models/base --wait 30 && infrahubctl run models/infrastructure_edge.py && infrahubctl menu load models/base_menu.yml && infrahubctl repository add demo-edge https://github.com/opsmill/infrahub-demo-edge --read-only" # ----------- Mesage Queue (Rabbit MQ) ----------- rabbitmq: @@ -166,6 +182,23 @@ nats: jetstream: enabled: true +# ------------- Prefect ------------- +prefect-server: + enabled: true + server: + image: + prefectTag: 3.0.3-python3.12-kubernetes + env: + - name: PREFECT_UI_SERVE_BASE + value: / + serviceAccount: + create: false + postgresql: + enabled: true + primary: + persistence: + enabled: false + # -------------- Cloud -------------- traefik: enabled: false diff --git a/models/base/dcim.yml b/models/base/dcim.yml index 1970facb99..b4cf2aa222 100644 --- a/models/base/dcim.yml +++ b/models/base/dcim.yml @@ -8,7 +8,6 @@ generics: label: "Interface" include_in_menu: true icon: "mdi:ethernet" - menu_placement: "InfraDevice" display_labels: - name__value order_by: @@ -101,6 +100,7 @@ generics: identifier: "device__interface" optional: false cardinality: one + order_weight: 1 kind: Parent - name: tags peer: BuiltinTag @@ -150,7 +150,6 @@ generics: label: "MLAG Interface" icon: "mdi:ethernet" include_in_menu: true - menu_placement: "InfraDevice" attributes: - name: mlag_id label: MLAG Id @@ -159,6 +158,7 @@ generics: relationships: - name: mlag_domain label: MLAG Domain + order_weight: 1 peer: InfraMlagDomain kind: Attribute cardinality: one @@ -242,6 +242,7 @@ nodes: optional: false cardinality: one kind: Attribute + order_weight: 1 identifier: "site__devices" - name: interfaces peer: InfraInterface @@ -430,7 +431,6 @@ nodes: description: "A Platform represents the type of software running on a device" label: "Platform" icon: "mdi:application-cog-outline" - menu_placement: "InfraDevice" default_filter: name__value human_friendly_id: ["name__value"] order_by: @@ -467,7 +467,6 @@ nodes: namespace: Infra description: "A Circuit represent a single physical link between two locations" label: "Circuit" - icon: "mdi:cable-data" default_filter: circuit_id__value human_friendly_id: ["circuit_id__value"] order_by: @@ -478,12 +477,14 @@ nodes: - name: circuit_id kind: Text unique: true + order_weight: 2 - name: description kind: Text optional: true - name: vendor_id kind: Text optional: true + order_weight: 3 - name: status kind: Dropdown choices: @@ -525,6 +526,7 @@ nodes: optional: false cardinality: one kind: Attribute + order_weight: 1 - name: endpoints peer: InfraCircuitEndpoint optional: true @@ -540,8 +542,6 @@ nodes: namespace: Infra description: "A Circuit endpoint is attached to each end of a circuit" label: "Circuit Endpoint" - icon: "mdi:ethernet-cable" - menu_placement: "InfraCircuit" include_in_menu: false attributes: - name: description @@ -566,8 +566,6 @@ nodes: namespace: Infra description: "Represents the group of devices that share interfaces in a multi chassis link aggregation group" label: "MLAG Domain" - icon: "eos-icons:cluster-management" - menu_placement: "InfraDevice" include_in_menu: true display_labels: - name__value @@ -616,6 +614,7 @@ nodes: namespace: Infra description: "L3 MLAG Interface" label: "MLAG Interface L3" + icon: "mdi:ethernet" include_in_menu: false display_labels: - mlag_id__value diff --git a/models/base/ipam.yml b/models/base/ipam.yml index 78befdbb16..929c9c51a3 100644 --- a/models/base/ipam.yml +++ b/models/base/ipam.yml @@ -39,11 +39,13 @@ nodes: - name: name kind: Text unique: true + order_weight: 2 - name: description kind: Text optional: true - name: vlan_id kind: Number + order_weight: 3 - name: status kind: Dropdown choices: @@ -86,6 +88,7 @@ nodes: cardinality: one kind: Attribute identifier: "site__vlans" + order_weight: 1 - name: gateway label: L3 Gateway peer: InfraInterfaceL3 diff --git a/models/base/location.yml b/models/base/location.yml index 407655d5f9..797995e648 100644 --- a/models/base/location.yml +++ b/models/base/location.yml @@ -23,7 +23,6 @@ nodes: - name: Continent namespace: Location description: "A continent on planet earth." - menu_placement: "LocationGeneric" icon: "jam:world" inherit_from: - "LocationGeneric" @@ -38,7 +37,6 @@ nodes: - name: Country namespace: Location description: "A country within a continent." - menu_placement: "LocationGeneric" icon: "gis:search-country" inherit_from: - "LocationGeneric" @@ -53,7 +51,6 @@ nodes: - name: Site namespace: Location description: "A site within a country." - menu_placement: "LocationGeneric" icon: "ri:building-line" inherit_from: - "LocationGeneric" diff --git a/models/base/organization.yml b/models/base/organization.yml index 29e0ef0a03..2940071bfe 100644 --- a/models/base/organization.yml +++ b/models/base/organization.yml @@ -46,7 +46,6 @@ nodes: - OrganizationGeneric generate_profile: false include_in_menu: true - menu_placement: OrganizationGeneric relationships: - name: platform peer: InfraPlatform @@ -63,7 +62,6 @@ nodes: inherit_from: - OrganizationGeneric include_in_menu: true - menu_placement: OrganizationGeneric relationships: - name: location peer: LocationSite @@ -84,7 +82,6 @@ nodes: inherit_from: - OrganizationGeneric include_in_menu: true - menu_placement: OrganizationGeneric relationships: - name: location peer: LocationSite diff --git a/models/base/routing.yml b/models/base/routing.yml index 93629b20be..ba9a720d25 100644 --- a/models/base/routing.yml +++ b/models/base/routing.yml @@ -8,7 +8,6 @@ nodes: description: "An Autonomous System (AS) is a set of Internet routable IP prefixes belonging to a network" label: "Autonomous System" icon: "mdi:bank-circle-outline" - menu_placement: InfraBGPSession default_filter: name__value human_friendly_id: ["name__value", "asn__value"] order_by: ["asn__value"] @@ -19,9 +18,11 @@ nodes: - name: name kind: Text unique: true + order_weight: 1 - name: asn kind: Number unique: true + order_weight: 2 - name: description kind: Text optional: true @@ -31,12 +32,12 @@ nodes: optional: false cardinality: one kind: Attribute + order_weight: 3 - name: BGPPeerGroup namespace: Infra description: "A BGP Peer Group is used to regroup parameters that are shared across multiple peers" label: "BGP Peer Group" icon: "mdi:view-grid-plus-outline" - menu_placement: InfraBGPSession default_filter: name__value human_friendly_id: ["name__value"] order_by: @@ -47,15 +48,18 @@ nodes: - name: name kind: Text unique: true + order_weight: 1 - name: description kind: Text optional: true - name: import_policies kind: Text optional: true + order_weight: 4 - name: export_policies kind: Text optional: true + order_weight: 5 relationships: - name: local_as identifier: bgppeergroup__local_as @@ -63,12 +67,14 @@ nodes: optional: true cardinality: one kind: Attribute + order_weight: 2 - name: remote_as identifier: bgppeergroup__remote_as peer: InfraAutonomousSystem optional: true cardinality: one kind: Attribute + order_weight: 3 - name: BGPSession namespace: Infra description: "A BGP Session represent a point to point connection between two routers" @@ -84,17 +90,21 @@ nodes: - name: type kind: Text enum: [EXTERNAL, INTERNAL] + order_weight: 1 - name: description kind: Text optional: true - name: import_policies kind: Text optional: true + order_weight: 8 - name: export_policies kind: Text optional: true + order_weight: 9 - name: status kind: Dropdown + order_weight: 6 choices: - name: active label: Active @@ -114,6 +124,7 @@ nodes: color: "#bfbfbf" - name: role kind: Dropdown + order_weight: 7 choices: - name: backbone label: Backbone @@ -134,24 +145,28 @@ nodes: optional: true cardinality: one kind: Attribute + order_weight: 2 - name: remote_as identifier: bgpsession__remote_as peer: InfraAutonomousSystem optional: false cardinality: one kind: Attribute + order_weight: 3 - name: local_ip identifier: bgpsession__local_ip peer: BuiltinIPAddress optional: true cardinality: one kind: Attribute + order_weight: 4 - name: remote_ip identifier: bgpsession__remote_ip peer: BuiltinIPAddress optional: false cardinality: one kind: Attribute + order_weight: 5 - name: device peer: InfraDevice optional: false diff --git a/models/base/service.yml b/models/base/service.yml index c965139eaa..ba31d0c35e 100644 --- a/models/base/service.yml +++ b/models/base/service.yml @@ -15,13 +15,13 @@ generics: kind: Text label: Name optional: false + order_weight: 1 nodes: - name: BackBoneService namespace: Infra description: "Backbone Service" label: "Backbone Service" icon: "carbon:container-services" - menu_placement: "InfraService" inherit_from: - InfraService uniqueness_constraints: @@ -31,24 +31,31 @@ nodes: kind: Text label: Circuit ID optional: false + order_weight: 3 - name: internal_circuit_id kind: Text label: Internal Circuit ID optional: false + order_weight: 2 relationships: - name: provider cardinality: one peer: OrganizationProvider optional: false + kind: Attribute - name: site_a label: Site A cardinality: one peer: LocationSite optional: false identifier: infrabackboneservice__location_site_a + kind: Attribute + order_weight: 4 - name: site_b label: Site B cardinality: one peer: LocationSite optional: false identifier: infrabackboneservice__location_site_b + kind: Attribute + order_weight: 5 diff --git a/models/base_menu.yml b/models/base_menu.yml new file mode 100644 index 0000000000..96da36d917 --- /dev/null +++ b/models/base_menu.yml @@ -0,0 +1,186 @@ +--- +apiVersion: infrahub.app/v1 +kind: Menu +spec: + data: + - namespace: Organization + name: MainMenu + label: Organization + icon: "mdi:domain" + kind: OrganizationGeneric + children: + data: + - namespace: Organization + name: Generic + label: All Organizations + kind: OrganizationGeneric + icon: "mdi:domain" + + - namespace: Organization + name: Manufacturer + label: Manufacturer + kind: OrganizationManufacturer + icon: "mdi:domain" + + - namespace: Organization + name: Provider + label: Provider + kind: OrganizationProvider + icon: "mdi:domain" + + - namespace: Organization + name: Tenant + label: Tenant + kind: OrganizationTenant + icon: "mdi:domain" + + - namespace: Location + name: Menu + label: Location + kind: LocationGeneric + icon: "mingcute:location-line" + children: + data: + - namespace: Location + name: Generic + label: All Locations + kind: LocationGeneric + icon: "mingcute:location-line" + + - namespace: Location + name: Continent + label: Continent + kind: LocationContinent + icon: "jam:world" + + - namespace: Location + name: Country + label: Country + kind: LocationCountry + icon: "gis:search-country" + + - namespace: Location + name: Site + label: Site + kind: LocationSite + icon: "ri:building-line" + + - namespace: Location + name: Rack + label: Rack + kind: LocationRack + icon: clarity:rack-server-solid + + - namespace: Infra + name: DeviceManagementMenu + label: Device Management + icon: "mdi:server" + children: + data: + - namespace: Infra + name: NetworkDeviceMenu + label: Network Device + icon: "mdi:server" + children: + data: + - namespace: Infra + name: Device + label: Device + kind: InfraDevice + icon: "mdi:server" + - name: Interface + namespace: Infra + label: "Interface" + icon: "mdi:ethernet" + kind: InfraInterface + + - namespace: Infra + name: MlagMenu + label: MLAG + icon: "eos-icons:cluster-management" + children: + data: + - name: MlagDomain + namespace: Infra + label: "MLAG Domain" + icon: "eos-icons:cluster-management" + kind: InfraMlagDomain + + - name: MlagInterface + namespace: Infra + label: "MLAG Interface" + icon: "mdi:ethernet" + kind: InfraMlagInterface + + - namespace: Infra + name: Platform + label: Platform + kind: InfraPlatform + icon: "mdi:application-cog-outline" + + + - namespace: Infra + name: CircuitMenu + label: Circuit Management + icon: "mdi:transit-connection-variant" + children: + data: + - name: Circuit + namespace: Infra + label: "Circuit" + icon: "mdi:cable-data" + kind: InfraCircuit + + - namespace: Infra + name: NetworkMenu + label: Network Configuration + icon: "mdi:lan" + children: + data: + - name: VLAN + namespace: Infra + label: "VLAN" + icon: "mdi:lan-pending" + kind: InfraVLAN + + - namespace: Infra + name: RoutingPeeringMenu + label: Routing & Peering + icon: "mdi:router" + children: + data: + - name: BGP + namespace: Infra + label: "BGP" + icon: "mdi:router" + children: + data: + - name: AutonomousSystem + namespace: Infra + label: "Autonomous System" + icon: "mdi:bank-circle-outline" + kind: InfraAutonomousSystem + + - name: BGPSession + namespace: Infra + label: "BGP Session" + icon: "mdi:router" + kind: InfraBGPSession + + - name: BGPPeerGroup + namespace: Infra + label: "BGP Peer Group" + icon: "mdi:view-grid-plus-outline" + kind: InfraBGPPeerGroup + + - namespace: Infra + name: Service + label: Services + icon: "carbon:container-services" + children: + data: + - namespace: Infra + name: BackBoneService + label: "Backbone Service" + kind: InfraBackBoneService + icon: "carbon:container-services" diff --git a/models/examples/security/infrastructure_security.py b/models/examples/security/infrastructure_security.py index 06a56b04bf..a4941c5c02 100644 --- a/models/examples/security/infrastructure_security.py +++ b/models/examples/security/infrastructure_security.py @@ -2,7 +2,11 @@ import random from ipaddress import IPv4Interface, IPv4Network -from infrahub_sdk import InfrahubClient, InfrahubNode, NodeStore +from infrahub_sdk import InfrahubClient +from infrahub_sdk.node import InfrahubNode +from infrahub_sdk.store import NodeStore + +from infrahub_sdk.protocols import CoreAccount # flake8: noqa # pylint: skip-file @@ -179,8 +183,7 @@ async def run(client: InfrahubClient, log: logging.Logger, branch: str): async for node, _ in batch.execute(): prepare_log(node, log) - account_security = store.get("security-builder") - store.get("John Doe") + account_security = store.get(key="security-builder", kind=CoreAccount, raise_when_missing=True) # ------------------------------------------ # Create Status, Role & Tags diff --git a/models/infrastructure_edge.py b/models/infrastructure_edge.py index 35d44de1c1..66a397e3c3 100644 --- a/models/infrastructure_edge.py +++ b/models/infrastructure_edge.py @@ -1,22 +1,177 @@ +import copy import logging +import time import uuid from collections import defaultdict +from enum import Enum from ipaddress import IPv4Network, IPv6Network -from typing import Optional +from typing import Optional, cast -from infrahub_sdk import UUIDT, InfrahubClient, InfrahubNode, NodeStore +from infrahub_sdk import InfrahubClient from infrahub_sdk.batch import InfrahubBatch +from infrahub_sdk.exceptions import NodeNotFoundError +from infrahub_sdk.protocols import ( + CoreAccount, + CoreAccountGroup, + CoreAccountRole, + CoreGlobalPermission, + CoreIPAddressPool, + CoreIPPrefixPool, + CoreObjectPermission, + CoreStandardGroup, + IpamNamespace, +) +from infrahub_sdk.protocols_base import CoreNode +from infrahub_sdk.store import NodeStore +from infrahub_sdk.uuidt import UUIDT +from protocols import ( + InfraAutonomousSystem, + InfraBGPSession, + InfraCircuit, + InfraCircuitEndpoint, + InfraDevice, + InfraInterfaceL2, + InfraInterfaceL3, + InfraLagInterfaceL2, + InfraMlagDomain, + InfraMlagInterfaceL2, + InfraPlatform, + InfraVLAN, + IpamIPAddress, + IpamIPPrefix, + LocationCountry, + LocationSite, + OrganizationProvider, +) from pydantic import BaseModel, ConfigDict, Field +PROFILES = { + "small": {"num_sites": 2, "num_device_per_site": 6, "has_bgp_mesh": False, "has_branch": False}, + "medium": {"num_sites": 5, "num_device_per_site": 6, "has_bgp_mesh": True, "has_branch": True}, + "large": {"num_sites": 10, "num_device_per_site": 26, "has_bgp_mesh": False, "has_branch": False}, + "x-large": {"num_sites": 50, "num_device_per_site": 52, "has_bgp_mesh": False, "has_branch": False}, + "xx-large": {"num_sites": 100, "num_device_per_site": 102, "has_bgp_mesh": False, "has_branch": False}, + "ultimate": {"num_sites": 200, "num_device_per_site": 204, "has_bgp_mesh": True, "has_branch": True}, +} + + +class ConfigError(Exception): + pass + + +# Define the global configuration object +class GlobalConfig: + def __init__(self) -> None: + self.default_profile_name = "medium" + self.num_sites = None + self.num_device_per_site = None + self.has_bgp_mesh = False + self.has_branch = False + + def __set_config(self, num_sites: int, num_device_per_site: int, has_bgp_mesh: bool, has_branch: bool) -> None: + # TODO: I guess it could be defined in the attribute itself? + # Ensure that num_site is between boudaries + if 2 <= int(num_sites) <= 200: + self.num_sites = int(num_sites) + else: + raise ConfigError(f"Value for `num_sites` ({num_sites}) should be between 2 and 200.") + + # Ensure that num_device_per_site is between boudaries + if 6 <= int(num_device_per_site) <= 204: + self.num_device_per_site = int(num_device_per_site) + else: + raise ConfigError(f"Value for `num_device_per_site` ({num_device_per_site}) should be between 6 and 204.") + + self.has_bgp_mesh = has_bgp_mesh + self.has_branch = has_branch + + def load_config( + self, + profile: str = None, + num_sites: int = None, + num_device_per_site: int = None, + has_bgp_mesh: bool = None, + has_branch: bool = None, + ) -> None: + if profile: + # Warn user that we are going to ignore his input + if num_sites or num_device_per_site or has_bgp_mesh or has_branch: + raise ConfigError("You can't set additional config items if you've already provided a profile.") + + # Make sure profile exists + if profile not in PROFILES: + raise ConfigError( + f"Value for profile ({profile}) doesn't exist, please pick one among {PROFILES.keys()}." + ) + + # Load prebuilt profile + profile_obj: dict = PROFILES[profile] + self.__set_config( + profile_obj["num_sites"], + profile_obj["num_device_per_site"], + profile_obj["has_bgp_mesh"], + profile_obj["has_branch"], + ) + else: + # Load from manual arguments, if provided + # If user only provides a part of the arguments e.g. only `number of site` + # we fall back on medium profile by default + default_profile: dict = PROFILES[self.default_profile_name] + + self.__set_config( + num_sites=num_sites if num_sites is not None else default_profile["num_sites"], + num_device_per_site=num_device_per_site + if num_device_per_site is not None + else default_profile["num_device_per_site"], + has_bgp_mesh=has_bgp_mesh if has_bgp_mesh is not None else default_profile["has_bgp_mesh"], + has_branch=has_branch if has_branch is not None else default_profile["has_branch"], + ) + + def __repr__(self) -> str: + return f"Config(Sites: {self.num_sites}, Devices per site: {self.num_device_per_site}, BGP mesh: {self.has_bgp_mesh}, Additional branches: {self.has_branch})" + + +def translate_str_to_bool(key: str, value: str) -> bool: + if value == "True": + return True + if value == "False": + return False + raise TypeError(f"Value for {key} should be 'True' or 'False'") + # pylint: skip-file +class AccountRole(BaseModel): + name: str + global_permissions: list[str] | str | None = None + object_permissions: list[str] | str | None = None + + +class AccountGroup(BaseModel): + name: str + roles: list[str] = Field(default_factory=list) + members: list[str] = Field(default_factory=list) + + class Account(BaseModel): name: str + label: str password: str account_type: str role: str +class GlobalPermission(BaseModel): + action: str + decision: int + + +class ObjectPermission(BaseModel): + namespace: str + name: str + action: str + decision: int + + class Asn(BaseModel): asn: int organization: str @@ -42,6 +197,7 @@ class Device(BaseModel): role: str tags: list[str] platform: str + _idx: int @property def l2_interface_names(self) -> list[str]: @@ -107,7 +263,7 @@ class P2pNetwork(BaseModel): site2: str edge: int circuit: str - pool: Optional[InfrahubNode] = None + pool: Optional[IpamIPPrefix] = None model_config = ConfigDict(arbitrary_types_allowed=True) @@ -129,6 +285,11 @@ def provider_name(self) -> str: return "Lumen" return "Zayo" + def get_pool(self) -> IpamIPPrefix: + if self.pool: + return self.pool + raise Exception("the variable pool hasn't been initilized yet") + class Platform(BaseModel): name: str @@ -212,68 +373,120 @@ class Vlan(BaseModel): ), ) -DEVICES = ( - Device( - name="edge1", + +class DevicePatternName(str, Enum): + LEAF = "LEAF" + CORE = "CORE" + EDGE = "EDGE" + + +DEVICE_PATTERNS = { + DevicePatternName.LEAF: Device( + name="leaf", status="active", - type="7280R3", + type="7010TX-48", profile="profile1", - role="edge", + role="leaf", tags=["red", "green"], - platform="Arista EOS", - ), - Device( - name="edge2", - status="active", - type="ASR1002-HX", - profile="profile1", - role="edge", - tags=["red", "blue", "green"], platform="Cisco IOS", ), - Device( - name="core1", - status="drained", + DevicePatternName.CORE: Device( + name="core", + status="active", type="MX204", profile="profile1", role="core", tags=["blue"], platform="Juniper JunOS", ), - Device( - name="core2", - status="provisioning", - type="MX204", - profile="profile1", - role="core", - tags=["red"], - platform="Juniper JunOS", - ), - Device( - name="leaf1", - status="active", - type="7010TX-48", - profile="profile1", - role="leaf", - tags=["red", "green"], - platform="Arista EOS", - ), - Device( - name="leaf2", + DevicePatternName.EDGE: Device( + name="edge", status="active", - type="7010TX-48", + type="7280R3", profile="profile1", - role="leaf", + role="edge", tags=["red", "green"], platform="Arista EOS", ), -) +} + +DEVICE_STATUSES = ["active", "provisioning", "drained"] + + +class SiteDesign: + def __init__(self, number_of_device: int) -> None: + """Takes the number of devices that need to be created on a given site. + This method will decide how many device of each type to create and return all those objects as a list.""" + if number_of_device > 0: + self.number_of_device = number_of_device + else: + raise ValueError("number_of_device must be non-negative") + + # There is a special case where there are 6 device... + if number_of_device == 6: + # Two of each + self.num_edge_device = 2 + self.num_core_device = 2 + self.num_leaf_device = 2 + + # Otherwise we try to compute something that makes a little bit of sense... + else: + # First we decide how many edge device we will spin + # The rule is the following: + # - between 0 -> 50 = 2 edges + # - then we add 2 edges every 50 devices + num_edge_device: int = 2 + num_edge_device += (self.number_of_device // 50) * 2 + self.num_edge_device = num_edge_device + + # Second goes core device, we take one third of the remaining device allocation + self.num_core_device: int = (self.number_of_device - self.num_edge_device) // 3 + + # Finally we allocate what's remaining as leaf + self.num_leaf_device: int = self.number_of_device - self.num_edge_device - self.num_core_device + + def device_generator(self, number: int, device_pattern_name: DevicePatternName) -> list[Device]: + """Generate a list of devices following the pattern provided.""" + result: list[Device] = [] + + for i in range(1, number + 1): + # Take the pattern as baseline + current_device: Device = copy.copy(DEVICE_PATTERNS[device_pattern_name]) + + # Start the tweaking + current_device.name += str(i) + current_device._idx = i + + # Add it to the list + result.append(current_device) + + # Return devices + return result + + def implement(self) -> list[Device]: + # Build the list of device + result: list[Device] = [] + + # Generate the list and return it + result.extend(self.device_generator(self.num_edge_device, DevicePatternName.EDGE)) + result.extend(self.device_generator(self.num_core_device, DevicePatternName.CORE)) + result.extend(self.device_generator(self.num_leaf_device, DevicePatternName.LEAF)) + + return result + + def __repr__(self) -> str: + return f"SiteDesign(Edge device: {self.num_edge_device}, Core device: {self.num_core_device}, Leaf device: {self.num_leaf_device})" NETWORKS_SUPERNET = IPv4Network("10.0.0.0/8") -NETWORKS_SUPERNET_IPV6 = IPv6Network("2001:DB8::/112") -NETWORKS_POOL_EXTERNAL_SUPERNET = IPv4Network("203.0.113.0/24") -MANAGEMENT_NETWORKS = IPv4Network("172.20.20.0/27") +NETWORKS_SUPERNET_IPV6 = IPv6Network("2001:DB8::/100") +MANAGEMENT_NETWORKS = IPv4Network("172.16.0.0/16") + +# Here with current logic we allocate 3 /29 per edge device +# We have max 10 edges on a single site, max 200 sites +# 3*10*200 = 6000 -> we need to be able to fit 6000 /29 +# Thus we need a /16 +NETWORKS_POOL_EXTERNAL_SUPERNET = IPv4Network("203.111.0.0/16") ACTIVE_STATUS = "active" BACKBONE_ROLE = "backbone" @@ -368,7 +581,9 @@ def site_generator(nbr_site: int = 2) -> list[Site]: ], } -LAG_INTERFACE_L2_ROLES_MAPPING = {"leaf": {"port-channel1": "peer", "port-channel2": "server"}} +LAG_INTERFACE_L2_ROLES_MAPPING: dict[str, dict[str, str]] = { + "leaf": {"port-channel1": "peer", "port-channel2": "server"} +} INTERFACE_L2_MODE_MAPPING = {"peer": "Trunk (ALL)"} @@ -433,19 +648,66 @@ def site_generator(nbr_site: int = 2) -> list[Site]: Asn(asn=7018, organization="AT&T Services"), ) -INTERFACE_OBJS: dict[str, list[InfrahubNode]] = defaultdict(list) +INTERFACE_OBJS: dict[str, list[InfraInterfaceL3]] = defaultdict(list) + +GLOBAL_PERMISSIONS = ( + GlobalPermission(action="edit_default_branch", decision=6), + GlobalPermission(action="merge_branch", decision=6), + GlobalPermission(action="merge_proposed_change", decision=6), + GlobalPermission(action="manage_schema", decision=6), + GlobalPermission(action="manage_accounts", decision=6), + GlobalPermission(action="manage_permissions", decision=6), + GlobalPermission(action="manage_repositories", decision=6), +) + +OBJECT_PERMISSIONS = { + "deny_any": ObjectPermission(namespace="*", name="*", action="any", decision=1), + "allow_any": ObjectPermission(namespace="*", name="*", action="any", decision=6), + "allow_branches": ObjectPermission(namespace="*", name="*", action="any", decision=4), + "view_any": ObjectPermission(namespace="*", name="*", action="view", decision=6), +} + +ACCOUNT_ROLES = ( + AccountRole(name="Administrator", global_permissions="__all__", object_permissions=["allow_any"]), + AccountRole(name="Global read-only", object_permissions=["deny_any", "view_any"]), + AccountRole( + name="Global read-write", + global_permissions=["edit_default_branch", "merge_branch", "merge_proposed_change"], + object_permissions=["allow_any"], + ), + AccountRole(name="Own branches read-write", object_permissions=["allow_branches"]), +) ACCOUNTS = ( - Account(name="pop-builder", account_type="Script", password="Password123", role="read-write"), - Account(name="CRM Synchronization", account_type="Script", password="Password123", role="read-write"), - Account(name="Jack Bauer", account_type="User", password="Password123", role="read-only"), - Account(name="Chloe O'Brian", account_type="User", password="Password123", role="read-write"), - Account(name="David Palmer", account_type="User", password="Password123", role="read-write"), - Account(name="Operation Team", account_type="User", password="Password123", role="read-only"), - Account(name="Engineering Team", account_type="User", password="Password123", role="read-write"), - Account(name="Architecture Team", account_type="User", password="Password123", role="read-only"), + Account(name="pop-builder", label="pop-builder", account_type="Script", password="Password123", role="read-write"), + Account( + name="crm-sync", label="CRM Synchronization", account_type="Script", password="Password123", role="read-write" + ), + Account(name="jbauer", label="Jack Bauer", account_type="User", password="Password123", role="read-only"), + Account(name="cobrian", label="Chloe O'Brian", account_type="User", password="Password123", role="read-write"), + Account(name="dpalmer", label="David Palmer", account_type="User", password="Password123", role="read-write"), + Account(name="sudo", label="Sue Dough", password="Password123", role="admin", account_type="User"), + Account(name="elawson", label="Emily Lawson", password="Password123", role="read-write", account_type="User"), + Account(name="jthompson", label="Jacob Thompson", password="Password123", role="read-write", account_type="User"), + Account(name="shernandez", label="Sofia Hernandez", password="Password123", role="read-write", account_type="User"), + Account(name="rpatel", label="Ryan Patel", password="Password123", role="read-only", account_type="User"), + Account(name="ocarter", label="Olivia Carter", password="Password123", role="read-only", account_type="User"), ) +ACCOUNT_GROUPS = { + "administrators": AccountGroup( + name="Administrators", roles=["Administrator"], members=["sudo", "pop-builder", "crm-sync"] + ), + "ops-team": AccountGroup( + name="Operations Team", roles=["Global read-only"], members=["jbauer", "elawson", "jthompson"] + ), + "eng-team": AccountGroup( + name="Engineering Team", roles=["Global read-write"], members=["cobrian", "shernandez", "rpatel"] + ), + "arch-team": AccountGroup( + name="Architecture Team", roles=["Own branches read-write"], members=["dpalmer", "ocarter"] + ), +} GROUPS = ( Group(name="edge_router", label="Edge Router"), @@ -510,15 +772,41 @@ def site_generator(nbr_site: int = 2) -> list[Site]: store = NodeStore() +async def find_and_connect_interfaces( + batch: InfrahubBatch, + log: logging.Logger, + interface_kind: InfraInterfaceL2 | InfraInterfaceL3, + first_device_name: str, + first_interface_name: str, + second_device_name: str, + second_interface_name: str, +) -> None: + # Connecting first interface to second interface + first_interface = store.get(kind=interface_kind, key=first_interface_name) + second_interface = store.get(kind=interface_kind, key=second_interface_name) + + first_interface.description.value = f"Connected to {second_device_name}::{second_interface.name.value}" + first_interface.connected_endpoint = second_interface + batch.add(task=first_interface.save, node=first_interface) + + # Adjust description on second interface + second_interface.description.value = f"Connected to {first_device_name}::{first_interface.name.value}" + batch.add(task=second_interface.save, node=second_interface) + + log.info( + f" - Connected '{first_device_name}::{first_interface_name}' <> '{second_device_name}::{second_interface_name}'" + ) + + async def apply_interface_profiles(client: InfrahubClient, log: logging.Logger, branch: str) -> None: # ------------------------------------------ # Add profile on interfaces upstream/backbone # ------------------------------------------ log.info("Starting to apply profiles to interfaces") - upstream_interfaces = await client.filters(branch=branch, kind="InfraInterfaceL3", role__value="upstream") - backbone_interfaces = await client.filters(branch=branch, kind="InfraInterfaceL3", role__value="backbone") - upstream_profile = store.get(key="upstream_profile", kind="ProfileInfraInterfaceL3") - backbone_profile = store.get(key="backbone_profile", kind="ProfileInfraInterfaceL3") + upstream_interfaces = await client.filters(branch=branch, kind=InfraInterfaceL3, role__value="upstream") + backbone_interfaces = await client.filters(branch=branch, kind=InfraInterfaceL3, role__value="backbone") + upstream_profile = store.get(key="upstream_profile", kind="ProfileInfraInterfaceL3", raise_when_missing=True) + backbone_profile = store.get(key="backbone_profile", kind="ProfileInfraInterfaceL3", raise_when_missing=True) batch = await client.create_batch() for interface in upstream_interfaces: @@ -550,8 +838,8 @@ async def create_backbone_connectivity( # CREATE Backbone Links & Circuits # -------------------------------------------------- log.info("Creating Backbone Links & Circuits") - account_pop = store.get("pop-builder") - interconnection_pool = store.get("interconnection_pool") + account_pop = store.get("pop-builder", kind=CoreAccount, raise_when_missing=True) + interconnection_pool = store.get("interconnection_pool", kind=CoreAccount, raise_when_missing=True) networks: list[P2pNetwork] = [] @@ -566,7 +854,7 @@ async def create_backbone_connectivity( for network in networks: network.pool = await client.allocate_next_ip_prefix( - resource_pool=interconnection_pool, branch=branch, identifier=network.identifier + resource_pool=interconnection_pool, kind=IpamIPPrefix, branch=branch, identifier=network.identifier ) log.info("- Done allocating addresses") @@ -575,12 +863,12 @@ async def create_backbone_connectivity( intf1 = INTERFACE_OBJS[backbone_link.site1_device].pop(0) intf2 = INTERFACE_OBJS[backbone_link.site2_device].pop(0) - backbone_link_ips = backbone_link.pool.prefix.value.hosts() + backbone_link_ips = backbone_link.get_pool().prefix.value.hosts() provider = store.get(kind="OrganizationProvider", key=backbone_link.provider_name) obj = await client.create( branch=branch, - kind="InfraCircuit", + kind=InfraCircuit, description=f"Backbone {backbone_link.site1} <-> {backbone_link.site2}", circuit_id=backbone_link.circuit, vendor_id=f"{backbone_link.provider_name.upper()}-{UUIDT().short()}", @@ -594,7 +882,7 @@ async def create_backbone_connectivity( # Create Circuit Endpoints endpoint1 = await client.create( branch=branch, - kind="InfraCircuitEndpoint", + kind=InfraCircuitEndpoint, description=f"Endpoint {backbone_link.circuit} to {backbone_link.site1_device}", site=backbone_link.site1, circuit=obj, @@ -604,7 +892,7 @@ async def create_backbone_connectivity( endpoint2 = await client.create( branch=branch, - kind="InfraCircuitEndpoint", + kind=InfraCircuitEndpoint, description=f"Endpoint {backbone_link.circuit} to {backbone_link.site2_device}", site=backbone_link.site2, circuit=obj, @@ -617,25 +905,25 @@ async def create_backbone_connectivity( intf21_address = f"{str(next(backbone_link_ips))}/31" intf11_ip = await client.create( branch=branch, - kind="IpamIPAddress", + kind=IpamIPAddress, interface={"id": intf1.id, "source": account_pop.id}, address={"value": intf11_address, "source": account_pop.id}, ) await intf11_ip.save() intf21_ip = await client.create( branch=branch, - kind="IpamIPAddress", + kind=IpamIPAddress, interface={"id": intf2.id, "source": account_pop.id}, address={"value": intf21_address, "source": account_pop.id}, ) await intf21_ip.save() # Update Interface - intf11 = await client.get(branch=branch, kind="InfraInterfaceL3", id=intf1.id) + intf11 = await client.get(branch=branch, kind=InfraInterfaceL3, id=intf1.id) intf11.description.value = f"Backbone: Connected to {backbone_link.site2_device} via {backbone_link.circuit}" await intf11.save() - intf21 = await client.get(branch=branch, kind="InfraInterfaceL3", id=intf2.id) + intf21 = await client.get(branch=branch, kind=InfraInterfaceL3, id=intf2.id) intf21.description.value = f"Backbone: Connected to {backbone_link.site1_device} via {backbone_link.circuit}" await intf21.save() @@ -651,7 +939,7 @@ async def create_bgp_mesh(client: InfrahubClient, log: logging.Logger, branch: s log.info("Creating Full Mesh iBGP SESSION between all the Edge devices") batch = await client.create_batch() num_sites = len(sites) - internal_as = store.get(kind="InfraAutonomousSystem", key="Duff") + internal_as = store.get(kind=InfraAutonomousSystem, key="Duff", raise_when_missing=True) for site1 in sites: for site2 in sites: @@ -663,8 +951,8 @@ async def create_bgp_mesh(client: InfrahubClient, log: logging.Logger, branch: s device1 = f"{site1.name}-edge{idx1}" device2 = f"{site2.name}-edge{idx2}" - loopback1 = store.get(key=f"{device1}-loopback") - loopback2 = store.get(key=f"{device2}-loopback") + loopback1 = store.get(key=f"{device1}-loopback", kind=InfraInterfaceL3, raise_when_missing=True) + loopback2 = store.get(key=f"{device2}-loopback", kind=InfraInterfaceL3, raise_when_missing=True) peer_group_name = "POP_GLOBAL" @@ -676,8 +964,8 @@ async def create_bgp_mesh(client: InfrahubClient, log: logging.Logger, branch: s local_ip=loopback1.id, remote_as=internal_as.id, remote_ip=loopback2.id, - peer_group=store.get(key=peer_group_name).id, - device=store.get(kind="InfraDevice", key=device1).id, + peer_group=store.get(key=peer_group_name, raise_when_missing=True).id, + device=store.get(kind=InfraDevice, key=device1, raise_when_missing=True).id, status=ACTIVE_STATUS, role=BACKBONE_ROLE, ) @@ -694,15 +982,15 @@ async def create_bgp_mesh(client: InfrahubClient, log: logging.Logger, branch: s async def generate_site_vlans( client: InfrahubClient, log: logging.Logger, branch: str, site: Site, site_id: int ) -> None: - account_pop = store.get("pop-builder") - group_eng = store.get("Engineering Team") - group_ops = store.get("Operation Team") + account_pop = store.get("pop-builder", kind=CoreAccount, raise_when_missing=True) + group_eng = store.get("eng-team", kind=CoreAccountGroup, raise_when_missing=True) + group_ops = store.get("ops-team", kind=CoreAccountGroup, raise_when_missing=True) for vlan in VLANS: vlan_name = f"{site.name}_{vlan.role}" obj = await client.create( branch=branch, - kind="InfraVLAN", + kind=InfraVLAN, site={"id": site_id, "source": account_pop.id, "is_protected": True}, name={"value": vlan_name, "is_protected": True, "source": account_pop.id}, vlan_id={"value": vlan.id, "is_protected": True, "owner": group_eng.id, "source": account_pop.id}, @@ -719,18 +1007,18 @@ async def generate_site_mlag_domain(client: InfrahubClient, log: logging.Logger, # -------------------------------------------------- for role, domain in MLAG_DOMAINS.items(): devices = [ - store.get(kind="InfraDevice", key=f"{site.name}-{role}1"), - store.get(kind="InfraDevice", key=f"{site.name}-{role}2"), + store.get(kind=InfraDevice, key=f"{site.name}-{role}1"), + store.get(kind=InfraDevice, key=f"{site.name}-{role}2"), ] name = f"{site.name}-{role}-12" peer_interfaces = [ - store.get(kind="InfraLagInterfaceL2", key=f"{device_obj.name.value}-lagl2-{domain['peer_interfaces'][idx]}") + store.get(kind=InfraLagInterfaceL2, key=f"{device_obj.name.value}-lagl2-{domain['peer_interfaces'][idx]}") # type: ignore[index] for idx, device_obj in enumerate(devices) ] mlag_domain = await client.create( - kind="InfraMlagDomain", + kind=InfraMlagDomain, name=name, domain_id=domain["domain_id"], devices=devices, @@ -745,19 +1033,19 @@ async def generate_site_mlag_domain(client: InfrahubClient, log: logging.Logger, # -------------------------------------------------- for role, mlags in MLAG_INTERFACE_L2.items(): devices = [ - store.get(kind="InfraDevice", key=f"{site.name}-{role}1"), - store.get(kind="InfraDevice", key=f"{site.name}-{role}2"), + store.get(kind=InfraDevice, key=f"{site.name}-{role}1"), + store.get(kind=InfraDevice, key=f"{site.name}-{role}2"), ] for mlag in mlags: members = [ - store.get(kind="InfraLagInterfaceL2", key=f"{device_obj.name.value}-lagl2-{mlag['members'][idx]}") + store.get(kind=InfraLagInterfaceL2, key=f"{device_obj.name.value}-lagl2-{mlag['members'][idx]}") # type: ignore[index] for idx, device_obj in enumerate(devices) ] - mlag_domain = store.get(kind="InfraMlagDomain", key=f"mlag-domain-{site.name}-{role}-12") + mlag_domain = store.get(kind=InfraMlagDomain, key=f"mlag-domain-{site.name}-{role}-12") mlag_interface = await client.create( - kind="InfraMlagInterfaceL2", mlag_domain=mlag_domain, mlag_id=mlag["mlag_id"], members=members + kind=InfraMlagInterfaceL2, mlag_domain=mlag_domain, mlag_id=mlag["mlag_id"], members=members ) await mlag_interface.save() @@ -768,18 +1056,19 @@ async def generate_site( log: logging.Logger, branch: str, site: Site, - interconnection_pool: InfrahubNode, - loopback_pool: InfrahubNode, - management_pool: InfrahubNode, - external_pool: InfrahubNode, + interconnection_pool: CoreNode, + loopback_pool: CoreNode, + management_pool: CoreNode, + external_pool: CoreNode, + site_design: SiteDesign, ) -> str: - group_eng = store.get("Engineering Team") - group_ops = store.get("Operation Team") - account_pop = store.get("pop-builder") - account_crm = store.get("CRM Synchronization") - internal_as = store.get(kind="InfraAutonomousSystem", key="Duff") + group_eng = store.get("eng-team", kind=CoreAccountGroup) + group_ops = store.get("ops-team", kind=CoreAccountGroup) + account_pop = store.get("pop-builder", kind=CoreAccount) + account_crm = store.get("crm-sync", kind=CoreAccount) + internal_as = store.get(kind=InfraAutonomousSystem, key="Duff") - country = store.get(kind="LocationCountry", key=site.country) + country = store.get(kind=LocationCountry, key=site.country) # -------------------------------------------------- # Create the Site # -------------------------------------------------- @@ -799,33 +1088,62 @@ async def generate_site( # -------------------------------------------------- # Create the site specific IP prefixes # -------------------------------------------------- - peer_networks = [ - await client.allocate_next_ip_prefix(resource_pool=interconnection_pool, branch=branch), - await client.allocate_next_ip_prefix(resource_pool=interconnection_pool, branch=branch), - ] - peer_network_hosts = {0: peer_networks[0].prefix.value.hosts(), 1: peer_networks[1].prefix.value.hosts()} + # TODO: Refactor that part for the sake of readability + # Here we dispatch to every p2p a /31 prefixe + # Between two edges we have 2 p2p connections so 2 prefixes + # So far we connect edge1<->edge2 then edge3<->edge4 ... + peer_networks: list[IpamIPPrefix] = [] + peer_network_hosts = { + # 0: {0: peer_networks[0].prefix.value.hosts(), 1: peer_networks[1].prefix.value.hosts()}, + # ^ Device id ^ interface id + } + + # Here we need as much prefix as we have edge device + for i in range(site_design.num_edge_device): + peer_networks.append( + await client.allocate_next_ip_prefix(resource_pool=interconnection_pool, kind=IpamIPPrefix, branch=branch) + ) - group_core_router_members = [] - group_edge_router_members = [] - group_cisco_devices_members = [] - group_arista_devices_members = [] + # Then we prepare all ips for all interfaces + # TODO: Refactor that part for the sake of readability + for i in range(1, site_design.num_edge_device, 2): + peer_network_hosts[i] = { + 0: peer_networks[i - 1].prefix.value.hosts(), + 1: peer_networks[i].prefix.value.hosts(), + } + peer_network_hosts[i + 1] = { + 0: peer_networks[i - 1].prefix.value.hosts(), + 1: peer_networks[i].prefix.value.hosts(), + } + + group_core_router_members: list[str] = [] + group_edge_router_members: list[str] = [] + group_cisco_devices_members: list[str] = [] + group_arista_devices_members: list[str] = [] group_upstream_interfaces_members = [] group_backbone_interfaces_members = [] - for idx, device in enumerate(DEVICES): + # -------------------------------------------------- + # Create devices + # -------------------------------------------------- + # Craft the list of devices + devices: list[Device] = site_design.implement() + + # TODO: There is room for improvement here, batch those device together + for device in devices: device_name = f"{site.name}-{device.name}" - platform_id = store.get(kind="InfraPlatform", key=device.platform).id + platform_id = store.get(kind=InfraPlatform, key=device.platform).id obj = await client.create( branch=branch, - kind="InfraDevice", + kind=InfraDevice, site={"id": site_obj.id, "source": account_pop.id, "is_protected": True}, name={"value": device_name, "source": account_pop.id, "is_protected": True}, status={"value": device.status, "owner": group_ops.id}, type={"value": device.type, "source": account_pop.id}, role={"value": device.role, "source": account_pop.id, "is_protected": True, "owner": group_eng.id}, asn={"id": internal_as.id, "source": account_pop.id, "is_protected": True, "owner": group_eng.id}, - tags=[store.get(kind="BuiltinTag", key=tag_name).id for tag_name in device.tags], + tags=[store.get(kind="BuiltinTag", key=tag_name, raise_when_missing=True).id for tag_name in device.tags], platform={"id": platform_id, "source": account_pop.id, "is_protected": True}, ) await obj.save() @@ -846,7 +1164,7 @@ async def generate_site( # Loopback Interface intf = await client.create( branch=branch, - kind="InfraInterfaceL3", + kind=InfraInterfaceL3, device={"id": obj.id, "is_protected": True}, name={"value": "Loopback0", "source": account_pop.id, "is_protected": True}, enabled=True, @@ -864,7 +1182,7 @@ async def generate_site( # Management Interface intf = await client.create( branch=branch, - kind="InfraInterfaceL3", + kind=InfraInterfaceL3, device={"id": obj.id, "is_protected": True}, name={"value": INTERFACE_MGMT_NAME[device.type], "source": account_pop.id}, enabled={"value": True, "owner": group_eng.id}, @@ -876,18 +1194,20 @@ async def generate_site( management_ip = await client.allocate_next_ip_address( resource_pool=management_pool, identifier=device_name, data={"interface": intf.id}, branch=branch ) + management_ip = cast(IpamIPAddress, management_ip) # set the IP address of the device to the management interface IP address - obj.primary_address = management_ip + obj.primary_address = management_ip # type: ignore[assignment] await obj.save() # L3 Interfaces + # TODO: There is room for improvement here for intf_idx, intf_name in enumerate(device.l3_interface_names): intf_role = INTERFACE_L3_ROLES_MAPPING[device.role][intf_idx] intf = await client.create( branch=branch, - kind="InfraInterfaceL3", + kind=InfraInterfaceL3, device={"id": obj.id, "is_protected": True}, name=intf_name, speed=10000, @@ -905,14 +1225,18 @@ async def generate_site( subnet = None address = None if intf_role == "peer": - address = f"{str(next(peer_network_hosts[intf_idx]))}/31" + # TODO: Refactor that part for the sake of readability + address = f"{str(next(peer_network_hosts[device._idx][intf_idx]))}/31" if intf_role == "upstream": group_upstream_interfaces_members.append(intf.id) if intf_role in ["upstream", "peering"] and "edge" in device.role: subnet = await client.allocate_next_ip_prefix( - resource_pool=external_pool, identifier=f"{device_name}__{intf_role}__{intf_idx}", branch=branch + kind=IpamIPPrefix, + resource_pool=external_pool, + identifier=f"{device_name}__{intf_role}__{intf_idx}", + branch=branch, ) subnet_hosts = subnet.prefix.value.hosts() address = f"{str(next(subnet_hosts))}/29" @@ -921,7 +1245,7 @@ async def generate_site( if address: ip = await client.create( branch=branch, - kind="IpamIPAddress", + kind=IpamIPAddress, interface={"id": intf.id, "source": account_pop.id}, address={"value": address, "source": account_pop.id}, ) @@ -938,11 +1262,11 @@ async def generate_site( elif intf_role == "peering": provider_name = "Equinix" - provider = store.get(kind="OrganizationProvider", key=provider_name) + provider = store.get(kind=OrganizationProvider, key=provider_name, raise_when_missing=True) circuit = await client.create( branch=branch, - kind="InfraCircuit", + kind=InfraCircuit, circuit_id=circuit_id, vendor_id=f"{provider_name.upper()}-{UUIDT().short()}", provider=provider.id, @@ -954,7 +1278,7 @@ async def generate_site( endpoint1 = await client.create( branch=branch, - kind="InfraCircuitEndpoint", + kind=InfraCircuitEndpoint, site=site_obj, circuit=circuit.id, connected_endpoint=intf.id, @@ -970,22 +1294,22 @@ async def generate_site( peer_ip = await client.create( branch=branch, - kind="IpamIPAddress", + kind=IpamIPAddress, address=peer_address, ) await peer_ip.save() - peer_as = store.get(kind="InfraAutonomousSystem", key=provider_name) + peer_as = store.get(kind=InfraAutonomousSystem, key=provider_name) bgp_session = await client.create( branch=branch, - kind="InfraBGPSession", + kind=InfraBGPSession, type="EXTERNAL", local_as=internal_as.id, local_ip=ip.id, remote_as=peer_as.id, remote_ip=peer_ip.id, - peer_group=store.get(key=peer_group_name).id, - device=store.get(key=device_name).id, + peer_group=store.get(key=peer_group_name, raise_when_missing=True).id, + device=store.get(key=device_name, raise_when_missing=True).id, status=ACTIVE_STATUS, role=intf_role, ) @@ -1009,11 +1333,11 @@ async def generate_site( untagged_vlan = None if l2_mode == "Access": - untagged_vlan = store.get(kind="InfraVLAN", key=f"{site.name}_server") + untagged_vlan = store.get(kind=InfraVLAN, key=f"{site.name}_server") intf = await client.create( branch=branch, - kind="InfraInterfaceL2", + kind=InfraInterfaceL2, device={"id": obj.id, "is_protected": True}, name=intf_name, speed=10000, @@ -1032,7 +1356,7 @@ async def generate_site( for lag_intf in LAG_INTERFACE_L2.get(device.type, []): try: - intf_role = LAG_INTERFACE_L2_ROLES_MAPPING[device.role][lag_intf["name"]] + intf_role = LAG_INTERFACE_L2_ROLES_MAPPING[device.role][lag_intf["name"]] # type: ignore[index] except KeyError: intf_role = "server" @@ -1042,11 +1366,11 @@ async def generate_site( untagged_vlan = None if l2_mode == "Access": - untagged_vlan = store.get(kind="InfraVLAN", key=f"{site.name}_server") + untagged_vlan = store.get(kind=InfraVLAN, key=f"{site.name}_server") lag = await client.create( branch=branch, - kind="InfraLagInterfaceL2", + kind=InfraLagInterfaceL2, device={"id": obj.id, "is_protected": True}, name=lag_intf["name"], description=description, @@ -1063,118 +1387,112 @@ async def generate_site( store.set(key=f"{device_name}-lagl2-{lag_intf['name']}", node=lag) - members = [store.get(key=f"{device_name}-l2-{member}").id for member in lag_intf["members"]] + members = [ + store.get(key=f"{device_name}-l2-{member}", raise_when_missing=True).id + for member in lag_intf["members"] + ] await lag.add_relationships(relation_to_update="members", related_nodes=members) await generate_site_mlag_domain(client=client, log=log, branch=branch, site=site) + # Create a batch for all those connections + batch_interface: InfrahubBatch = await client.create_batch() + # -------------------------------------------------- - # Connect both devices within the Site together with 2 interfaces + # Connect edge devices 2 by 2 # -------------------------------------------------- - for idx in range(2): - intf1 = store.get(kind="InfraInterfaceL3", key=f"{site.name}-edge1-l3-{idx}") - intf2 = store.get(kind="InfraInterfaceL3", key=f"{site.name}-edge2-l3-{idx}") - - intf1.description.value = f"Connected to {site.name}-edge2 {intf2.name.value}" - intf1.connected_endpoint = intf2 - await intf1.save() - - intf2.description.value = f"Connected to {site.name}-edge1 {intf1.name.value}" - await intf2.save() + for idx in range(1, site_design.num_edge_device, 2): + # Connecting eth 0 to eth 0 + await find_and_connect_interfaces( + batch_interface, + log, + InfraInterfaceL3, + f"{site.name}-edge{idx}", + f"{site.name}-edge{idx}-l3-0", + f"{site.name}-edge{idx + 1}", + f"{site.name}-edge{idx + 1}-l3-0", + ) - log.info(f" - Connected '{site.name}-edge1::{intf1.name.value}' <> '{site.name}-edge2::{intf2.name.value}'") + # Connecting eth 1 to eth 1 + await find_and_connect_interfaces( + batch_interface, + log, + InfraInterfaceL3, + f"{site.name}-edge{idx}", + f"{site.name}-edge{idx}-l3-1", + f"{site.name}-edge{idx + 1}", + f"{site.name}-edge{idx + 1}-l3-1", + ) # -------------------------------------------------- - # Connect both leaf devices within a Site together with the 2 peer interfaces + # Connect leaf devices 2 by 2 # -------------------------------------------------- - for idx in range(2): - intf1 = store.get(kind="InfraInterfaceL2", key=f"{site.name}-leaf1-l2-Ethernet{idx + 1}") - intf2 = store.get(kind="InfraInterfaceL2", key=f"{site.name}-leaf2-l2-Ethernet{idx + 1}") - - intf1.description.value = f"Connected to {site.name}-leaf2 {intf2.name.value}" - intf1.connected_endpoint = intf2 - await intf1.save() + for idx in range(1, site_design.num_leaf_device, 2): + # Connecting eth 1 to eth 1 + await find_and_connect_interfaces( + batch_interface, + log, + InfraInterfaceL2, + f"{site.name}-leaf{idx}", + f"{site.name}-leaf{idx}-l2-Ethernet1", + f"{site.name}-leaf{idx + 1}", + f"{site.name}-leaf{idx + 1}-l2-Ethernet1", + ) - intf2.description.value = f"Connected to {site.name}-leaf1 {intf1.name.value}" - await intf2.save() + # Connecting eth 2 to eth 2 + await find_and_connect_interfaces( + batch_interface, + log, + InfraInterfaceL2, + f"{site.name}-leaf{idx}", + f"{site.name}-leaf{idx}-l2-Ethernet2", + f"{site.name}-leaf{idx + 1}", + f"{site.name}-leaf{idx + 1}-l2-Ethernet2", + ) - log.info(f" - Connected '{site.name}-leaf1::{intf1.name.value}' <> '{site.name}-leaf2::{intf2.name.value}'") + async for node, _ in batch_interface.execute(): + log.info(f"- Saving {node._schema.kind} - {node.name.value}") # -------------------------------------------------- # Update all the group we may have touched during the site creation # -------------------------------------------------- - if group_edge_router_members: - group_edge_router = store.get(kind="CoreStandardGroup", key="edge_router") + group_edge_router = store.get(kind=CoreStandardGroup, key="edge_router") await group_edge_router.add_relationships(relation_to_update="members", related_nodes=group_edge_router_members) if group_core_router_members: - group_core_router = store.get(kind="CoreStandardGroup", key="core_router") + group_core_router = store.get(kind=CoreStandardGroup, key="core_router") await group_core_router.add_relationships(relation_to_update="members", related_nodes=group_core_router_members) if group_cisco_devices_members: - group_cisco_devices = store.get(kind="CoreStandardGroup", key="cisco_devices") + group_cisco_devices = store.get(kind=CoreStandardGroup, key="cisco_devices") await group_cisco_devices.add_relationships( relation_to_update="members", related_nodes=group_cisco_devices_members ) + if group_arista_devices_members: - group_arista_devices = store.get(kind="CoreStandardGroup", key="arista_devices") + group_arista_devices = store.get(kind=CoreStandardGroup, key="arista_devices") await group_arista_devices.add_relationships( relation_to_update="members", related_nodes=group_arista_devices_members ) if group_upstream_interfaces_members: - group_upstream_interfaces = store.get(kind="CoreStandardGroup", key="upstream_interfaces") + group_upstream_interfaces = store.get(kind=CoreStandardGroup, key="upstream_interfaces") await group_upstream_interfaces.add_relationships( relation_to_update="members", related_nodes=group_upstream_interfaces_members ) if group_backbone_interfaces_members: - group_backbone_interfaces = store.get(kind="CoreStandardGroup", key="backbone_interfaces") + group_backbone_interfaces = store.get(kind=CoreStandardGroup, key="backbone_interfaces") await group_backbone_interfaces.add_relationships( relation_to_update="members", related_nodes=group_backbone_interfaces_members ) - # -------------------------------------------------- - # Create iBGP Sessions within the Site - # -------------------------------------------------- - for idx in range(2): - if idx == 0: - device1 = f"{site.name}-{DEVICES[0].name}" - device2 = f"{site.name}-{DEVICES[1].name}" - elif idx == 1: - device1 = f"{site.name}-{DEVICES[1].name}" - device2 = f"{site.name}-{DEVICES[0].name}" - - peer_group_name = "POP_INTERNAL" - - loopback1 = store.get(key=f"{device1}-loopback") - loopback2 = store.get(key=f"{device2}-loopback") - - obj = await client.create( - branch=branch, - kind="InfraBGPSession", - type="INTERNAL", - local_as=internal_as.id, - local_ip=loopback1.id, - remote_as=internal_as.id, - remote_ip=loopback2.id, - peer_group=store.get(key=peer_group_name).id, - device=store.get(kind="InfraDevice", key=device1).id, - status=ACTIVE_STATUS, - role=BACKBONE_ROLE, - ) - await obj.save() - - log.info( - f" - Created BGP Session '{device1}' >> '{device2}': '{peer_group_name}' '{loopback1.address.value}' >> '{loopback2.address.value}'" - ) - return site.name async def branch_scenario_add_upstream( - client: InfrahubClient, log: logging.Logger, site_name: str, external_pool: InfrahubNode + client: InfrahubClient, log: logging.Logger, site_name: str, external_pool: CoreNode ) -> None: """ Create a new branch and Add a new upstream link with GTT on the edge1 device of the given site. @@ -1188,16 +1506,16 @@ async def branch_scenario_add_upstream( ) log.info(f"- Creating branch: {new_branch_name!r}") # Querying the object for now, need to pull from the store instead - site = await client.get(branch=new_branch_name, kind="LocationSite", name__value=site_name) - device = await client.get(branch=new_branch_name, kind="InfraDevice", name__value=device_name) + site = await client.get(branch=new_branch_name, kind=LocationSite, name__value=site_name) + device = await client.get(branch=new_branch_name, kind=InfraDevice, name__value=device_name) gtt_organization = await client.get( - branch=new_branch_name, kind="OrganizationProvider", name__value="GTT Communications" + branch=new_branch_name, kind=OrganizationProvider, name__value="GTT Communications" ) role_spare = "spare" intfs = await client.filters( - branch=new_branch_name, kind="InfraInterfaceL3", device__ids=[device.id], role__value=role_spare + branch=new_branch_name, kind=InfraInterfaceL3, device__ids=[device.id], role__value=role_spare ) intf = intfs[0] log.info(f" - Adding new Upstream on '{device_name}::{intf.name.value}'") @@ -1206,20 +1524,21 @@ async def branch_scenario_add_upstream( subnet = await client.allocate_next_ip_prefix( resource_pool=external_pool, identifier=device_name, branch=new_branch_name ) + subnet = cast(IpamIPPrefix, subnet) subnet_hosts = subnet.prefix.value.hosts() address = f"{str(next(subnet_hosts))}/29" peer_address = f"{str(next(subnet_hosts))}/29" peer_ip = await client.create( branch=new_branch_name, - kind="IpamIPAddress", + kind=IpamIPAddress, address=peer_address, ) await peer_ip.save() ip = await client.create( branch=new_branch_name, - kind="IpamIPAddress", + kind=IpamIPAddress, interface={"id": intf.id}, address={"value": address}, ) @@ -1230,7 +1549,7 @@ async def branch_scenario_add_upstream( circuit = await client.create( branch=new_branch_name, - kind="InfraCircuit", + kind=InfraCircuit, circuit_id=circuit_id, vendor_id=f"{gtt_organization.name.value.upper()}-{UUIDT().short()}", provider=gtt_organization.id, @@ -1242,7 +1561,7 @@ async def branch_scenario_add_upstream( endpoint1 = await client.create( branch=new_branch_name, - kind="InfraCircuitEndpoint", + kind=InfraCircuitEndpoint, site=site, circuit=circuit.id, connected_endpoint=intf.id, @@ -1283,7 +1602,7 @@ async def branch_scenario_add_upstream( async def branch_scenario_replace_ip_addresses( - client: InfrahubClient, log: logging.Logger, site_name: str, interconnection_pool: InfrahubNode + client: InfrahubClient, log: logging.Logger, site_name: str, interconnection_pool: CoreNode ) -> None: """ Create a new Branch and Change the IP addresses between edge1 and edge2 on the selected site @@ -1301,23 +1620,26 @@ async def branch_scenario_replace_ip_addresses( log.info(f"- Creating branch: {new_branch_name!r}") new_peer_network = await client.allocate_next_ip_prefix( - resource_pool=interconnection_pool, identifier=f"{device1_name}__{device2_name}", branch=new_branch_name + kind=IpamIPPrefix, + resource_pool=interconnection_pool, + identifier=f"{device1_name}__{device2_name}", + branch=new_branch_name, ) new_peer_network_hosts = new_peer_network.prefix.value.hosts() - device1 = await client.get(branch=new_branch_name, kind="InfraDevice", name__value=device1_name) - device2 = await client.get(branch=new_branch_name, kind="InfraDevice", name__value=device2_name) + device1 = await client.get(branch=new_branch_name, kind=InfraDevice, name__value=device1_name) + device2 = await client.get(branch=new_branch_name, kind=InfraDevice, name__value=device2_name) role_peer = "peer" peer_intfs_dev1 = sorted( await client.filters( - branch=new_branch_name, kind="InfraInterfaceL3", device__ids=[device1.id], role__value=role_peer + branch=new_branch_name, kind=InfraInterfaceL3, device__ids=[device1.id], role__value=role_peer ), key=lambda x: x.name.value, ) peer_intfs_dev2 = sorted( await client.filters( - branch=new_branch_name, kind="InfraInterfaceL3", device__ids=[device2.id], role__value=role_peer + branch=new_branch_name, kind=InfraInterfaceL3, device__ids=[device2.id], role__value=role_peer ), key=lambda x: x.name.value, ) @@ -1325,7 +1647,7 @@ async def branch_scenario_replace_ip_addresses( # Querying the object for now, need to pull from the store instead peer_ip = await client.create( branch=new_branch_name, - kind="IpamIPAddress", + kind=IpamIPAddress, interface={"id": peer_intfs_dev1[0].id}, address=f"{str(next(new_peer_network_hosts))}/31", ) @@ -1334,7 +1656,7 @@ async def branch_scenario_replace_ip_addresses( ip = await client.create( branch=new_branch_name, - kind="IpamIPAddress", + kind=IpamIPAddress, interface={"id": peer_intfs_dev2[0].id}, # , "source": account_pop.id}, address={"value": f"{str(next(new_peer_network_hosts))}/31"}, # , "source": account_pop.id}, ) @@ -1399,12 +1721,10 @@ async def branch_scenario_remove_colt(client: InfrahubClient, log: logging.Logge for item in colt_circuits: circuit_id = item["node"]["circuit"]["node"]["circuit_id"]["value"] - circuit_endpoint = await client.get(branch=new_branch_name, kind="InfraCircuitEndpoint", id=item["node"]["id"]) + circuit_endpoint = await client.get(branch=new_branch_name, kind=InfraCircuitEndpoint, id=item["node"]["id"]) await circuit_endpoint.delete() - circuit = await client.get( - branch=new_branch_name, kind="InfraCircuit", id=item["node"]["circuit"]["node"]["id"] - ) + circuit = await client.get(branch=new_branch_name, kind=InfraCircuit, id=item["node"]["circuit"]["node"]["id"]) await circuit.delete() log.info(f" - Deleted Colt Technology Services [{circuit_id}]") @@ -1430,24 +1750,24 @@ async def branch_scenario_conflict_device(client: InfrahubClient, log: logging.L drained_status = "drained" # Update Device 1 Status both in the Branch and in Main - device1_branch = await client.get(branch=new_branch_name, kind="InfraDevice", name__value=device1_name) + device1_branch = await client.get(branch=new_branch_name, kind=InfraDevice, name__value=device1_name) device1_branch.status.value = maintenance_status await device1_branch.save() intf1_branch = await client.get( - branch=new_branch_name, kind="InfraInterfaceL3", device__ids=[device1_branch.id], name__value="Ethernet1" + branch=new_branch_name, kind=InfraInterfaceL3, device__ids=[device1_branch.id], name__value="Ethernet1" ) intf1_branch.enabled.value = False intf1_branch.status.value = drained_status await intf1_branch.save() - device1_main = await client.get(kind="InfraDevice", name__value=device1_name) + device1_main = await client.get(kind=InfraDevice, name__value=device1_name) device1_main.status.value = provisioning_status await device1_main.save() - intf1_main = await client.get(kind="InfraInterfaceL3", device__ids=[device1_branch.id], name__value="Ethernet1") + intf1_main = await client.get(kind=InfraInterfaceL3, device__ids=[device1_branch.id], name__value="Ethernet1") intf1_main.enabled.value = False await intf1_main.save() @@ -1467,22 +1787,22 @@ async def branch_scenario_conflict_platform(client: InfrahubClient, log: logging # Create a new Platform object with the same name, both in the branch and in main platform1_branch = await client.create( - branch=new_branch_name, kind="InfraPlatform", name="Cisco IOS XR", netmiko_device_type="cisco_xr" + branch=new_branch_name, kind=InfraPlatform, name="Cisco IOS XR", netmiko_device_type="cisco_xr" ) await platform1_branch.save() - platform1_main = await client.create(kind="InfraPlatform", name="Cisco IOS XR", netmiko_device_type="cisco_xr") + platform1_main = await client.create(kind=InfraPlatform, name="Cisco IOS XR", netmiko_device_type="cisco_xr") await platform1_main.save() # Delete an existing Platform object on both in the Branch and in Main - platform2_branch = await client.get(branch=new_branch_name, kind="InfraPlatform", name__value="Cisco NXOS SSH") + platform2_branch = await client.get(branch=new_branch_name, kind=InfraPlatform, name__value="Cisco NXOS SSH") await platform2_branch.delete() - platform2_main = await client.get(kind="InfraPlatform", name__value="Cisco NXOS SSH") + platform2_main = await client.get(kind=InfraPlatform, name__value="Cisco NXOS SSH") await platform2_main.delete() # Delete an existing Platform object in the branch and update it in main - platform3_branch = await client.get(branch=new_branch_name, kind="InfraPlatform", name__value="Juniper JunOS") + platform3_branch = await client.get(branch=new_branch_name, kind=InfraPlatform, name__value="Juniper JunOS") await platform3_branch.delete() - platform3_main = await client.get(kind="InfraPlatform", name__value="Juniper JunOS") + platform3_main = await client.get(kind=InfraPlatform, name__value="Juniper JunOS") platform3_main.nornir_platform.value = "juniper_junos" await platform3_main.save() @@ -1513,20 +1833,122 @@ async def generate_continents_countries(client: InfrahubClient, log: logging.Log log.info("Created continents and countries") -async def prepare_accounts(client: InfrahubClient, log: logging.Logger, branch: str, batch: InfrahubBatch) -> None: - for account in ACCOUNTS: +async def prepare_permissions(client: InfrahubClient, log: logging.Logger, branch: str, batch: InfrahubBatch) -> None: + for p in GLOBAL_PERMISSIONS: + obj = await client.get( + branch=branch, kind="CoreGlobalPermission", hfid=[p.action, str(p.decision)], raise_when_missing=True + ) + store.set(key=p.action, node=obj) + + for name, p in OBJECT_PERMISSIONS.items(): + try: + obj = await client.get( + branch=branch, kind="CoreObjectPermission", hfid=[p.namespace, p.name, p.action, str(p.decision)] + ) + except NodeNotFoundError: + obj = await client.create(branch=branch, kind="CoreObjectPermission", data=p.model_dump()) + batch.add(task=obj.save, node=obj) + store.set(key=name, node=obj) + + +async def prepare_account_roles(client: InfrahubClient, log: logging.Logger, branch: str, batch: InfrahubBatch) -> None: + for role in ACCOUNT_ROLES: obj = await client.create( branch=branch, - kind="CoreAccount", - data=account.model_dump(), + kind="CoreAccountRole", + data=role.model_dump(exclude={"global_permissions", "object_permissions"}), ) batch.add(task=obj.save, node=obj) + store.set(key=role.name, node=obj) + + +async def prepare_accounts(client: InfrahubClient, log: logging.Logger, branch: str, batch: InfrahubBatch) -> None: + for account in ACCOUNTS: + obj = await client.create(branch=branch, kind="CoreAccount", data=account.model_dump(exclude={"groups"})) + batch.add(task=obj.save, node=obj) store.set(key=account.name, node=obj) + for name, group in ACCOUNT_GROUPS.items(): + obj = await client.create( + branch=branch, kind="CoreAccountGroup", data=group.model_dump(exclude={"roles", "members"}) + ) + batch.add(task=obj.save, node=obj) + store.set(key=name, node=obj) + + +async def map_permissions_to_roles( + client: InfrahubClient, log: logging.Logger, branch: str, batch: InfrahubBatch +) -> None: + for role in ACCOUNT_ROLES: + if not role.global_permissions and not role.object_permissions: + continue + + obj = store.get(role.name, kind=CoreAccountRole, raise_when_missing=True) + await obj.permissions.fetch() + + permissions: list[CoreGlobalPermission | CoreObjectPermission] = [] + if role.global_permissions: + if isinstance(role.global_permissions, str) and role.global_permissions == "__all__": + permissions.extend( + [ + store.get(p.action, kind=CoreGlobalPermission, raise_when_missing=True) + for p in GLOBAL_PERMISSIONS + ] + ) + else: + permissions.extend( + [ + store.get(p_name, kind=CoreGlobalPermission, raise_when_missing=True) + for p_name in role.global_permissions + ] + ) + if role.object_permissions: + if isinstance(role.object_permissions, str) and role.object_permissions == "__all__": + permissions.extend( + [ + store.get(p_name, kind=CoreObjectPermission, raise_when_missing=True) + for p_name in GLOBAL_PERMISSIONS + ] + ) + else: + permissions.extend( + [ + store.get(p_name, kind=CoreObjectPermission, raise_when_missing=True) + for p_name in role.object_permissions + ] + ) + + obj.permissions.extend(permissions) + batch.add(task=obj.save, node=obj) + + +async def map_user_and_roles_to_groups( + client: InfrahubClient, log: logging.Logger, branch: str, batch: InfrahubBatch +) -> None: + for group_name, group in ACCOUNT_GROUPS.items(): + updated = False + obj = store.get(group_name, kind=CoreAccountGroup, raise_when_missing=True) + + if group.roles: + await obj.roles.fetch() + obj.roles.extend( + data=[store.get(role, kind=CoreAccountRole, raise_when_missing=True) for role in group.roles] + ) + updated = True + if group.members: + await obj.members.fetch() + obj.members.extend( + data=[store.get(member, kind=CoreAccount, raise_when_missing=True) for member in group.members] + ) + updated = True + + if updated: + batch.add(task=obj.save, node=obj) + async def prepare_asns(client: InfrahubClient, log: logging.Logger, branch: str, batch: InfrahubBatch) -> None: - account_chloe = store.get("Chloe O'Brian") - account_crm = store.get("CRM Synchronization") + account_chloe = store.get("cobrian", kind=CoreAccount, raise_when_missing=True) + account_crm = store.get("crm-sync", kind=CoreAccount, raise_when_missing=True) organizations_dict = {org.name: org.type for org in ORGANIZATIONS} for asn in ASNS: organization_type = organizations_dict.get(asn.organization, None) @@ -1542,7 +1964,9 @@ async def prepare_asns(client: InfrahubClient, log: logging.Logger, branch: str, "owner": account_chloe.id, } data_asn["organization"] = { - "id": store.get(kind=f"Organization{organization_type.title()}", key=asn.organization).id, + "id": store.get( + kind=f"Organization{organization_type.title()}", raise_when_missing=True, key=asn.organization + ).id, "source": account_crm.id, } else: @@ -1555,7 +1979,7 @@ async def prepare_asns(client: InfrahubClient, log: logging.Logger, branch: str, async def prepare_bgp_peer_groups( client: InfrahubClient, log: logging.Logger, branch: str, batch: InfrahubBatch ) -> None: - account_pop = store.get("pop-builder") + account_pop = store.get("pop-builder", kind=CoreAccount, raise_when_missing=True) log.info("Creating BGP Peer Groups") for peer_group in BGP_PEER_GROUPS: @@ -1587,7 +2011,7 @@ async def prepare_bgp_peer_groups( async def prepare_groups(client: InfrahubClient, log: logging.Logger, branch: str, batch: InfrahubBatch) -> None: for group in GROUPS: - obj = await client.create(branch=branch, kind="CoreStandardGroup", data=group.model_dump()) + obj = await client.create(branch=branch, kind=CoreStandardGroup, data=group.model_dump()) batch.add(task=obj.save, node=obj) store.set(key=group.name, node=obj) @@ -1620,7 +2044,7 @@ async def prepare_platforms(client: InfrahubClient, log: logging.Logger, branch: for platform in PLATFORMS: obj = await client.create( branch=branch, - kind="InfraPlatform", + kind=InfraPlatform, data=platform.model_dump(), ) batch.add(task=obj.save, node=obj) @@ -1628,7 +2052,7 @@ async def prepare_platforms(client: InfrahubClient, log: logging.Logger, branch: async def prepare_tags(client: InfrahubClient, log: logging.Logger, branch: str, batch: InfrahubBatch) -> None: - account_pop = store.get("pop-builder") + account_pop = store.get("pop-builder", kind=CoreAccount, raise_when_missing=True) log.info("Creating Tags") for tag in TAGS: @@ -1642,12 +2066,55 @@ async def prepare_tags(client: InfrahubClient, log: logging.Logger, branch: str, # # infrahubctl run models/infrastructure_edge.py # +# You can also provide inputs to the script in order to generate more or less data +# +# infrahubctl run models/infrastructure_edge.py profile="large" +# infrahubctl run models/infrastructure_edge.py num_sites=10 num_device_per_site=14 +# infrahubctl run models/infrastructure_edge.py has_bgp_mesh=False has_branch=False +# # --------------------------------------------------------------- -async def run(client: InfrahubClient, log: logging.Logger, branch: str, num_sites: int = 5) -> None: +async def run( + client: InfrahubClient, + log: logging.Logger, + branch: str, + profile: str = None, + num_sites: int = None, + num_device_per_site: int = None, + has_bgp_mesh: str = None, + has_branch: str = None, +) -> None: + # Create timer to keep track of time elapsed + start: float = time.time() + + # ------------------------------------------ + # Config + # ------------------------------------------ + # Create an instance of the global configuration + config = GlobalConfig() + + # Translate str to bool + bool_has_bgp_mesh: bool = None + if has_bgp_mesh is not None: + bool_has_bgp_mesh = translate_str_to_bool("has_bgp_mesh", has_bgp_mesh) + + bool_has_branch: bool = None + if has_branch is not None: + bool_has_branch = translate_str_to_bool("has_branch", has_branch) + + # Load args into the config + try: + config.load_config(profile, num_sites, num_device_per_site, bool_has_bgp_mesh, bool_has_branch) + except ConfigError as ex: + log.fatal(ex) + return False # FIXME: What should I return here for the script to fail properly + + # Print config + log.info(f"Loading data with {config}") + # ------------------------------------------ # Create Continents, Countries # ------------------------------------------ - num_sites = int(num_sites) + num_sites = int(config.num_sites) log.info("Creating Infrastructure Data") await generate_continents_countries(client=client, log=log, branch=branch) @@ -1655,10 +2122,32 @@ async def run(client: InfrahubClient, log: logging.Logger, branch: str, num_site # ------------------------------------------ # Create User Accounts, Groups, Organizations & Platforms # ------------------------------------------ - log.info("Creating User Accounts, Groups & Organizations & Platforms") + log.info("Creating User Accounts, Groups, Roles, Permissions & Organizations & Platforms") + + batch = await client.create_batch() + await prepare_permissions(client=client, log=log, branch=branch, batch=batch) + await prepare_account_roles(client=client, log=log, branch=branch, batch=batch) + async for node, _ in batch.execute(): + if hasattr(node, "name"): + log.info(f"- Created {node._schema.kind} - {node.name.value}") + else: + log.info(f"- Created {node._schema.kind} - {node}") batch = await client.create_batch() await prepare_accounts(client=client, log=log, branch=branch, batch=batch) + async for node, _ in batch.execute(): + log.info(f"- Created {node._schema.kind} - {node.name.value}") + + batch = await client.create_batch() + await map_permissions_to_roles(client=client, log=log, branch=branch, batch=batch) + async for node, _ in batch.execute(): + log.info(f"- Updated {node._schema.kind} - {node.name.value} with permissions") + + batch = await client.create_batch() + await map_user_and_roles_to_groups(client=client, log=log, branch=branch, batch=batch) + async for node, _ in batch.execute(): + log.info(f"- Updated {node._schema.kind} - {node.name.value} with roles and members") + await prepare_groups(client=client, log=log, branch=branch, batch=batch) await prepare_platforms(client=client, log=log, branch=branch, batch=batch) await prepare_organizations(client=client, log=log, branch=branch, batch=batch) @@ -1670,7 +2159,7 @@ async def run(client: InfrahubClient, log: logging.Logger, branch: str, num_site else: log.info(f"- Created {node._schema.kind} - {node.name.value}") - account_pop = store.get("pop-builder") + account_pop = store.get("pop-builder", kind=CoreAccount, raise_when_missing=True) batch = await client.create_batch() await prepare_asns(client=client, log=log, branch=branch, batch=batch) @@ -1687,17 +2176,17 @@ async def run(client: InfrahubClient, log: logging.Logger, branch: str, num_site # ------------------------------------------ # Create IP prefixes # ------------------------------------------ - default_ip_namespace = await client.get(kind="IpamNamespace", name__value="default") + default_ip_namespace = await client.get(kind=IpamNamespace, name__value="default") log.info("Creating IP Prefixes") log.info("Creating IP Core Supernet and Pool") supernet_prefix = await client.create( - branch=branch, kind="IpamIPPrefix", prefix=str(NETWORKS_SUPERNET), member_type="prefix" + branch=branch, kind=IpamIPPrefix, prefix=str(NETWORKS_SUPERNET), member_type="prefix" ) await supernet_prefix.save() supernet_pool = await client.create( - kind="CoreIPPrefixPool", + kind=CoreIPPrefixPool, name="Internal networks pool", default_prefix_type="IpamIPPrefix", default_prefix_length=16, @@ -1712,7 +2201,7 @@ async def run(client: InfrahubClient, log: logging.Logger, branch: str, num_site resource_pool=supernet_pool, member_type="address", branch=branch ) loopback_pool = await client.create( - kind="CoreIPAddressPool", + kind=CoreIPAddressPool, name="Loopbacks pool", default_address_type="IpamIPAddress", default_prefix_length=32, @@ -1723,9 +2212,11 @@ async def run(client: InfrahubClient, log: logging.Logger, branch: str, num_site await loopback_pool.save() log.info("Creating IP Interconnection Prefix and Pool") - interconnection_prefix = await client.allocate_next_ip_prefix(resource_pool=supernet_pool, branch=branch) + interconnection_prefix = await client.allocate_next_ip_prefix( + kind=IpamIPPrefix, resource_pool=supernet_pool, branch=branch + ) interconnection_pool = await client.create( - kind="CoreIPPrefixPool", + kind=CoreIPPrefixPool, name="Interconnections pool", default_prefix_type="IpamIPPrefix", default_prefix_length=31, @@ -1746,10 +2237,10 @@ async def run(client: InfrahubClient, log: logging.Logger, branch: str, num_site ) await management_prefix.save() management_pool = await client.create( - kind="CoreIPAddressPool", + kind=CoreIPAddressPool, name="Management addresses pool", default_address_type="IpamIPAddress", - default_prefix_length=28, + default_prefix_length=16, ip_namespace=default_ip_namespace, resources=[management_prefix], branch=branch, @@ -1762,7 +2253,7 @@ async def run(client: InfrahubClient, log: logging.Logger, branch: str, num_site ) await external_supernet.save() external_pool = await client.create( - kind="CoreIPPrefixPool", + kind=CoreIPPrefixPool, name="External prefixes pool", default_prefix_type="IpamIPPrefix", default_prefix_length=29, @@ -1779,10 +2270,10 @@ async def run(client: InfrahubClient, log: logging.Logger, branch: str, num_site ) await ipv6_supernet_prefix.save() ipv6_supernet_pool = await client.create( - kind="CoreIPPrefixPool", + kind=CoreIPPrefixPool, name="Internal networks pool (IPv6)", default_prefix_type="IpamIPPrefix", - default_prefix_length=120, + default_prefix_length=110, default_member_type="address", ip_namespace=default_ip_namespace, resources=[ipv6_supernet_prefix], @@ -1794,13 +2285,13 @@ async def run(client: InfrahubClient, log: logging.Logger, branch: str, num_site # Create Pool IPv6 prefixes # ------------------------------------------ log.info("Creating pool IPv6 Prefixes and IPs") - ipv6_internal_networks = [ - await client.allocate_next_ip_prefix(resource_pool=ipv6_supernet_pool, branch=branch), - await client.allocate_next_ip_prefix(resource_pool=ipv6_supernet_pool, branch=branch), - await client.allocate_next_ip_prefix(resource_pool=ipv6_supernet_pool, branch=branch), - await client.allocate_next_ip_prefix(resource_pool=ipv6_supernet_pool, branch=branch), - await client.allocate_next_ip_prefix(resource_pool=ipv6_supernet_pool, branch=branch), - await client.allocate_next_ip_prefix(resource_pool=ipv6_supernet_pool, branch=branch), + ipv6_internal_networks: list[IpamIPPrefix] = [ + await client.allocate_next_ip_prefix(resource_pool=ipv6_supernet_pool, kind=IpamIPPrefix, branch=branch), + await client.allocate_next_ip_prefix(resource_pool=ipv6_supernet_pool, kind=IpamIPPrefix, branch=branch), + await client.allocate_next_ip_prefix(resource_pool=ipv6_supernet_pool, kind=IpamIPPrefix, branch=branch), + await client.allocate_next_ip_prefix(resource_pool=ipv6_supernet_pool, kind=IpamIPPrefix, branch=branch), + await client.allocate_next_ip_prefix(resource_pool=ipv6_supernet_pool, kind=IpamIPPrefix, branch=branch), + await client.allocate_next_ip_prefix(resource_pool=ipv6_supernet_pool, kind=IpamIPPrefix, branch=branch), ] log.info("IP Prefixes Creation Completed") @@ -1818,7 +2309,7 @@ async def run(client: InfrahubClient, log: logging.Logger, branch: str, num_site batch = await client.create_batch() for ipv6_addr in ipv6_addresses: obj = await client.create( - branch=branch, kind="IpamIPAddress", address={"value": ipv6_addr, "source": account_pop.id} + branch=branch, kind=IpamIPAddress, address={"value": ipv6_addr, "source": account_pop.id} ) batch.add(task=obj.save, node=obj) @@ -1832,6 +2323,11 @@ async def run(client: InfrahubClient, log: logging.Logger, branch: str, num_site # ------------------------------------------ log.info("Creating Site and associated objects (Device, Circuit, BGP Sessions)") sites = site_generator(nbr_site=num_sites) + + # Compute the design to follow for each site + site_design: SiteDesign = SiteDesign(config.num_device_per_site) + log.info(f"following {site_design}") + for site in sites: response = await generate_site( client=client, @@ -1842,6 +2338,7 @@ async def run(client: InfrahubClient, log: logging.Logger, branch: str, num_site loopback_pool=loopback_pool, management_pool=management_pool, external_pool=external_pool, + site_design=site_design, ) log.info(f"{response} - Creation Completed") @@ -1851,7 +2348,8 @@ async def run(client: InfrahubClient, log: logging.Logger, branch: str, num_site log=log, ) - await create_bgp_mesh(client=client, branch=branch, log=log, sites=sites) + if config.has_bgp_mesh: + await create_bgp_mesh(client=client, branch=branch, log=log, sites=sites) await create_backbone_connectivity(client=client, branch=branch, log=log, num_sites=num_sites) @@ -1863,7 +2361,7 @@ async def run(client: InfrahubClient, log: logging.Logger, branch: str, num_site # Scenario 4 - Create some Relationship One and Attribute conflicts on a device # Scenario 5 - Create some Node ADD and DELETE conflicts on some platform objects # -------------------------------------------------- - if branch == "main": + if branch == "main" and config.has_branch: await branch_scenario_add_upstream(site_name=sites[1].name, client=client, log=log, external_pool=external_pool) await branch_scenario_replace_ip_addresses( site_name=sites[2].name, client=client, log=log, interconnection_pool=interconnection_pool @@ -1871,3 +2369,6 @@ async def run(client: InfrahubClient, log: logging.Logger, branch: str, num_site await branch_scenario_remove_colt(site_name=sites[0].name, client=client, log=log) await branch_scenario_conflict_device(site_name=sites[3].name, client=client, log=log) await branch_scenario_conflict_platform(client=client, log=log) + + # Stop the timer and display elapsed time + log.info(f"Data loaded in {round(time.time() - start)}s") diff --git a/models/protocols.py b/models/protocols.py new file mode 100644 index 0000000000..cce3526241 --- /dev/null +++ b/models/protocols.py @@ -0,0 +1,258 @@ +# +# Generated by "infrahubctl protocols" +# + +from __future__ import annotations + +from typing import TYPE_CHECKING + +from infrahub_sdk.protocols import ( + BuiltinIPAddress, + BuiltinIPPrefix, + CoreArtifactTarget, + CoreNode, +) + +if TYPE_CHECKING: + from infrahub_sdk.node import RelatedNode, RelationshipManager + from infrahub_sdk.protocols_base import ( + Boolean, + Dropdown, + DropdownOptional, + Integer, + IntegerOptional, + String, + StringOptional, + ) + + +class InfraEndpoint(CoreNode): + connected_endpoint: RelatedNode + + +class OrganizationGeneric(CoreNode): + name: String + description: StringOptional + tags: RelationshipManager + asn: RelationshipManager + + +class LocationGeneric(CoreNode): + name: String + description: StringOptional + + +class InfraInterface(CoreNode): + name: String + description: StringOptional + speed: Integer + mtu: Integer + enabled: Boolean + status: DropdownOptional + role: DropdownOptional + device: RelatedNode + tags: RelationshipManager + + +class InfraLagInterface(CoreNode): + lacp: String + minimum_links: Integer + max_bundle: IntegerOptional + mlag: RelatedNode + + +class InfraMlagInterface(CoreNode): + mlag_id: Integer + mlag_domain: RelatedNode + + +class InfraService(CoreNode): + name: String + + +class InfraAutonomousSystem(CoreNode): + name: String + asn: Integer + description: StringOptional + organization: RelatedNode + + +class InfraBGPPeerGroup(CoreNode): + name: String + description: StringOptional + import_policies: StringOptional + export_policies: StringOptional + local_as: RelatedNode + remote_as: RelatedNode + + +class InfraBGPSession(CoreArtifactTarget): + type: String + description: StringOptional + import_policies: StringOptional + export_policies: StringOptional + status: Dropdown + role: Dropdown + local_as: RelatedNode + remote_as: RelatedNode + local_ip: RelatedNode + remote_ip: RelatedNode + device: RelatedNode + peer_group: RelatedNode + peer_session: RelatedNode + + +class InfraBackBoneService(InfraService): + circuit_id: String + internal_circuit_id: String + provider: RelatedNode + site_a: RelatedNode + site_b: RelatedNode + + +class InfraCircuit(CoreNode): + circuit_id: String + description: StringOptional + vendor_id: StringOptional + status: Dropdown + role: Dropdown + provider: RelatedNode + endpoints: RelationshipManager + bgp_sessions: RelationshipManager + + +class InfraCircuitEndpoint(InfraEndpoint): + description: StringOptional + site: RelatedNode + circuit: RelatedNode + + +class LocationContinent(LocationGeneric): + pass + + +class LocationCountry(LocationGeneric): + pass + + +class InfraDevice(CoreArtifactTarget): + name: String + description: StringOptional + type: String + status: DropdownOptional + role: DropdownOptional + site: RelatedNode + interfaces: RelationshipManager + asn: RelatedNode + tags: RelationshipManager + primary_address: RelatedNode + platform: RelatedNode + mlag_domain: RelatedNode + + +class IpamIPAddress(BuiltinIPAddress): + interface: RelatedNode + + +class IpamIPPrefix(BuiltinIPPrefix): + pass + + +class InfraInterfaceL2(InfraInterface, InfraEndpoint, CoreArtifactTarget): + l2_mode: String + lacp_rate: String + lacp_priority: Integer + lag: RelatedNode + untagged_vlan: RelatedNode + tagged_vlan: RelationshipManager + + +class InfraInterfaceL3(InfraInterface, InfraEndpoint, CoreArtifactTarget): + lacp_rate: String + lacp_priority: Integer + ip_addresses: RelationshipManager + lag: RelatedNode + + +class InfraLagInterfaceL2(InfraInterface, InfraLagInterface, CoreArtifactTarget): + l2_mode: String + members: RelationshipManager + untagged_vlan: RelatedNode + tagged_vlan: RelationshipManager + + +class InfraLagInterfaceL3(InfraInterface, InfraLagInterface, CoreArtifactTarget): + members: RelationshipManager + ip_addresses: RelationshipManager + + +class OrganizationManufacturer(OrganizationGeneric): + platform: RelationshipManager + + +class InfraMlagDomain(CoreNode): + name: String + domain_id: Integer + devices: RelationshipManager + peer_interfaces: RelationshipManager + + +class InfraMlagInterfaceL2(InfraMlagInterface): + members: RelationshipManager + + +class InfraMlagInterfaceL3(InfraMlagInterface): + members: RelationshipManager + + +class InfraPlatform(CoreNode): + name: String + description: StringOptional + nornir_platform: StringOptional + napalm_driver: StringOptional + netmiko_device_type: StringOptional + ansible_network_os: StringOptional + devices: RelationshipManager + + +class OrganizationProvider(OrganizationGeneric): + location: RelationshipManager + circuit: RelationshipManager + + +class LocationRack(LocationGeneric): + name: String + description: StringOptional + height: String + facility_id: StringOptional + serial_number: StringOptional + asset_tag: StringOptional + status: Dropdown + role: DropdownOptional + site: RelatedNode + tags: RelationshipManager + + +class LocationSite(LocationGeneric): + city: StringOptional + address: StringOptional + contact: StringOptional + devices: RelationshipManager + vlans: RelationshipManager + circuit_endpoints: RelationshipManager + tags: RelationshipManager + + +class OrganizationTenant(OrganizationGeneric): + location: RelationshipManager + circuit: RelationshipManager + + +class InfraVLAN(CoreNode): + name: String + description: StringOptional + vlan_id: Integer + status: Dropdown + role: Dropdown + site: RelatedNode + gateway: RelatedNode diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 52a943ec3e..0000000000 --- a/package-lock.json +++ /dev/null @@ -1,1365 +0,0 @@ -{ - "name": "infrahub", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "dependencies": { - "markdownlint-cli2": "^0.11.0" - }, - "devDependencies": { - "husky": "^8.0.3", - "lint-staged": "^13.2.0" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@sindresorhus/merge-streams": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-1.0.0.tgz", - "integrity": "sha512-rUV5WyJrJLoloD4NDN1V1+LDMDWOa4OTsT4yYJwQNpTU6FWxkxHpL7eu4w+DmiH8x/EAM1otkPE1+LaspIbplw==", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, - "node_modules/astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/chalk": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", - "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", - "dev": true, - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-truncate": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz", - "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==", - "dev": true, - "dependencies": { - "slice-ansi": "^5.0.0", - "string-width": "^5.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/colorette": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", - "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", - "dev": true - }, - "node_modules/commander": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.0.tgz", - "integrity": "sha512-zS5PnTI22FIRM6ylNW8G4Ap0IEOyk62fhLSD0+uHRT9McRCLGpkVNvao4bjimpK/GShynyQkFFxHhwMcETmduA==", - "dev": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true - }, - "node_modules/entities": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", - "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/execa": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-7.1.1.tgz", - "integrity": "sha512-wH0eMf/UXckdUYnO21+HDztteVv05rq2GXksxT4fCGeHkBhw1DROXh40wcjMcRqDOWE7iPJ4n3M7e2+YFP+76Q==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.1", - "human-signals": "^4.3.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^3.0.7", - "strip-final-newline": "^3.0.0" - }, - "engines": { - "node": "^14.18.0 || ^16.14.0 || >=18.0.0" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "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": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fastq": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.16.0.tgz", - "integrity": "sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/globby": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-14.0.0.tgz", - "integrity": "sha512-/1WM/LNHRAOH9lZta77uGbq0dAEQM+XjNesWwhlERDVenqothRbnzTrL3/LrIoEPPjeUHC3vrS6TwoyxeHs7MQ==", - "dependencies": { - "@sindresorhus/merge-streams": "^1.0.0", - "fast-glob": "^3.3.2", - "ignore": "^5.2.4", - "path-type": "^5.0.0", - "slash": "^5.1.0", - "unicorn-magic": "^0.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/human-signals": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.0.tgz", - "integrity": "sha512-zyzVyMjpGBX2+6cDVZeFPCdtOtdsxOeseRhB9tkQ6xXmGUNrcnBzdEKPy3VPNYz+4gy1oukVOXcrJCunSyc6QQ==", - "dev": true, - "engines": { - "node": ">=14.18.0" - } - }, - "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, - "bin": { - "husky": "lib/bin.js" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/typicode" - } - }, - "node_modules/ignore": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", - "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", - "engines": { - "node": ">= 4" - } - }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "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, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/lilconfig": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", - "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/linkify-it": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-4.0.1.tgz", - "integrity": "sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==", - "dependencies": { - "uc.micro": "^1.0.1" - } - }, - "node_modules/lint-staged": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-13.2.0.tgz", - "integrity": "sha512-GbyK5iWinax5Dfw5obm2g2ccUiZXNGtAS4mCbJ0Lv4rq6iEtfBSjOYdcbOtAIFtM114t0vdpViDDetjVTSd8Vw==", - "dev": true, - "dependencies": { - "chalk": "5.2.0", - "cli-truncate": "^3.1.0", - "commander": "^10.0.0", - "debug": "^4.3.4", - "execa": "^7.0.0", - "lilconfig": "2.1.0", - "listr2": "^5.0.7", - "micromatch": "^4.0.5", - "normalize-path": "^3.0.0", - "object-inspect": "^1.12.3", - "pidtree": "^0.6.0", - "string-argv": "^0.3.1", - "yaml": "^2.2.1" - }, - "bin": { - "lint-staged": "bin/lint-staged.js" - }, - "engines": { - "node": "^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/lint-staged" - } - }, - "node_modules/listr2": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-5.0.8.tgz", - "integrity": "sha512-mC73LitKHj9w6v30nLNGPetZIlfpUniNSsxxrbaPcWOjDb92SHPzJPi/t+v1YC/lxKz/AJ9egOjww0qUuFxBpA==", - "dev": true, - "dependencies": { - "cli-truncate": "^2.1.0", - "colorette": "^2.0.19", - "log-update": "^4.0.0", - "p-map": "^4.0.0", - "rfdc": "^1.3.0", - "rxjs": "^7.8.0", - "through": "^2.3.8", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": "^14.13.1 || >=16.0.0" - }, - "peerDependencies": { - "enquirer": ">= 2.3.0 < 3" - }, - "peerDependenciesMeta": { - "enquirer": { - "optional": true - } - } - }, - "node_modules/listr2/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/listr2/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/listr2/node_modules/cli-truncate": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", - "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", - "dev": true, - "dependencies": { - "slice-ansi": "^3.0.0", - "string-width": "^4.2.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/listr2/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/listr2/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/listr2/node_modules/slice-ansi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", - "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/listr2/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/listr2/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/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/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/log-update/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/log-update/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/log-update/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/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/log-update/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/log-update/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/log-update/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/markdown-it": { - "version": "13.0.2", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-13.0.2.tgz", - "integrity": "sha512-FtwnEuuK+2yVU7goGn/MJ0WBZMM9ZPgU9spqlFs7/A/pDIUNSOQZhUgOqYCficIuR2QaFnrt8LHqBWsbTAoI5w==", - "dependencies": { - "argparse": "^2.0.1", - "entities": "~3.0.1", - "linkify-it": "^4.0.1", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" - }, - "bin": { - "markdown-it": "bin/markdown-it.js" - } - }, - "node_modules/markdownlint": { - "version": "0.32.1", - "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.32.1.tgz", - "integrity": "sha512-3sx9xpi4xlHlokGyHO9k0g3gJbNY4DI6oNEeEYq5gQ4W7UkiJ90VDAnuDl2U+yyXOUa6BX+0gf69ZlTUGIBp6A==", - "dependencies": { - "markdown-it": "13.0.2", - "markdownlint-micromark": "0.1.7" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/DavidAnson" - } - }, - "node_modules/markdownlint-cli2": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/markdownlint-cli2/-/markdownlint-cli2-0.11.0.tgz", - "integrity": "sha512-RmFpr+My5in8KT+H/A6ozKIVYVzZtL5t9c8DYdv0YJdljl385z44CcCVBrclpHxCGMY2tr0hZ/ca+meGGvgdnQ==", - "dependencies": { - "globby": "14.0.0", - "markdownlint": "0.32.1", - "markdownlint-cli2-formatter-default": "0.0.4", - "micromatch": "4.0.5", - "strip-json-comments": "5.0.1", - "yaml": "2.3.4" - }, - "bin": { - "markdownlint-cli2": "markdownlint-cli2.js", - "markdownlint-cli2-config": "markdownlint-cli2-config.js", - "markdownlint-cli2-fix": "markdownlint-cli2-fix.js" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/DavidAnson" - } - }, - "node_modules/markdownlint-cli2-formatter-default": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/markdownlint-cli2-formatter-default/-/markdownlint-cli2-formatter-default-0.0.4.tgz", - "integrity": "sha512-xm2rM0E+sWgjpPn1EesPXx5hIyrN2ddUnUwnbCsD/ONxYtw3PX6LydvdH6dciWAoFDpwzbHM1TO7uHfcMd6IYg==", - "peerDependencies": { - "markdownlint-cli2": ">=0.0.4" - } - }, - "node_modules/markdownlint-micromark": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/markdownlint-micromark/-/markdownlint-micromark-0.1.7.tgz", - "integrity": "sha512-BbRPTC72fl5vlSKv37v/xIENSRDYL/7X/XoFzZ740FGEbs9vZerLrIkFRY0rv7slQKxDczToYuMmqQFN61fi4Q==", - "engines": { - "node": ">=16" - } - }, - "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/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "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, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-path": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", - "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", - "dev": true, - "dependencies": { - "path-key": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm-run-path/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, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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, - "dependencies": { - "mimic-fn": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "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", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "dev": true, - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-type": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz", - "integrity": "sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pidtree": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", - "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", - "dev": true, - "bin": { - "pidtree": "bin/pidtree.js" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "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/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/restore-cursor/node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/restore-cursor/node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rfdc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", - "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", - "dev": true - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/rxjs": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", - "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==", - "dev": true, - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "node_modules/slash": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", - "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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, - "dependencies": { - "ansi-styles": "^6.0.0", - "is-fullwidth-code-point": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/string-argv": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", - "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", - "dev": true, - "engines": { - "node": ">=0.6.19" - } - }, - "node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/strip-ansi": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", - "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "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, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/strip-json-comments": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.1.tgz", - "integrity": "sha512-0fk9zBqO67Nq5M/m45qHCJxylV/DhBlIOVExqgOMiCCrzrhU6tCibRXNqE3jwJLftzE9SNuZtYbpzcO+i9FiKw==", - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", - "dev": true - }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/uc.micro": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", - "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==" - }, - "node_modules/unicorn-magic": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", - "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yaml": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", - "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", - "engines": { - "node": ">= 14" - } - } - } -} diff --git a/package.json b/package.json deleted file mode 100644 index c6e150564a..0000000000 --- a/package.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "scripts": { - "lint:frontend": "cd frontend/app && npm run format-code", - "prepare": "husky install" - }, - "dependencies": { - "markdownlint-cli2": "^0.11.0" - }, - "devDependencies": { - "husky": "^8.0.3", - "lint-staged": "^13.2.0" - }, - "husky": { - "hooks": { - "pre-commit": "lint-staged" - } - }, - "lint-staged": { - "*.(js|tsx|ts)": [ - "npm run lint:frontend" - ] - } -} diff --git a/poetry.lock b/poetry.lock index 6745ea2459..edea06928b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -31,18 +31,41 @@ pamqp = "3.3.0" yarl = "*" [[package]] -name = "aniso8601" -version = "9.0.1" -description = "A library for parsing ISO 8601 strings." +name = "aiosqlite" +version = "0.20.0" +description = "asyncio bridge to the standard sqlite3 module" optional = false -python-versions = "*" +python-versions = ">=3.8" +files = [ + {file = "aiosqlite-0.20.0-py3-none-any.whl", hash = "sha256:36a1deaca0cac40ebe32aac9977a6e2bbc7f5189f23f4a54d5908986729e5bd6"}, + {file = "aiosqlite-0.20.0.tar.gz", hash = "sha256:6d35c8c256637f4672f843c31021464090805bf925385ac39473fb16eaaca3d7"}, +] + +[package.dependencies] +typing_extensions = ">=4.0" + +[package.extras] +dev = ["attribution (==1.7.0)", "black (==24.2.0)", "coverage[toml] (==7.4.1)", "flake8 (==7.0.0)", "flake8-bugbear (==24.2.6)", "flit (==3.9.0)", "mypy (==1.8.0)", "ufmt (==2.3.0)", "usort (==1.0.8.post1)"] +docs = ["sphinx (==7.2.6)", "sphinx-mdinclude (==0.5.3)"] + +[[package]] +name = "alembic" +version = "1.13.2" +description = "A database migration tool for SQLAlchemy." +optional = false +python-versions = ">=3.8" files = [ - {file = "aniso8601-9.0.1-py2.py3-none-any.whl", hash = "sha256:1d2b7ef82963909e93c4f24ce48d4de9e66009a21bf1c1e1c85bdd0812fe412f"}, - {file = "aniso8601-9.0.1.tar.gz", hash = "sha256:72e3117667eedf66951bb2d93f4296a56b94b078a8a95905a052611fb3f1b973"}, + {file = "alembic-1.13.2-py3-none-any.whl", hash = "sha256:6b8733129a6224a9a711e17c99b08462dbf7cc9670ba8f2e2ae9af860ceb1953"}, + {file = "alembic-1.13.2.tar.gz", hash = "sha256:1ff0ae32975f4fd96028c39ed9bb3c867fe3af956bd7bb37343b54c9fe7445ef"}, ] +[package.dependencies] +Mako = "*" +SQLAlchemy = ">=1.3.0" +typing-extensions = ">=4" + [package.extras] -dev = ["black", "coverage", "isort", "pre-commit", "pyenchant", "pylint"] +tz = ["backports.zoneinfo"] [[package]] name = "annotated-types" @@ -57,13 +80,13 @@ files = [ [[package]] name = "anyio" -version = "4.4.0" +version = "4.6.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "anyio-4.4.0-py3-none-any.whl", hash = "sha256:c1b2d8f46a8a812513012e1107cb0e68c17159a7a594208005a57dc776e1bdc7"}, - {file = "anyio-4.4.0.tar.gz", hash = "sha256:5aadc6a1bbb7cdb0bede386cac5e2940f5e2ff3aa20277e991cf028e0585ce94"}, + {file = "anyio-4.6.0-py3-none-any.whl", hash = "sha256:c7d2e9d63e31599eeb636c8c5c03a7e108d73b345f064f1c19fdc87b79036a9a"}, + {file = "anyio-4.6.0.tar.gz", hash = "sha256:137b4559cbb034c477165047febb6ff83f390fc3b20bf181c1fc0a728cb8beeb"}, ] [package.dependencies] @@ -73,9 +96,28 @@ sniffio = ">=1.1" typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""} [package.extras] -doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] -test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] -trio = ["trio (>=0.23)"] +doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.21.0b1)"] +trio = ["trio (>=0.26.1)"] + +[[package]] +name = "apprise" +version = "1.9.0" +description = "Push Notifications that work with just about every platform!" +optional = false +python-versions = ">=3.6" +files = [ + {file = "apprise-1.9.0-py3-none-any.whl", hash = "sha256:7192c953eeb282a7afee012512d3de0104b5a6a11bdda29283435df5a79dfe7f"}, + {file = "apprise-1.9.0.tar.gz", hash = "sha256:b5c93afd6331afe4b63a55d1cea9076e47becb4ba89b562b181c13e25bb0c7d6"}, +] + +[package.dependencies] +certifi = "*" +click = ">=5.0" +markdown = "*" +PyYAML = "*" +requests = "*" +requests-oauthlib = "*" [[package]] name = "asgi-correlation-id" @@ -94,6 +136,20 @@ starlette = ">=0.18" [package.extras] celery = ["celery"] +[[package]] +name = "asgi-lifespan" +version = "2.1.0" +description = "Programmatic startup/shutdown of ASGI apps." +optional = false +python-versions = ">=3.7" +files = [ + {file = "asgi-lifespan-2.1.0.tar.gz", hash = "sha256:5e2effaf0bfe39829cf2d64e7ecc47c7d86d676a6599f7afba378c31f5e3a308"}, + {file = "asgi_lifespan-2.1.0-py3-none-any.whl", hash = "sha256:ed840706680e28428c01e14afb3875d7d76d3206f3d5b2f2294e059b5c23804f"}, +] + +[package.dependencies] +sniffio = "*" + [[package]] name = "asgiref" version = "3.8.1" @@ -154,6 +210,96 @@ files = [ {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, ] +[[package]] +name = "asyncpg" +version = "0.29.0" +description = "An asyncio PostgreSQL driver" +optional = false +python-versions = ">=3.8.0" +files = [ + {file = "asyncpg-0.29.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72fd0ef9f00aeed37179c62282a3d14262dbbafb74ec0ba16e1b1864d8a12169"}, + {file = "asyncpg-0.29.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:52e8f8f9ff6e21f9b39ca9f8e3e33a5fcdceaf5667a8c5c32bee158e313be385"}, + {file = "asyncpg-0.29.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9e6823a7012be8b68301342ba33b4740e5a166f6bbda0aee32bc01638491a22"}, + {file = "asyncpg-0.29.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:746e80d83ad5d5464cfbf94315eb6744222ab00aa4e522b704322fb182b83610"}, + {file = "asyncpg-0.29.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ff8e8109cd6a46ff852a5e6bab8b0a047d7ea42fcb7ca5ae6eaae97d8eacf397"}, + {file = "asyncpg-0.29.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:97eb024685b1d7e72b1972863de527c11ff87960837919dac6e34754768098eb"}, + {file = "asyncpg-0.29.0-cp310-cp310-win32.whl", hash = "sha256:5bbb7f2cafd8d1fa3e65431833de2642f4b2124be61a449fa064e1a08d27e449"}, + {file = "asyncpg-0.29.0-cp310-cp310-win_amd64.whl", hash = "sha256:76c3ac6530904838a4b650b2880f8e7af938ee049e769ec2fba7cd66469d7772"}, + {file = "asyncpg-0.29.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4900ee08e85af01adb207519bb4e14b1cae8fd21e0ccf80fac6aa60b6da37b4"}, + {file = "asyncpg-0.29.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a65c1dcd820d5aea7c7d82a3fdcb70e096f8f70d1a8bf93eb458e49bfad036ac"}, + {file = "asyncpg-0.29.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b52e46f165585fd6af4863f268566668407c76b2c72d366bb8b522fa66f1870"}, + {file = "asyncpg-0.29.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc600ee8ef3dd38b8d67421359779f8ccec30b463e7aec7ed481c8346decf99f"}, + {file = "asyncpg-0.29.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:039a261af4f38f949095e1e780bae84a25ffe3e370175193174eb08d3cecab23"}, + {file = "asyncpg-0.29.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6feaf2d8f9138d190e5ec4390c1715c3e87b37715cd69b2c3dfca616134efd2b"}, + {file = "asyncpg-0.29.0-cp311-cp311-win32.whl", hash = "sha256:1e186427c88225ef730555f5fdda6c1812daa884064bfe6bc462fd3a71c4b675"}, + {file = "asyncpg-0.29.0-cp311-cp311-win_amd64.whl", hash = "sha256:cfe73ffae35f518cfd6e4e5f5abb2618ceb5ef02a2365ce64f132601000587d3"}, + {file = "asyncpg-0.29.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6011b0dc29886ab424dc042bf9eeb507670a3b40aece3439944006aafe023178"}, + {file = "asyncpg-0.29.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b544ffc66b039d5ec5a7454667f855f7fec08e0dfaf5a5490dfafbb7abbd2cfb"}, + {file = "asyncpg-0.29.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d84156d5fb530b06c493f9e7635aa18f518fa1d1395ef240d211cb563c4e2364"}, + {file = "asyncpg-0.29.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54858bc25b49d1114178d65a88e48ad50cb2b6f3e475caa0f0c092d5f527c106"}, + {file = "asyncpg-0.29.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:bde17a1861cf10d5afce80a36fca736a86769ab3579532c03e45f83ba8a09c59"}, + {file = "asyncpg-0.29.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:37a2ec1b9ff88d8773d3eb6d3784dc7e3fee7756a5317b67f923172a4748a175"}, + {file = "asyncpg-0.29.0-cp312-cp312-win32.whl", hash = "sha256:bb1292d9fad43112a85e98ecdc2e051602bce97c199920586be83254d9dafc02"}, + {file = "asyncpg-0.29.0-cp312-cp312-win_amd64.whl", hash = "sha256:2245be8ec5047a605e0b454c894e54bf2ec787ac04b1cb7e0d3c67aa1e32f0fe"}, + {file = "asyncpg-0.29.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0009a300cae37b8c525e5b449233d59cd9868fd35431abc470a3e364d2b85cb9"}, + {file = "asyncpg-0.29.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5cad1324dbb33f3ca0cd2074d5114354ed3be2b94d48ddfd88af75ebda7c43cc"}, + {file = "asyncpg-0.29.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:012d01df61e009015944ac7543d6ee30c2dc1eb2f6b10b62a3f598beb6531548"}, + {file = "asyncpg-0.29.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:000c996c53c04770798053e1730d34e30cb645ad95a63265aec82da9093d88e7"}, + {file = "asyncpg-0.29.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e0bfe9c4d3429706cf70d3249089de14d6a01192d617e9093a8e941fea8ee775"}, + {file = "asyncpg-0.29.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:642a36eb41b6313ffa328e8a5c5c2b5bea6ee138546c9c3cf1bffaad8ee36dd9"}, + {file = "asyncpg-0.29.0-cp38-cp38-win32.whl", hash = "sha256:a921372bbd0aa3a5822dd0409da61b4cd50df89ae85150149f8c119f23e8c408"}, + {file = "asyncpg-0.29.0-cp38-cp38-win_amd64.whl", hash = "sha256:103aad2b92d1506700cbf51cd8bb5441e7e72e87a7b3a2ca4e32c840f051a6a3"}, + {file = "asyncpg-0.29.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5340dd515d7e52f4c11ada32171d87c05570479dc01dc66d03ee3e150fb695da"}, + {file = "asyncpg-0.29.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e17b52c6cf83e170d3d865571ba574577ab8e533e7361a2b8ce6157d02c665d3"}, + {file = "asyncpg-0.29.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f100d23f273555f4b19b74a96840aa27b85e99ba4b1f18d4ebff0734e78dc090"}, + {file = "asyncpg-0.29.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48e7c58b516057126b363cec8ca02b804644fd012ef8e6c7e23386b7d5e6ce83"}, + {file = "asyncpg-0.29.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f9ea3f24eb4c49a615573724d88a48bd1b7821c890c2effe04f05382ed9e8810"}, + {file = "asyncpg-0.29.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8d36c7f14a22ec9e928f15f92a48207546ffe68bc412f3be718eedccdf10dc5c"}, + {file = "asyncpg-0.29.0-cp39-cp39-win32.whl", hash = "sha256:797ab8123ebaed304a1fad4d7576d5376c3a006a4100380fb9d517f0b59c1ab2"}, + {file = "asyncpg-0.29.0-cp39-cp39-win_amd64.whl", hash = "sha256:cce08a178858b426ae1aa8409b5cc171def45d4293626e7aa6510696d46decd8"}, + {file = "asyncpg-0.29.0.tar.gz", hash = "sha256:d1c49e1f44fffafd9a55e1a9b101590859d881d639ea2922516f5d9c512d354e"}, +] + +[package.dependencies] +async-timeout = {version = ">=4.0.3", markers = "python_version < \"3.12.0\""} + +[package.extras] +docs = ["Sphinx (>=5.3.0,<5.4.0)", "sphinx-rtd-theme (>=1.2.2)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] +test = ["flake8 (>=6.1,<7.0)", "uvloop (>=0.15.3)"] + +[[package]] +name = "attrs" +version = "24.2.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"}, + {file = "attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346"}, +] + +[package.extras] +benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] +tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] + +[[package]] +name = "authlib" +version = "1.3.2" +description = "The ultimate Python library in building OAuth and OpenID Connect servers and clients." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Authlib-1.3.2-py2.py3-none-any.whl", hash = "sha256:ede026a95e9f5cdc2d4364a52103f5405e75aa156357e831ef2bfd0bc5094dfc"}, + {file = "authlib-1.3.2.tar.gz", hash = "sha256:4b16130117f9eb82aa6eec97f6dd4673c3f960ac0283ccdae2897ee4bc030ba2"}, +] + +[package.dependencies] +cryptography = "*" + [[package]] name = "bcrypt" version = "4.1.3" @@ -194,17 +340,6 @@ files = [ tests = ["pytest (>=3.2.1,!=3.3.0)"] typecheck = ["mypy"] -[[package]] -name = "blinker" -version = "1.8.2" -description = "Fast, simple object-to-object and broadcast signaling" -optional = false -python-versions = ">=3.8" -files = [ - {file = "blinker-1.8.2-py3-none-any.whl", hash = "sha256:1779309f71bf239144b9399d06ae925637cf6634cf6bd131104184531bf67c01"}, - {file = "blinker-1.8.2.tar.gz", hash = "sha256:8f77b09d3bf7c795e969e9486f39c2c5e9c39d4ee07424be2bc594ece9642d83"}, -] - [[package]] name = "boto3" version = "1.34.129" @@ -244,95 +379,14 @@ urllib3 = {version = ">=1.25.4,<2.2.0 || >2.2.0,<3", markers = "python_version > crt = ["awscrt (==0.21.2)"] [[package]] -name = "brotli" -version = "1.1.0" -description = "Python bindings for the Brotli compression library" +name = "cachetools" +version = "5.5.0" +description = "Extensible memoizing collections and decorators" optional = false -python-versions = "*" +python-versions = ">=3.7" files = [ - {file = "Brotli-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e1140c64812cb9b06c922e77f1c26a75ec5e3f0fb2bf92cc8c58720dec276752"}, - {file = "Brotli-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c8fd5270e906eef71d4a8d19b7c6a43760c6abcfcc10c9101d14eb2357418de9"}, - {file = "Brotli-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ae56aca0402a0f9a3431cddda62ad71666ca9d4dc3a10a142b9dce2e3c0cda3"}, - {file = "Brotli-1.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:43ce1b9935bfa1ede40028054d7f48b5469cd02733a365eec8a329ffd342915d"}, - {file = "Brotli-1.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:7c4855522edb2e6ae7fdb58e07c3ba9111e7621a8956f481c68d5d979c93032e"}, - {file = "Brotli-1.1.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:38025d9f30cf4634f8309c6874ef871b841eb3c347e90b0851f63d1ded5212da"}, - {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e6a904cb26bfefc2f0a6f240bdf5233be78cd2488900a2f846f3c3ac8489ab80"}, - {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a37b8f0391212d29b3a91a799c8e4a2855e0576911cdfb2515487e30e322253d"}, - {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e84799f09591700a4154154cab9787452925578841a94321d5ee8fb9a9a328f0"}, - {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f66b5337fa213f1da0d9000bc8dc0cb5b896b726eefd9c6046f699b169c41b9e"}, - {file = "Brotli-1.1.0-cp310-cp310-win32.whl", hash = "sha256:be36e3d172dc816333f33520154d708a2657ea63762ec16b62ece02ab5e4daf2"}, - {file = "Brotli-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:0c6244521dda65ea562d5a69b9a26120769b7a9fb3db2fe9545935ed6735b128"}, - {file = "Brotli-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc"}, - {file = "Brotli-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c8146669223164fc87a7e3de9f81e9423c67a79d6b3447994dfb9c95da16e2d6"}, - {file = "Brotli-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30924eb4c57903d5a7526b08ef4a584acc22ab1ffa085faceb521521d2de32dd"}, - {file = "Brotli-1.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ceb64bbc6eac5a140ca649003756940f8d6a7c444a68af170b3187623b43bebf"}, - {file = "Brotli-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a469274ad18dc0e4d316eefa616d1d0c2ff9da369af19fa6f3daa4f09671fd61"}, - {file = "Brotli-1.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:524f35912131cc2cabb00edfd8d573b07f2d9f21fa824bd3fb19725a9cf06327"}, - {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5b3cc074004d968722f51e550b41a27be656ec48f8afaeeb45ebf65b561481dd"}, - {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9"}, - {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265"}, - {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8"}, - {file = "Brotli-1.1.0-cp311-cp311-win32.whl", hash = "sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50"}, - {file = "Brotli-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1"}, - {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:316cc9b17edf613ac76b1f1f305d2a748f1b976b033b049a6ecdfd5612c70409"}, - {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:caf9ee9a5775f3111642d33b86237b05808dafcd6268faa492250e9b78046eb2"}, - {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70051525001750221daa10907c77830bc889cb6d865cc0b813d9db7fefc21451"}, - {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7f4bf76817c14aa98cc6697ac02f3972cb8c3da93e9ef16b9c66573a68014f91"}, - {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0c5516f0aed654134a2fc936325cc2e642f8a0e096d075209672eb321cff408"}, - {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c3020404e0b5eefd7c9485ccf8393cfb75ec38ce75586e046573c9dc29967a0"}, - {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4ed11165dd45ce798d99a136808a794a748d5dc38511303239d4e2363c0695dc"}, - {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4093c631e96fdd49e0377a9c167bfd75b6d0bad2ace734c6eb20b348bc3ea180"}, - {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e4c4629ddad63006efa0ef968c8e4751c5868ff0b1c5c40f76524e894c50248"}, - {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:861bf317735688269936f755fa136a99d1ed526883859f86e41a5d43c61d8966"}, - {file = "Brotli-1.1.0-cp312-cp312-win32.whl", hash = "sha256:5f4d5ea15c9382135076d2fb28dde923352fe02951e66935a9efaac8f10e81b0"}, - {file = "Brotli-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:906bc3a79de8c4ae5b86d3d75a8b77e44404b0f4261714306e3ad248d8ab0951"}, - {file = "Brotli-1.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a090ca607cbb6a34b0391776f0cb48062081f5f60ddcce5d11838e67a01928d1"}, - {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2de9d02f5bda03d27ede52e8cfe7b865b066fa49258cbab568720aa5be80a47d"}, - {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2333e30a5e00fe0fe55903c8832e08ee9c3b1382aacf4db26664a16528d51b4b"}, - {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4d4a848d1837973bf0f4b5e54e3bec977d99be36a7895c61abb659301b02c112"}, - {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:fdc3ff3bfccdc6b9cc7c342c03aa2400683f0cb891d46e94b64a197910dc4064"}, - {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:5eeb539606f18a0b232d4ba45adccde4125592f3f636a6182b4a8a436548b914"}, - {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:fd5f17ff8f14003595ab414e45fce13d073e0762394f957182e69035c9f3d7c2"}, - {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:069a121ac97412d1fe506da790b3e69f52254b9df4eb665cd42460c837193354"}, - {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e93dfc1a1165e385cc8239fab7c036fb2cd8093728cbd85097b284d7b99249a2"}, - {file = "Brotli-1.1.0-cp36-cp36m-win32.whl", hash = "sha256:a599669fd7c47233438a56936988a2478685e74854088ef5293802123b5b2460"}, - {file = "Brotli-1.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:d143fd47fad1db3d7c27a1b1d66162e855b5d50a89666af46e1679c496e8e579"}, - {file = "Brotli-1.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:11d00ed0a83fa22d29bc6b64ef636c4552ebafcef57154b4ddd132f5638fbd1c"}, - {file = "Brotli-1.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f733d788519c7e3e71f0855c96618720f5d3d60c3cb829d8bbb722dddce37985"}, - {file = "Brotli-1.1.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:929811df5462e182b13920da56c6e0284af407d1de637d8e536c5cd00a7daf60"}, - {file = "Brotli-1.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0b63b949ff929fbc2d6d3ce0e924c9b93c9785d877a21a1b678877ffbbc4423a"}, - {file = "Brotli-1.1.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:d192f0f30804e55db0d0e0a35d83a9fead0e9a359a9ed0285dbacea60cc10a84"}, - {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:f296c40e23065d0d6650c4aefe7470d2a25fffda489bcc3eb66083f3ac9f6643"}, - {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:919e32f147ae93a09fe064d77d5ebf4e35502a8df75c29fb05788528e330fe74"}, - {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:23032ae55523cc7bccb4f6a0bf368cd25ad9bcdcc1990b64a647e7bbcce9cb5b"}, - {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:224e57f6eac61cc449f498cc5f0e1725ba2071a3d4f48d5d9dffba42db196438"}, - {file = "Brotli-1.1.0-cp37-cp37m-win32.whl", hash = "sha256:587ca6d3cef6e4e868102672d3bd9dc9698c309ba56d41c2b9c85bbb903cdb95"}, - {file = "Brotli-1.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2954c1c23f81c2eaf0b0717d9380bd348578a94161a65b3a2afc62c86467dd68"}, - {file = "Brotli-1.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:efa8b278894b14d6da122a72fefcebc28445f2d3f880ac59d46c90f4c13be9a3"}, - {file = "Brotli-1.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:03d20af184290887bdea3f0f78c4f737d126c74dc2f3ccadf07e54ceca3bf208"}, - {file = "Brotli-1.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6172447e1b368dcbc458925e5ddaf9113477b0ed542df258d84fa28fc45ceea7"}, - {file = "Brotli-1.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a743e5a28af5f70f9c080380a5f908d4d21d40e8f0e0c8901604d15cfa9ba751"}, - {file = "Brotli-1.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0541e747cce78e24ea12d69176f6a7ddb690e62c425e01d31cc065e69ce55b48"}, - {file = "Brotli-1.1.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:cdbc1fc1bc0bff1cef838eafe581b55bfbffaed4ed0318b724d0b71d4d377619"}, - {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:890b5a14ce214389b2cc36ce82f3093f96f4cc730c1cffdbefff77a7c71f2a97"}, - {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ab4fbee0b2d9098c74f3057b2bc055a8bd92ccf02f65944a241b4349229185a"}, - {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:141bd4d93984070e097521ed07e2575b46f817d08f9fa42b16b9b5f27b5ac088"}, - {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fce1473f3ccc4187f75b4690cfc922628aed4d3dd013d047f95a9b3919a86596"}, - {file = "Brotli-1.1.0-cp38-cp38-win32.whl", hash = "sha256:db85ecf4e609a48f4b29055f1e144231b90edc90af7481aa731ba2d059226b1b"}, - {file = "Brotli-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3d7954194c36e304e1523f55d7042c59dc53ec20dd4e9ea9d151f1b62b4415c0"}, - {file = "Brotli-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5fb2ce4b8045c78ebbc7b8f3c15062e435d47e7393cc57c25115cfd49883747a"}, - {file = "Brotli-1.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7905193081db9bfa73b1219140b3d315831cbff0d8941f22da695832f0dd188f"}, - {file = "Brotli-1.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a77def80806c421b4b0af06f45d65a136e7ac0bdca3c09d9e2ea4e515367c7e9"}, - {file = "Brotli-1.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8dadd1314583ec0bf2d1379f7008ad627cd6336625d6679cf2f8e67081b83acf"}, - {file = "Brotli-1.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:901032ff242d479a0efa956d853d16875d42157f98951c0230f69e69f9c09bac"}, - {file = "Brotli-1.1.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:22fc2a8549ffe699bfba2256ab2ed0421a7b8fadff114a3d201794e45a9ff578"}, - {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ae15b066e5ad21366600ebec29a7ccbc86812ed267e4b28e860b8ca16a2bc474"}, - {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:949f3b7c29912693cee0afcf09acd6ebc04c57af949d9bf77d6101ebb61e388c"}, - {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:89f4988c7203739d48c6f806f1e87a1d96e0806d44f0fba61dba81392c9e474d"}, - {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:de6551e370ef19f8de1807d0a9aa2cdfdce2e85ce88b122fe9f6b2b076837e59"}, - {file = "Brotli-1.1.0-cp39-cp39-win32.whl", hash = "sha256:f0d8a7a6b5983c2496e364b969f0e526647a06b075d034f3297dc66f3b360c64"}, - {file = "Brotli-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:cdad5b9014d83ca68c25d2e9444e28e967ef16e80f6b436918c700c117a85467"}, - {file = "Brotli-1.1.0.tar.gz", hash = "sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724"}, + {file = "cachetools-5.5.0-py3-none-any.whl", hash = "sha256:02134e8439cdc2ffb62023ce1debca2944c3f289d66bb17ead3ab3dede74b292"}, + {file = "cachetools-5.5.0.tar.gz", hash = "sha256:2cc24fb4cbe39633fb7badd9db9ca6295d766d9c2995f245725a46715d050f2a"}, ] [[package]] @@ -549,6 +603,17 @@ files = [ [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} +[[package]] +name = "cloudpickle" +version = "3.0.0" +description = "Pickler class to extend the standard pickle.Pickler functionality" +optional = false +python-versions = ">=3.8" +files = [ + {file = "cloudpickle-3.0.0-py3-none-any.whl", hash = "sha256:246ee7d0c295602a036e86369c77fecda4ab17b506496730f2f576d9016fd9c7"}, + {file = "cloudpickle-3.0.0.tar.gz", hash = "sha256:996d9a482c6fb4f33c1a35335cf8afd065d2a56e973270364840712d9131a882"}, +] + [[package]] name = "colorama" version = "0.4.6" @@ -560,21 +625,6 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] -[[package]] -name = "configargparse" -version = "1.7" -description = "A drop-in replacement for argparse that allows options to also be set via config files and/or environment variables." -optional = false -python-versions = ">=3.5" -files = [ - {file = "ConfigArgParse-1.7-py3-none-any.whl", hash = "sha256:d249da6591465c6c26df64a9f73d2536e743be2f244eb3ebe61114af2f94f86b"}, - {file = "ConfigArgParse-1.7.tar.gz", hash = "sha256:e7067471884de5478c58a511e529f0f9bd1c66bfef1dea90935438d6c23306d1"}, -] - -[package.extras] -test = ["PyYAML", "mock", "pytest"] -yaml = ["PyYAML"] - [[package]] name = "contourpy" version = "1.3.0" @@ -659,6 +709,17 @@ mypy = ["contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.11.1)", "types-Pil test = ["Pillow", "contourpy[test-no-images]", "matplotlib"] test-no-images = ["pytest", "pytest-cov", "pytest-rerunfailures", "pytest-xdist", "wurlitzer"] +[[package]] +name = "coolname" +version = "2.2.0" +description = "Random name and slug generator" +optional = false +python-versions = "*" +files = [ + {file = "coolname-2.2.0-py2.py3-none-any.whl", hash = "sha256:4d1563186cfaf71b394d5df4c744f8c41303b6846413645e31d31915cdeb13e8"}, + {file = "coolname-2.2.0.tar.gz", hash = "sha256:6c5d5731759104479e7ca195a9b64f7900ac5bead40183c09323c7d0be9e75c7"}, +] + [[package]] name = "coverage" version = "7.6.1" @@ -746,6 +807,70 @@ tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.1 [package.extras] toml = ["tomli"] +[[package]] +name = "croniter" +version = "3.0.3" +description = "croniter provides iteration for datetime object with cron like format" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.6" +files = [ + {file = "croniter-3.0.3-py2.py3-none-any.whl", hash = "sha256:b3bd11f270dc54ccd1f2397b813436015a86d30ffc5a7a9438eec1ed916f2101"}, + {file = "croniter-3.0.3.tar.gz", hash = "sha256:34117ec1741f10a7bd0ec3ad7d8f0eb8fa457a2feb9be32e6a2250e158957668"}, +] + +[package.dependencies] +python-dateutil = "*" +pytz = ">2021.1" + +[[package]] +name = "cryptography" +version = "43.0.1" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +optional = false +python-versions = ">=3.7" +files = [ + {file = "cryptography-43.0.1-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:8385d98f6a3bf8bb2d65a73e17ed87a3ba84f6991c155691c51112075f9ffc5d"}, + {file = "cryptography-43.0.1-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27e613d7077ac613e399270253259d9d53872aaf657471473ebfc9a52935c062"}, + {file = "cryptography-43.0.1-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68aaecc4178e90719e95298515979814bda0cbada1256a4485414860bd7ab962"}, + {file = "cryptography-43.0.1-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:de41fd81a41e53267cb020bb3a7212861da53a7d39f863585d13ea11049cf277"}, + {file = "cryptography-43.0.1-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f98bf604c82c416bc829e490c700ca1553eafdf2912a91e23a79d97d9801372a"}, + {file = "cryptography-43.0.1-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:61ec41068b7b74268fa86e3e9e12b9f0c21fcf65434571dbb13d954bceb08042"}, + {file = "cryptography-43.0.1-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:014f58110f53237ace6a408b5beb6c427b64e084eb451ef25a28308270086494"}, + {file = "cryptography-43.0.1-cp37-abi3-win32.whl", hash = "sha256:2bd51274dcd59f09dd952afb696bf9c61a7a49dfc764c04dd33ef7a6b502a1e2"}, + {file = "cryptography-43.0.1-cp37-abi3-win_amd64.whl", hash = "sha256:666ae11966643886c2987b3b721899d250855718d6d9ce41b521252a17985f4d"}, + {file = "cryptography-43.0.1-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:ac119bb76b9faa00f48128b7f5679e1d8d437365c5d26f1c2c3f0da4ce1b553d"}, + {file = "cryptography-43.0.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bbcce1a551e262dfbafb6e6252f1ae36a248e615ca44ba302df077a846a8806"}, + {file = "cryptography-43.0.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58d4e9129985185a06d849aa6df265bdd5a74ca6e1b736a77959b498e0505b85"}, + {file = "cryptography-43.0.1-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:d03a475165f3134f773d1388aeb19c2d25ba88b6a9733c5c590b9ff7bbfa2e0c"}, + {file = "cryptography-43.0.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:511f4273808ab590912a93ddb4e3914dfd8a388fed883361b02dea3791f292e1"}, + {file = "cryptography-43.0.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:80eda8b3e173f0f247f711eef62be51b599b5d425c429b5d4ca6a05e9e856baa"}, + {file = "cryptography-43.0.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:38926c50cff6f533f8a2dae3d7f19541432610d114a70808f0926d5aaa7121e4"}, + {file = "cryptography-43.0.1-cp39-abi3-win32.whl", hash = "sha256:a575913fb06e05e6b4b814d7f7468c2c660e8bb16d8d5a1faf9b33ccc569dd47"}, + {file = "cryptography-43.0.1-cp39-abi3-win_amd64.whl", hash = "sha256:d75601ad10b059ec832e78823b348bfa1a59f6b8d545db3a24fd44362a1564cb"}, + {file = "cryptography-43.0.1-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ea25acb556320250756e53f9e20a4177515f012c9eaea17eb7587a8c4d8ae034"}, + {file = "cryptography-43.0.1-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c1332724be35d23a854994ff0b66530119500b6053d0bd3363265f7e5e77288d"}, + {file = "cryptography-43.0.1-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:fba1007b3ef89946dbbb515aeeb41e30203b004f0b4b00e5e16078b518563289"}, + {file = "cryptography-43.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5b43d1ea6b378b54a1dc99dd8a2b5be47658fe9a7ce0a58ff0b55f4b43ef2b84"}, + {file = "cryptography-43.0.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:88cce104c36870d70c49c7c8fd22885875d950d9ee6ab54df2745f83ba0dc365"}, + {file = "cryptography-43.0.1-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:9d3cdb25fa98afdd3d0892d132b8d7139e2c087da1712041f6b762e4f807cc96"}, + {file = "cryptography-43.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e710bf40870f4db63c3d7d929aa9e09e4e7ee219e703f949ec4073b4294f6172"}, + {file = "cryptography-43.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7c05650fe8023c5ed0d46793d4b7d7e6cd9c04e68eabe5b0aeea836e37bdcec2"}, + {file = "cryptography-43.0.1.tar.gz", hash = "sha256:203e92a75716d8cfb491dc47c79e17d0d9207ccffcbcb35f598fbe463ae3444d"}, +] + +[package.dependencies] +cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} + +[package.extras] +docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] +docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"] +nox = ["nox"] +pep8test = ["check-sdist", "click", "mypy", "ruff"] +sdist = ["build"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["certifi", "cryptography-vectors (==43.0.1)", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] +test-randomorder = ["pytest-randomly"] + [[package]] name = "cycler" version = "0.12.1" @@ -761,6 +886,28 @@ files = [ docs = ["ipython", "matplotlib", "numpydoc", "sphinx"] tests = ["pytest", "pytest-cov", "pytest-xdist"] +[[package]] +name = "dateparser" +version = "1.2.0" +description = "Date parsing library designed to parse dates from HTML pages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "dateparser-1.2.0-py2.py3-none-any.whl", hash = "sha256:0b21ad96534e562920a0083e97fd45fa959882d4162acc358705144520a35830"}, + {file = "dateparser-1.2.0.tar.gz", hash = "sha256:7975b43a4222283e0ae15be7b4999d08c9a70e2d378ac87385b1ccf2cffbbb30"}, +] + +[package.dependencies] +python-dateutil = "*" +pytz = "*" +regex = "<2019.02.19 || >2019.02.19,<2021.8.27 || >2021.8.27" +tzlocal = "*" + +[package.extras] +calendars = ["convertdate", "hijri-converter"] +fasttext = ["fasttext"] +langdetect = ["langdetect"] + [[package]] name = "decorator" version = "5.1.1" @@ -934,13 +1081,13 @@ tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipyth [[package]] name = "faker" -version = "28.4.1" +version = "29.0.0" description = "Faker is a Python package that generates fake data for you." optional = false python-versions = ">=3.8" files = [ - {file = "Faker-28.4.1-py3-none-any.whl", hash = "sha256:e59c01d1e8b8e20a83255ab8232c143cb2af3b4f5ab6a3f5ce495f385ad8ab4c"}, - {file = "faker-28.4.1.tar.gz", hash = "sha256:4294d169255a045990720d6f3fa4134b764a4cdf46ef0d3c7553d2506f1adaa1"}, + {file = "Faker-29.0.0-py3-none-any.whl", hash = "sha256:32d0ee7d42925ff06e4a7d906ee7efbf34f5052a41a2a1eb8bb174a422a5498f"}, + {file = "faker-29.0.0.tar.gz", hash = "sha256:34e89aec594cad9773431ca479ee95c7ce03dd9f22fda2524e2373b880a2fa77"}, ] [package.dependencies] @@ -948,18 +1095,18 @@ python-dateutil = ">=2.4" [[package]] name = "fastapi" -version = "0.112.4" +version = "0.115.2" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" optional = false python-versions = ">=3.8" files = [ - {file = "fastapi-0.112.4-py3-none-any.whl", hash = "sha256:6d4f9c3301825d4620665cace8e2bc34e303f61c05a5382d1d61a048ea7f2f37"}, - {file = "fastapi-0.112.4.tar.gz", hash = "sha256:b1f72e1f72afe7902ccd639ba320abb5d57a309804f45c10ab0ce3693cadeb33"}, + {file = "fastapi-0.115.2-py3-none-any.whl", hash = "sha256:61704c71286579cc5a598763905928f24ee98bfcc07aabe84cfefb98812bbc86"}, + {file = "fastapi-0.115.2.tar.gz", hash = "sha256:3995739e0b09fa12f984bce8fa9ae197b35d433750d3d312422d846e283697ee"}, ] [package.dependencies] pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" -starlette = ">=0.37.2,<0.39.0" +starlette = ">=0.37.2,<0.41.0" typing-extensions = ">=4.8.0" [package.extras] @@ -985,120 +1132,75 @@ full = ["peewee (>=3)", "pillow (>=9.4,<10.0)", "sqlalchemy (>=1.4)"] [[package]] name = "filelock" -version = "3.16.0" +version = "3.16.1" description = "A platform independent file lock." optional = false python-versions = ">=3.8" files = [ - {file = "filelock-3.16.0-py3-none-any.whl", hash = "sha256:f6ed4c963184f4c84dd5557ce8fece759a3724b37b80c6c4f20a2f63a4dc6609"}, - {file = "filelock-3.16.0.tar.gz", hash = "sha256:81de9eb8453c769b63369f87f11131a7ab04e367f8d97ad39dc230daa07e3bec"}, + {file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"}, + {file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"}, ] [package.extras] -docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.1.1)", "pytest (>=8.3.2)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.3)"] +docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4.1)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"] typing = ["typing-extensions (>=4.12.2)"] -[[package]] -name = "flask" -version = "3.0.3" -description = "A simple framework for building complex web applications." -optional = false -python-versions = ">=3.8" -files = [ - {file = "flask-3.0.3-py3-none-any.whl", hash = "sha256:34e815dfaa43340d1d15a5c3a02b8476004037eb4840b34910c6e21679d288f3"}, - {file = "flask-3.0.3.tar.gz", hash = "sha256:ceb27b0af3823ea2737928a4d99d125a06175b8512c445cbd9a9ce200ef76842"}, -] - -[package.dependencies] -blinker = ">=1.6.2" -click = ">=8.1.3" -itsdangerous = ">=2.1.2" -Jinja2 = ">=3.1.2" -Werkzeug = ">=3.0.0" - -[package.extras] -async = ["asgiref (>=3.2)"] -dotenv = ["python-dotenv"] - -[[package]] -name = "flask-cors" -version = "5.0.0" -description = "A Flask extension adding a decorator for CORS support" -optional = false -python-versions = "*" -files = [ - {file = "Flask_Cors-5.0.0-py2.py3-none-any.whl", hash = "sha256:b9e307d082a9261c100d8fb0ba909eec6a228ed1b60a8315fd85f783d61910bc"}, - {file = "flask_cors-5.0.0.tar.gz", hash = "sha256:5aadb4b950c4e93745034594d9f3ea6591f734bb3662e16e255ffbf5e89c88ef"}, -] - -[package.dependencies] -Flask = ">=0.9" - -[[package]] -name = "flask-login" -version = "0.6.3" -description = "User authentication and session management for Flask." -optional = false -python-versions = ">=3.7" -files = [ - {file = "Flask-Login-0.6.3.tar.gz", hash = "sha256:5e23d14a607ef12806c699590b89d0f0e0d67baeec599d75947bf9c147330333"}, - {file = "Flask_Login-0.6.3-py3-none-any.whl", hash = "sha256:849b25b82a436bf830a054e74214074af59097171562ab10bfa999e6b78aae5d"}, -] - -[package.dependencies] -Flask = ">=1.0.4" -Werkzeug = ">=1.0.1" - [[package]] name = "fonttools" -version = "4.53.1" +version = "4.54.1" description = "Tools to manipulate font files" optional = false python-versions = ">=3.8" files = [ - {file = "fonttools-4.53.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0679a30b59d74b6242909945429dbddb08496935b82f91ea9bf6ad240ec23397"}, - {file = "fonttools-4.53.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e8bf06b94694251861ba7fdeea15c8ec0967f84c3d4143ae9daf42bbc7717fe3"}, - {file = "fonttools-4.53.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b96cd370a61f4d083c9c0053bf634279b094308d52fdc2dd9a22d8372fdd590d"}, - {file = "fonttools-4.53.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1c7c5aa18dd3b17995898b4a9b5929d69ef6ae2af5b96d585ff4005033d82f0"}, - {file = "fonttools-4.53.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e013aae589c1c12505da64a7d8d023e584987e51e62006e1bb30d72f26522c41"}, - {file = "fonttools-4.53.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9efd176f874cb6402e607e4cc9b4a9cd584d82fc34a4b0c811970b32ba62501f"}, - {file = "fonttools-4.53.1-cp310-cp310-win32.whl", hash = "sha256:c8696544c964500aa9439efb6761947393b70b17ef4e82d73277413f291260a4"}, - {file = "fonttools-4.53.1-cp310-cp310-win_amd64.whl", hash = "sha256:8959a59de5af6d2bec27489e98ef25a397cfa1774b375d5787509c06659b3671"}, - {file = "fonttools-4.53.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:da33440b1413bad53a8674393c5d29ce64d8c1a15ef8a77c642ffd900d07bfe1"}, - {file = "fonttools-4.53.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5ff7e5e9bad94e3a70c5cd2fa27f20b9bb9385e10cddab567b85ce5d306ea923"}, - {file = "fonttools-4.53.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6e7170d675d12eac12ad1a981d90f118c06cf680b42a2d74c6c931e54b50719"}, - {file = "fonttools-4.53.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bee32ea8765e859670c4447b0817514ca79054463b6b79784b08a8df3a4d78e3"}, - {file = "fonttools-4.53.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6e08f572625a1ee682115223eabebc4c6a2035a6917eac6f60350aba297ccadb"}, - {file = "fonttools-4.53.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b21952c092ffd827504de7e66b62aba26fdb5f9d1e435c52477e6486e9d128b2"}, - {file = "fonttools-4.53.1-cp311-cp311-win32.whl", hash = "sha256:9dfdae43b7996af46ff9da520998a32b105c7f098aeea06b2226b30e74fbba88"}, - {file = "fonttools-4.53.1-cp311-cp311-win_amd64.whl", hash = "sha256:d4d0096cb1ac7a77b3b41cd78c9b6bc4a400550e21dc7a92f2b5ab53ed74eb02"}, - {file = "fonttools-4.53.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d92d3c2a1b39631a6131c2fa25b5406855f97969b068e7e08413325bc0afba58"}, - {file = "fonttools-4.53.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3b3c8ebafbee8d9002bd8f1195d09ed2bd9ff134ddec37ee8f6a6375e6a4f0e8"}, - {file = "fonttools-4.53.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32f029c095ad66c425b0ee85553d0dc326d45d7059dbc227330fc29b43e8ba60"}, - {file = "fonttools-4.53.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10f5e6c3510b79ea27bb1ebfcc67048cde9ec67afa87c7dd7efa5c700491ac7f"}, - {file = "fonttools-4.53.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:f677ce218976496a587ab17140da141557beb91d2a5c1a14212c994093f2eae2"}, - {file = "fonttools-4.53.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9e6ceba2a01b448e36754983d376064730690401da1dd104ddb543519470a15f"}, - {file = "fonttools-4.53.1-cp312-cp312-win32.whl", hash = "sha256:791b31ebbc05197d7aa096bbc7bd76d591f05905d2fd908bf103af4488e60670"}, - {file = "fonttools-4.53.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ed170b5e17da0264b9f6fae86073be3db15fa1bd74061c8331022bca6d09bab"}, - {file = "fonttools-4.53.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:c818c058404eb2bba05e728d38049438afd649e3c409796723dfc17cd3f08749"}, - {file = "fonttools-4.53.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:651390c3b26b0c7d1f4407cad281ee7a5a85a31a110cbac5269de72a51551ba2"}, - {file = "fonttools-4.53.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e54f1bba2f655924c1138bbc7fa91abd61f45c68bd65ab5ed985942712864bbb"}, - {file = "fonttools-4.53.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9cd19cf4fe0595ebdd1d4915882b9440c3a6d30b008f3cc7587c1da7b95be5f"}, - {file = "fonttools-4.53.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:2af40ae9cdcb204fc1d8f26b190aa16534fcd4f0df756268df674a270eab575d"}, - {file = "fonttools-4.53.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:35250099b0cfb32d799fb5d6c651220a642fe2e3c7d2560490e6f1d3f9ae9169"}, - {file = "fonttools-4.53.1-cp38-cp38-win32.whl", hash = "sha256:f08df60fbd8d289152079a65da4e66a447efc1d5d5a4d3f299cdd39e3b2e4a7d"}, - {file = "fonttools-4.53.1-cp38-cp38-win_amd64.whl", hash = "sha256:7b6b35e52ddc8fb0db562133894e6ef5b4e54e1283dff606fda3eed938c36fc8"}, - {file = "fonttools-4.53.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:75a157d8d26c06e64ace9df037ee93a4938a4606a38cb7ffaf6635e60e253b7a"}, - {file = "fonttools-4.53.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4824c198f714ab5559c5be10fd1adf876712aa7989882a4ec887bf1ef3e00e31"}, - {file = "fonttools-4.53.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:becc5d7cb89c7b7afa8321b6bb3dbee0eec2b57855c90b3e9bf5fb816671fa7c"}, - {file = "fonttools-4.53.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84ec3fb43befb54be490147b4a922b5314e16372a643004f182babee9f9c3407"}, - {file = "fonttools-4.53.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:73379d3ffdeecb376640cd8ed03e9d2d0e568c9d1a4e9b16504a834ebadc2dfb"}, - {file = "fonttools-4.53.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:02569e9a810f9d11f4ae82c391ebc6fb5730d95a0657d24d754ed7763fb2d122"}, - {file = "fonttools-4.53.1-cp39-cp39-win32.whl", hash = "sha256:aae7bd54187e8bf7fd69f8ab87b2885253d3575163ad4d669a262fe97f0136cb"}, - {file = "fonttools-4.53.1-cp39-cp39-win_amd64.whl", hash = "sha256:e5b708073ea3d684235648786f5f6153a48dc8762cdfe5563c57e80787c29fbb"}, - {file = "fonttools-4.53.1-py3-none-any.whl", hash = "sha256:f1f8758a2ad110bd6432203a344269f445a2907dc24ef6bccfd0ac4e14e0d71d"}, - {file = "fonttools-4.53.1.tar.gz", hash = "sha256:e128778a8e9bc11159ce5447f76766cefbd876f44bd79aff030287254e4752c4"}, + {file = "fonttools-4.54.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7ed7ee041ff7b34cc62f07545e55e1468808691dddfd315d51dd82a6b37ddef2"}, + {file = "fonttools-4.54.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:41bb0b250c8132b2fcac148e2e9198e62ff06f3cc472065dff839327945c5882"}, + {file = "fonttools-4.54.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7965af9b67dd546e52afcf2e38641b5be956d68c425bef2158e95af11d229f10"}, + {file = "fonttools-4.54.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:278913a168f90d53378c20c23b80f4e599dca62fbffae4cc620c8eed476b723e"}, + {file = "fonttools-4.54.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0e88e3018ac809b9662615072dcd6b84dca4c2d991c6d66e1970a112503bba7e"}, + {file = "fonttools-4.54.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:4aa4817f0031206e637d1e685251ac61be64d1adef111060df84fdcbc6ab6c44"}, + {file = "fonttools-4.54.1-cp310-cp310-win32.whl", hash = "sha256:7e3b7d44e18c085fd8c16dcc6f1ad6c61b71ff463636fcb13df7b1b818bd0c02"}, + {file = "fonttools-4.54.1-cp310-cp310-win_amd64.whl", hash = "sha256:dd9cc95b8d6e27d01e1e1f1fae8559ef3c02c76317da650a19047f249acd519d"}, + {file = "fonttools-4.54.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5419771b64248484299fa77689d4f3aeed643ea6630b2ea750eeab219588ba20"}, + {file = "fonttools-4.54.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:301540e89cf4ce89d462eb23a89464fef50915255ece765d10eee8b2bf9d75b2"}, + {file = "fonttools-4.54.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76ae5091547e74e7efecc3cbf8e75200bc92daaeb88e5433c5e3e95ea8ce5aa7"}, + {file = "fonttools-4.54.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82834962b3d7c5ca98cb56001c33cf20eb110ecf442725dc5fdf36d16ed1ab07"}, + {file = "fonttools-4.54.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d26732ae002cc3d2ecab04897bb02ae3f11f06dd7575d1df46acd2f7c012a8d8"}, + {file = "fonttools-4.54.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:58974b4987b2a71ee08ade1e7f47f410c367cdfc5a94fabd599c88165f56213a"}, + {file = "fonttools-4.54.1-cp311-cp311-win32.whl", hash = "sha256:ab774fa225238986218a463f3fe151e04d8c25d7de09df7f0f5fce27b1243dbc"}, + {file = "fonttools-4.54.1-cp311-cp311-win_amd64.whl", hash = "sha256:07e005dc454eee1cc60105d6a29593459a06321c21897f769a281ff2d08939f6"}, + {file = "fonttools-4.54.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:54471032f7cb5fca694b5f1a0aaeba4af6e10ae989df408e0216f7fd6cdc405d"}, + {file = "fonttools-4.54.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8fa92cb248e573daab8d032919623cc309c005086d743afb014c836636166f08"}, + {file = "fonttools-4.54.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a911591200114969befa7f2cb74ac148bce5a91df5645443371aba6d222e263"}, + {file = "fonttools-4.54.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93d458c8a6a354dc8b48fc78d66d2a8a90b941f7fec30e94c7ad9982b1fa6bab"}, + {file = "fonttools-4.54.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5eb2474a7c5be8a5331146758debb2669bf5635c021aee00fd7c353558fc659d"}, + {file = "fonttools-4.54.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c9c563351ddc230725c4bdf7d9e1e92cbe6ae8553942bd1fb2b2ff0884e8b714"}, + {file = "fonttools-4.54.1-cp312-cp312-win32.whl", hash = "sha256:fdb062893fd6d47b527d39346e0c5578b7957dcea6d6a3b6794569370013d9ac"}, + {file = "fonttools-4.54.1-cp312-cp312-win_amd64.whl", hash = "sha256:e4564cf40cebcb53f3dc825e85910bf54835e8a8b6880d59e5159f0f325e637e"}, + {file = "fonttools-4.54.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6e37561751b017cf5c40fce0d90fd9e8274716de327ec4ffb0df957160be3bff"}, + {file = "fonttools-4.54.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:357cacb988a18aace66e5e55fe1247f2ee706e01debc4b1a20d77400354cddeb"}, + {file = "fonttools-4.54.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8e953cc0bddc2beaf3a3c3b5dd9ab7554677da72dfaf46951e193c9653e515a"}, + {file = "fonttools-4.54.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:58d29b9a294573d8319f16f2f79e42428ba9b6480442fa1836e4eb89c4d9d61c"}, + {file = "fonttools-4.54.1-cp313-cp313-win32.whl", hash = "sha256:9ef1b167e22709b46bf8168368b7b5d3efeaaa746c6d39661c1b4405b6352e58"}, + {file = "fonttools-4.54.1-cp313-cp313-win_amd64.whl", hash = "sha256:262705b1663f18c04250bd1242b0515d3bbae177bee7752be67c979b7d47f43d"}, + {file = "fonttools-4.54.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ed2f80ca07025551636c555dec2b755dd005e2ea8fbeb99fc5cdff319b70b23b"}, + {file = "fonttools-4.54.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9dc080e5a1c3b2656caff2ac2633d009b3a9ff7b5e93d0452f40cd76d3da3b3c"}, + {file = "fonttools-4.54.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d152d1be65652fc65e695e5619e0aa0982295a95a9b29b52b85775243c06556"}, + {file = "fonttools-4.54.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8583e563df41fdecef31b793b4dd3af8a9caa03397be648945ad32717a92885b"}, + {file = "fonttools-4.54.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:0d1d353ef198c422515a3e974a1e8d5b304cd54a4c2eebcae708e37cd9eeffb1"}, + {file = "fonttools-4.54.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:fda582236fee135d4daeca056c8c88ec5f6f6d88a004a79b84a02547c8f57386"}, + {file = "fonttools-4.54.1-cp38-cp38-win32.whl", hash = "sha256:e7d82b9e56716ed32574ee106cabca80992e6bbdcf25a88d97d21f73a0aae664"}, + {file = "fonttools-4.54.1-cp38-cp38-win_amd64.whl", hash = "sha256:ada215fd079e23e060157aab12eba0d66704316547f334eee9ff26f8c0d7b8ab"}, + {file = "fonttools-4.54.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f5b8a096e649768c2f4233f947cf9737f8dbf8728b90e2771e2497c6e3d21d13"}, + {file = "fonttools-4.54.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4e10d2e0a12e18f4e2dd031e1bf7c3d7017be5c8dbe524d07706179f355c5dac"}, + {file = "fonttools-4.54.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:31c32d7d4b0958600eac75eaf524b7b7cb68d3a8c196635252b7a2c30d80e986"}, + {file = "fonttools-4.54.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c39287f5c8f4a0c5a55daf9eaf9ccd223ea59eed3f6d467133cc727d7b943a55"}, + {file = "fonttools-4.54.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:a7a310c6e0471602fe3bf8efaf193d396ea561486aeaa7adc1f132e02d30c4b9"}, + {file = "fonttools-4.54.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d3b659d1029946f4ff9b6183984578041b520ce0f8fb7078bb37ec7445806b33"}, + {file = "fonttools-4.54.1-cp39-cp39-win32.whl", hash = "sha256:e96bc94c8cda58f577277d4a71f51c8e2129b8b36fd05adece6320dd3d57de8a"}, + {file = "fonttools-4.54.1-cp39-cp39-win_amd64.whl", hash = "sha256:e8a4b261c1ef91e7188a30571be6ad98d1c6d9fa2427244c545e2fa0a2494dd7"}, + {file = "fonttools-4.54.1-py3-none-any.whl", hash = "sha256:37cddd62d83dc4f72f7c3f3c2bcf2697e89a30efb152079896544a93907733bd"}, + {file = "fonttools-4.54.1.tar.gz", hash = "sha256:957f669d4922f92c171ba01bef7f29410668db09f6c02111e22b2bce446f3285"}, ] [package.extras] @@ -1116,153 +1218,43 @@ unicode = ["unicodedata2 (>=15.1.0)"] woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"] [[package]] -name = "gevent" -version = "24.2.1" -description = "Coroutine-based network library" +name = "fsspec" +version = "2024.9.0" +description = "File-system specification" optional = false python-versions = ">=3.8" files = [ - {file = "gevent-24.2.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:6f947a9abc1a129858391b3d9334c45041c08a0f23d14333d5b844b6e5c17a07"}, - {file = "gevent-24.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bde283313daf0b34a8d1bab30325f5cb0f4e11b5869dbe5bc61f8fe09a8f66f3"}, - {file = "gevent-24.2.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5a1df555431f5cd5cc189a6ee3544d24f8c52f2529134685f1e878c4972ab026"}, - {file = "gevent-24.2.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:14532a67f7cb29fb055a0e9b39f16b88ed22c66b96641df8c04bdc38c26b9ea5"}, - {file = "gevent-24.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd23df885318391856415e20acfd51a985cba6919f0be78ed89f5db9ff3a31cb"}, - {file = "gevent-24.2.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:ca80b121bbec76d7794fcb45e65a7eca660a76cc1a104ed439cdbd7df5f0b060"}, - {file = "gevent-24.2.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b9913c45d1be52d7a5db0c63977eebb51f68a2d5e6fd922d1d9b5e5fd758cc98"}, - {file = "gevent-24.2.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:918cdf8751b24986f915d743225ad6b702f83e1106e08a63b736e3a4c6ead789"}, - {file = "gevent-24.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:3d5325ccfadfd3dcf72ff88a92fb8fc0b56cacc7225f0f4b6dcf186c1a6eeabc"}, - {file = "gevent-24.2.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:03aa5879acd6b7076f6a2a307410fb1e0d288b84b03cdfd8c74db8b4bc882fc5"}, - {file = "gevent-24.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8bb35ce57a63c9a6896c71a285818a3922d8ca05d150fd1fe49a7f57287b836"}, - {file = "gevent-24.2.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d7f87c2c02e03d99b95cfa6f7a776409083a9e4d468912e18c7680437b29222c"}, - {file = "gevent-24.2.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:968581d1717bbcf170758580f5f97a2925854943c45a19be4d47299507db2eb7"}, - {file = "gevent-24.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7899a38d0ae7e817e99adb217f586d0a4620e315e4de577444ebeeed2c5729be"}, - {file = "gevent-24.2.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:f5e8e8d60e18d5f7fd49983f0c4696deeddaf6e608fbab33397671e2fcc6cc91"}, - {file = "gevent-24.2.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fbfdce91239fe306772faab57597186710d5699213f4df099d1612da7320d682"}, - {file = "gevent-24.2.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cdf66977a976d6a3cfb006afdf825d1482f84f7b81179db33941f2fc9673bb1d"}, - {file = "gevent-24.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:1dffb395e500613e0452b9503153f8f7ba587c67dd4a85fc7cd7aa7430cb02cc"}, - {file = "gevent-24.2.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:6c47ae7d1174617b3509f5d884935e788f325eb8f1a7efc95d295c68d83cce40"}, - {file = "gevent-24.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7cac622e11b4253ac4536a654fe221249065d9a69feb6cdcd4d9af3503602e0"}, - {file = "gevent-24.2.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bf5b9c72b884c6f0c4ed26ef204ee1f768b9437330422492c319470954bc4cc7"}, - {file = "gevent-24.2.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f5de3c676e57177b38857f6e3cdfbe8f38d1cd754b63200c0615eaa31f514b4f"}, - {file = "gevent-24.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4faf846ed132fd7ebfbbf4fde588a62d21faa0faa06e6f468b7faa6f436b661"}, - {file = "gevent-24.2.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:368a277bd9278ddb0fde308e6a43f544222d76ed0c4166e0d9f6b036586819d9"}, - {file = "gevent-24.2.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f8a04cf0c5b7139bc6368b461257d4a757ea2fe89b3773e494d235b7dd51119f"}, - {file = "gevent-24.2.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9d8d0642c63d453179058abc4143e30718b19a85cbf58c2744c9a63f06a1d388"}, - {file = "gevent-24.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:94138682e68ec197db42ad7442d3cf9b328069c3ad8e4e5022e6b5cd3e7ffae5"}, - {file = "gevent-24.2.1-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:8f4b8e777d39013595a7740b4463e61b1cfe5f462f1b609b28fbc1e4c4ff01e5"}, - {file = "gevent-24.2.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:141a2b24ad14f7b9576965c0c84927fc85f824a9bb19f6ec1e61e845d87c9cd8"}, - {file = "gevent-24.2.1-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:9202f22ef811053077d01f43cc02b4aaf4472792f9fd0f5081b0b05c926cca19"}, - {file = "gevent-24.2.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2955eea9c44c842c626feebf4459c42ce168685aa99594e049d03bedf53c2800"}, - {file = "gevent-24.2.1-cp38-cp38-win32.whl", hash = "sha256:44098038d5e2749b0784aabb27f1fcbb3f43edebedf64d0af0d26955611be8d6"}, - {file = "gevent-24.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:117e5837bc74a1673605fb53f8bfe22feb6e5afa411f524c835b2ddf768db0de"}, - {file = "gevent-24.2.1-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:2ae3a25ecce0a5b0cd0808ab716bfca180230112bb4bc89b46ae0061d62d4afe"}, - {file = "gevent-24.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7ceb59986456ce851160867ce4929edaffbd2f069ae25717150199f8e1548b8"}, - {file = "gevent-24.2.1-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:2e9ac06f225b696cdedbb22f9e805e2dd87bf82e8fa5e17756f94e88a9d37cf7"}, - {file = "gevent-24.2.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:90cbac1ec05b305a1b90ede61ef73126afdeb5a804ae04480d6da12c56378df1"}, - {file = "gevent-24.2.1-cp39-cp39-win32.whl", hash = "sha256:782a771424fe74bc7e75c228a1da671578c2ba4ddb2ca09b8f959abdf787331e"}, - {file = "gevent-24.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:3adfb96637f44010be8abd1b5e73b5070f851b817a0b182e601202f20fa06533"}, - {file = "gevent-24.2.1-pp310-pypy310_pp73-macosx_11_0_universal2.whl", hash = "sha256:7b00f8c9065de3ad226f7979154a7b27f3b9151c8055c162332369262fc025d8"}, - {file = "gevent-24.2.1.tar.gz", hash = "sha256:432fc76f680acf7cf188c2ee0f5d3ab73b63c1f03114c7cd8a34cebbe5aa2056"}, + {file = "fsspec-2024.9.0-py3-none-any.whl", hash = "sha256:a0947d552d8a6efa72cc2c730b12c41d043509156966cca4fb157b0f2a0c574b"}, + {file = "fsspec-2024.9.0.tar.gz", hash = "sha256:4b0afb90c2f21832df142f292649035d80b421f60a9e1c027802e5a0da2b04e8"}, ] -[package.dependencies] -cffi = {version = ">=1.12.2", markers = "platform_python_implementation == \"CPython\" and sys_platform == \"win32\""} -greenlet = [ - {version = ">=2.0.0", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.11\""}, - {version = ">=3.0rc3", markers = "platform_python_implementation == \"CPython\" and python_version >= \"3.11\""}, -] -"zope.event" = "*" -"zope.interface" = "*" - [package.extras] -dnspython = ["dnspython (>=1.16.0,<2.0)", "idna"] -docs = ["furo", "repoze.sphinx.autointerface", "sphinx", "sphinxcontrib-programoutput", "zope.schema"] -monitor = ["psutil (>=5.7.0)"] -recommended = ["cffi (>=1.12.2)", "dnspython (>=1.16.0,<2.0)", "idna", "psutil (>=5.7.0)"] -test = ["cffi (>=1.12.2)", "coverage (>=5.0)", "dnspython (>=1.16.0,<2.0)", "idna", "objgraph", "psutil (>=5.7.0)", "requests"] - -[[package]] -name = "geventhttpclient" -version = "2.3.1" -description = "HTTP client library for gevent" -optional = false -python-versions = ">=3.9" -files = [ - {file = "geventhttpclient-2.3.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:da22ab7bf5af4ba3d07cffee6de448b42696e53e7ac1fe97ed289037733bf1c2"}, - {file = "geventhttpclient-2.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2399e3d4e2fae8bbd91756189da6e9d84adf8f3eaace5eef0667874a705a29f8"}, - {file = "geventhttpclient-2.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d3e33e87d0d5b9f5782c4e6d3cb7e3592fea41af52713137d04776df7646d71b"}, - {file = "geventhttpclient-2.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c071db313866c3d0510feb6c0f40ec086ccf7e4a845701b6316c82c06e8b9b29"}, - {file = "geventhttpclient-2.3.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f36f0c6ef88a27e60af8369d9c2189fe372c6f2943182a7568e0f2ad33bb69f1"}, - {file = "geventhttpclient-2.3.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c4624843c03a5337282a42247d987c2531193e57255ee307b36eeb4f243a0c21"}, - {file = "geventhttpclient-2.3.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d614573621ba827c417786057e1e20e9f96c4f6b3878c55b1b7b54e1026693bc"}, - {file = "geventhttpclient-2.3.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5d51330a40ac9762879d0e296c279c1beae8cfa6484bb196ac829242c416b709"}, - {file = "geventhttpclient-2.3.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bc9f2162d4e8cb86bb5322d99bfd552088a3eacd540a841298f06bb8bc1f1f03"}, - {file = "geventhttpclient-2.3.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:06e59d3397e63c65ecc7a7561a5289f0cf2e2c2252e29632741e792f57f5d124"}, - {file = "geventhttpclient-2.3.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4436eef515b3e0c1d4a453ae32e047290e780a623c1eddb11026ae9d5fb03d42"}, - {file = "geventhttpclient-2.3.1-cp310-cp310-win32.whl", hash = "sha256:5d1cf7d8a4f8e15cc8fd7d88ac4cdb058d6274203a42587e594cc9f0850ac862"}, - {file = "geventhttpclient-2.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:4deaebc121036f7ea95430c2d0f80ab085b15280e6ab677a6360b70e57020e7f"}, - {file = "geventhttpclient-2.3.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f0ae055b9ce1704f2ce72c0847df28f4e14dbb3eea79256cda6c909d82688ea3"}, - {file = "geventhttpclient-2.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f087af2ac439495b5388841d6f3c4de8d2573ca9870593d78f7b554aa5cfa7f5"}, - {file = "geventhttpclient-2.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:76c367d175810facfe56281e516c9a5a4a191eff76641faaa30aa33882ed4b2f"}, - {file = "geventhttpclient-2.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a58376d0d461fe0322ff2ad362553b437daee1eeb92b4c0e3b1ffef9e77defbe"}, - {file = "geventhttpclient-2.3.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f440cc704f8a9869848a109b2c401805c17c070539b2014e7b884ecfc8591e33"}, - {file = "geventhttpclient-2.3.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f10c62994f9052f23948c19de930b2d1f063240462c8bd7077c2b3290e61f4fa"}, - {file = "geventhttpclient-2.3.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:52c45d9f3dd9627844c12e9ca347258c7be585bed54046336220e25ea6eac155"}, - {file = "geventhttpclient-2.3.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:77c1a2c6e3854bf87cd5588b95174640c8a881716bd07fa0d131d082270a6795"}, - {file = "geventhttpclient-2.3.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ce649d4e25c2d56023471df0bf1e8e2ab67dfe4ff12ce3e8fe7e6fae30cd672a"}, - {file = "geventhttpclient-2.3.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:265d9f31b4ac8f688eebef0bd4c814ffb37a16f769ad0c8c8b8c24a84db8eab5"}, - {file = "geventhttpclient-2.3.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2de436a9d61dae877e4e811fb3e2594e2a1df1b18f4280878f318aef48a562b9"}, - {file = "geventhttpclient-2.3.1-cp311-cp311-win32.whl", hash = "sha256:83e22178b9480b0a95edf0053d4f30b717d0b696b3c262beabe6964d9c5224b1"}, - {file = "geventhttpclient-2.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:97b072a282233384c1302a7dee88ad8bfedc916f06b1bc1da54f84980f1406a9"}, - {file = "geventhttpclient-2.3.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:e1c90abcc2735cd8dd2d2572a13da32f6625392dc04862decb5c6476a3ddee22"}, - {file = "geventhttpclient-2.3.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5deb41c2f51247b4e568c14964f59d7b8e537eff51900564c88af3200004e678"}, - {file = "geventhttpclient-2.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c6f1a56a66a90c4beae2f009b5e9d42db9a58ced165aa35441ace04d69cb7b37"}, - {file = "geventhttpclient-2.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ee6e741849c29e3129b1ec3828ac3a5e5dcb043402f852ea92c52334fb8cabf"}, - {file = "geventhttpclient-2.3.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0d0972096a63b1ddaa73fa3dab2c7a136e3ab8bf7999a2f85a5dee851fa77cdd"}, - {file = "geventhttpclient-2.3.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:00675ba682fb7d19d659c14686fa8a52a65e3f301b56c2a4ee6333b380dd9467"}, - {file = "geventhttpclient-2.3.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea77b67c186df90473416f4403839728f70ef6cf1689cec97b4f6bbde392a8a8"}, - {file = "geventhttpclient-2.3.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ddcc3f0fdffd9a3801e1005b73026202cffed8199863fdef9315bea9a860a032"}, - {file = "geventhttpclient-2.3.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:c9f1ef4ec048563cc621a47ff01a4f10048ff8b676d7a4d75e5433ed8e703e56"}, - {file = "geventhttpclient-2.3.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:a364b30bec7a0a00dbe256e2b6807e4dc866bead7ac84aaa51ca5e2c3d15c258"}, - {file = "geventhttpclient-2.3.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:25d255383d3d6a6fbd643bb51ae1a7e4f6f7b0dbd5f3225b537d0bd0432eaf39"}, - {file = "geventhttpclient-2.3.1-cp312-cp312-win32.whl", hash = "sha256:ad0b507e354d2f398186dcb12fe526d0594e7c9387b514fb843f7a14fdf1729a"}, - {file = "geventhttpclient-2.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:7924e0883bc2b177cfe27aa65af6bb9dd57f3e26905c7675a2d1f3ef69df7cca"}, - {file = "geventhttpclient-2.3.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:fe912c6456faab196b952adcd63e9353a0d5c8deb31c8d733d38f4f0ab22e359"}, - {file = "geventhttpclient-2.3.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8b599359779c2278018786c35d70664d441a7cd0d6baef2b2cd0d1685cf478ed"}, - {file = "geventhttpclient-2.3.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:34107b506e2c40ec7784efa282469bf86888cacddced463dceeb58c201834897"}, - {file = "geventhttpclient-2.3.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc34031905b2b31a80d88cd33d7e42b81812950e5304860ab6a65ee2803e2046"}, - {file = "geventhttpclient-2.3.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50b54f67ba2087f4d9d2172065c5c5de0f0c7f865ac350116e5452de4be31444"}, - {file = "geventhttpclient-2.3.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9ddeb431836c2ef7fd33c505a06180dc907b474e0e8537a43ff12e12c9bf0307"}, - {file = "geventhttpclient-2.3.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4890713433ca19b081f70b5f7ad258a0979ec3354f9538b50b3ad7d0a86f88de"}, - {file = "geventhttpclient-2.3.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b8ca7dcbe94cb563341087b00b6fbd0fdd70b2acc1b5d963f9ebbfbc1e5e2893"}, - {file = "geventhttpclient-2.3.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:05a1bbdd43ae36bcc10b3dbfa0806aefc5033a91efecfddfe56159446a46ea71"}, - {file = "geventhttpclient-2.3.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:f82c454595a88a5e510ae0985711ef398386998b6f37d90fc30e9ff1a2001280"}, - {file = "geventhttpclient-2.3.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6b032a5cdb1721921f4cd36aad620af318263b462962cfb23d648cdb93aab232"}, - {file = "geventhttpclient-2.3.1-cp39-cp39-win32.whl", hash = "sha256:ce2c7d18bac7ffdacc4a86cd490bea6136a7d1e1170f8624f2e3bbe3b189d5b8"}, - {file = "geventhttpclient-2.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:6ca50dd9761971d3557b897108933b34fb4a11533d52f0f2753840c740a2861a"}, - {file = "geventhttpclient-2.3.1-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c31431e38df45b3c79bf3c9427c796adb8263d622bc6fa25e2f6ba916c2aad93"}, - {file = "geventhttpclient-2.3.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:855ab1e145575769b180b57accb0573a77cd6a7392f40a6ef7bc9a4926ebd77b"}, - {file = "geventhttpclient-2.3.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a374aad77c01539e786d0c7829bec2eba034ccd45733c1bf9811ad18d2a8ecd"}, - {file = "geventhttpclient-2.3.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66c1e97460608304f400485ac099736fff3566d3d8db2038533d466f8cf5de5a"}, - {file = "geventhttpclient-2.3.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:4f843f81ee44ba4c553a1b3f73115e0ad8f00044023c24db29f5b1df3da08465"}, - {file = "geventhttpclient-2.3.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:321b73c73d73b85cfeff36b9b5ee04174ec8406fb3dadc129558a26ccb879360"}, - {file = "geventhttpclient-2.3.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:829d03c2a140edbe74ad1fb4f850384f585f3e06fc47cfe647d065412b93926f"}, - {file = "geventhttpclient-2.3.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:994c543f156db7bce3bae15491a0e041eeb3f1cf467e0d1db0c161a900a90bec"}, - {file = "geventhttpclient-2.3.1-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4beff505306aa9da5cdfe2f206b403ec7c8d06a22d6b7248365772858c4ee8c"}, - {file = "geventhttpclient-2.3.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:fb0a9673074541ccda09a2423fa16f4528819ceb1ba19d252213f6aca7d4b44a"}, - {file = "geventhttpclient-2.3.1.tar.gz", hash = "sha256:b40ddac8517c456818942c7812f555f84702105c82783238c9fcb8dc12675185"}, -] - -[package.dependencies] -brotli = "*" -certifi = "*" -gevent = "*" -urllib3 = "*" - -[package.extras] -benchmarks = ["httplib2", "httpx", "requests", "urllib3"] -dev = ["dpkt", "pytest", "requests"] -examples = ["oauth2"] +abfs = ["adlfs"] +adl = ["adlfs"] +arrow = ["pyarrow (>=1)"] +dask = ["dask", "distributed"] +dev = ["pre-commit", "ruff"] +doc = ["numpydoc", "sphinx", "sphinx-design", "sphinx-rtd-theme", "yarl"] +dropbox = ["dropbox", "dropboxdrivefs", "requests"] +full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "dask", "distributed", "dropbox", "dropboxdrivefs", "fusepy", "gcsfs", "libarchive-c", "ocifs", "panel", "paramiko", "pyarrow (>=1)", "pygit2", "requests", "s3fs", "smbprotocol", "tqdm"] +fuse = ["fusepy"] +gcs = ["gcsfs"] +git = ["pygit2"] +github = ["requests"] +gs = ["gcsfs"] +gui = ["panel"] +hdfs = ["pyarrow (>=1)"] +http = ["aiohttp (!=4.0.0a0,!=4.0.0a1)"] +libarchive = ["libarchive-c"] +oci = ["ocifs"] +s3 = ["s3fs"] +sftp = ["paramiko"] +smb = ["smbprotocol"] +ssh = ["paramiko"] +test = ["aiohttp (!=4.0.0a0,!=4.0.0a1)", "numpy", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "requests"] +test-downstream = ["aiobotocore (>=2.5.4,<3.0.0)", "dask-expr", "dask[dataframe,test]", "moto[server] (>4,<5)", "pytest-timeout", "xarray"] +test-full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "cloudpickle", "dask", "distributed", "dropbox", "dropboxdrivefs", "fastparquet", "fusepy", "gcsfs", "jinja2", "kerchunk", "libarchive-c", "lz4", "notebook", "numpy", "ocifs", "pandas", "panel", "paramiko", "pyarrow", "pyarrow (>=1)", "pyftpdlib", "pygit2", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "python-snappy", "requests", "smbprotocol", "tqdm", "urllib3", "zarr", "zstandard"] +tqdm = ["tqdm"] [[package]] name = "gitdb" @@ -1315,23 +1307,23 @@ grpc = ["grpcio (>=1.44.0,<2.0.0.dev0)"] [[package]] name = "graphene" -version = "3.3" +version = "3.4" description = "GraphQL Framework for Python" optional = false python-versions = "*" files = [ - {file = "graphene-3.3-py2.py3-none-any.whl", hash = "sha256:bb3810be33b54cb3e6969506671eb72319e8d7ba0d5ca9c8066472f75bf35a38"}, - {file = "graphene-3.3.tar.gz", hash = "sha256:529bf40c2a698954217d3713c6041d69d3f719ad0080857d7ee31327112446b0"}, + {file = "graphene-3.4-py2.py3-none-any.whl", hash = "sha256:28bf359b802cdb808130a5521135d4c88a262564598cfdc91628d2c172b99dce"}, + {file = "graphene-3.4.tar.gz", hash = "sha256:65e5ec84c5b7fb4fc41518acfbafb62ebb393d3982fbba00cd5393e431a80b97"}, ] [package.dependencies] -aniso8601 = ">=8,<10" graphql-core = ">=3.1,<3.3" graphql-relay = ">=3.1,<3.3" +typing-extensions = ">=4.7.1,<5" [package.extras] -dev = ["black (==22.3.0)", "coveralls (>=3.3,<4)", "flake8 (>=4,<5)", "iso8601 (>=1,<2)", "mock (>=4,<5)", "pytest (>=6,<7)", "pytest-asyncio (>=0.16,<2)", "pytest-benchmark (>=3.4,<4)", "pytest-cov (>=3,<4)", "pytest-mock (>=3,<4)", "pytz (==2022.1)", "snapshottest (>=0.6,<1)"] -test = ["coveralls (>=3.3,<4)", "iso8601 (>=1,<2)", "mock (>=4,<5)", "pytest (>=6,<7)", "pytest-asyncio (>=0.16,<2)", "pytest-benchmark (>=3.4,<4)", "pytest-cov (>=3,<4)", "pytest-mock (>=3,<4)", "pytz (==2022.1)", "snapshottest (>=0.6,<1)"] +dev = ["coveralls (>=3.3,<5)", "pytest (>=8,<9)", "pytest-asyncio (>=0.16,<2)", "pytest-benchmark (>=4,<5)", "pytest-cov (>=5,<6)", "pytest-mock (>=3,<4)", "ruff (==0.5.0)"] +test = ["coveralls (>=3.3,<5)", "pytest (>=8,<9)", "pytest-asyncio (>=0.16,<2)", "pytest-benchmark (>=4,<5)", "pytest-cov (>=5,<6)", "pytest-mock (>=3,<4)"] [[package]] name = "graphql-core" @@ -1358,77 +1350,122 @@ files = [ [package.dependencies] graphql-core = ">=3.2,<3.3" +[[package]] +name = "graphviz" +version = "0.20.3" +description = "Simple Python interface for Graphviz" +optional = false +python-versions = ">=3.8" +files = [ + {file = "graphviz-0.20.3-py3-none-any.whl", hash = "sha256:81f848f2904515d8cd359cc611faba817598d2feaac4027b266aa3eda7b3dde5"}, + {file = "graphviz-0.20.3.zip", hash = "sha256:09d6bc81e6a9fa392e7ba52135a9d49f1ed62526f96499325930e87ca1b5925d"}, +] + +[package.extras] +dev = ["flake8", "pep8-naming", "tox (>=3)", "twine", "wheel"] +docs = ["sphinx (>=5,<7)", "sphinx-autodoc-typehints", "sphinx-rtd-theme"] +test = ["coverage", "pytest (>=7,<8.1)", "pytest-cov", "pytest-mock (>=3)"] + [[package]] name = "greenlet" -version = "3.0.3" +version = "3.1.1" description = "Lightweight in-process concurrent programming" optional = false python-versions = ">=3.7" files = [ - {file = "greenlet-3.0.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405"}, - {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f"}, - {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb"}, - {file = "greenlet-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9"}, - {file = "greenlet-3.0.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22"}, - {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3"}, - {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d"}, - {file = "greenlet-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728"}, - {file = "greenlet-3.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf"}, - {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305"}, - {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6"}, - {file = "greenlet-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2"}, - {file = "greenlet-3.0.3-cp37-cp37m-macosx_11_0_universal2.whl", hash = "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41"}, - {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7"}, - {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6"}, - {file = "greenlet-3.0.3-cp37-cp37m-win32.whl", hash = "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d"}, - {file = "greenlet-3.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67"}, - {file = "greenlet-3.0.3-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4"}, - {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5"}, - {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da"}, - {file = "greenlet-3.0.3-cp38-cp38-win32.whl", hash = "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3"}, - {file = "greenlet-3.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf"}, - {file = "greenlet-3.0.3-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b"}, - {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6"}, - {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113"}, - {file = "greenlet-3.0.3-cp39-cp39-win32.whl", hash = "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e"}, - {file = "greenlet-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067"}, - {file = "greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491"}, + {file = "greenlet-3.1.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:0bbae94a29c9e5c7e4a2b7f0aae5c17e8e90acbfd3bf6270eeba60c39fce3563"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fde093fb93f35ca72a556cf72c92ea3ebfda3d79fc35bb19fbe685853869a83"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:36b89d13c49216cadb828db8dfa6ce86bbbc476a82d3a6c397f0efae0525bdd0"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94b6150a85e1b33b40b1464a3f9988dcc5251d6ed06842abff82e42632fac120"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93147c513fac16385d1036b7e5b102c7fbbdb163d556b791f0f11eada7ba65dc"}, + {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da7a9bff22ce038e19bf62c4dd1ec8391062878710ded0a845bcf47cc0200617"}, + {file = "greenlet-3.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b2795058c23988728eec1f36a4e5e4ebad22f8320c85f3587b539b9ac84128d7"}, + {file = "greenlet-3.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ed10eac5830befbdd0c32f83e8aa6288361597550ba669b04c48f0f9a2c843c6"}, + {file = "greenlet-3.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:77c386de38a60d1dfb8e55b8c1101d68c79dfdd25c7095d51fec2dd800892b80"}, + {file = "greenlet-3.1.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:e4d333e558953648ca09d64f13e6d8f0523fa705f51cae3f03b5983489958c70"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fc016b73c94e98e29af67ab7b9a879c307c6731a2c9da0db5a7d9b7edd1159"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d5e975ca70269d66d17dd995dafc06f1b06e8cb1ec1e9ed54c1d1e4a7c4cf26e"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b2813dc3de8c1ee3f924e4d4227999285fd335d1bcc0d2be6dc3f1f6a318ec1"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e347b3bfcf985a05e8c0b7d462ba6f15b1ee1c909e2dcad795e49e91b152c383"}, + {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e8f8c9cb53cdac7ba9793c276acd90168f416b9ce36799b9b885790f8ad6c0a"}, + {file = "greenlet-3.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:62ee94988d6b4722ce0028644418d93a52429e977d742ca2ccbe1c4f4a792511"}, + {file = "greenlet-3.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1776fd7f989fc6b8d8c8cb8da1f6b82c5814957264d1f6cf818d475ec2bf6395"}, + {file = "greenlet-3.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:48ca08c771c268a768087b408658e216133aecd835c0ded47ce955381105ba39"}, + {file = "greenlet-3.1.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:4afe7ea89de619adc868e087b4d2359282058479d7cfb94970adf4b55284574d"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f406b22b7c9a9b4f8aa9d2ab13d6ae0ac3e85c9a809bd590ad53fed2bf70dc79"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c3a701fe5a9695b238503ce5bbe8218e03c3bcccf7e204e455e7462d770268aa"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2846930c65b47d70b9d178e89c7e1a69c95c1f68ea5aa0a58646b7a96df12441"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99cfaa2110534e2cf3ba31a7abcac9d328d1d9f1b95beede58294a60348fba36"}, + {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1443279c19fca463fc33e65ef2a935a5b09bb90f978beab37729e1c3c6c25fe9"}, + {file = "greenlet-3.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b7cede291382a78f7bb5f04a529cb18e068dd29e0fb27376074b6d0317bf4dd0"}, + {file = "greenlet-3.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:23f20bb60ae298d7d8656c6ec6db134bca379ecefadb0b19ce6f19d1f232a942"}, + {file = "greenlet-3.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:7124e16b4c55d417577c2077be379514321916d5790fa287c9ed6f23bd2ffd01"}, + {file = "greenlet-3.1.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:05175c27cb459dcfc05d026c4232f9de8913ed006d42713cb8a5137bd49375f1"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:935e943ec47c4afab8965954bf49bfa639c05d4ccf9ef6e924188f762145c0ff"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:667a9706c970cb552ede35aee17339a18e8f2a87a51fba2ed39ceeeb1004798a"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8a678974d1f3aa55f6cc34dc480169d58f2e6d8958895d68845fa4ab566509e"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efc0f674aa41b92da8c49e0346318c6075d734994c3c4e4430b1c3f853e498e4"}, + {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0153404a4bb921f0ff1abeb5ce8a5131da56b953eda6e14b88dc6bbc04d2049e"}, + {file = "greenlet-3.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:275f72decf9932639c1c6dd1013a1bc266438eb32710016a1c742df5da6e60a1"}, + {file = "greenlet-3.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c4aab7f6381f38a4b42f269057aee279ab0fc7bf2e929e3d4abfae97b682a12c"}, + {file = "greenlet-3.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42703b1cf69f2aa1df7d1030b9d77d3e584a70755674d60e710f0af570f3761"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1695e76146579f8c06c1509c7ce4dfe0706f49c6831a817ac04eebb2fd02011"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7876452af029456b3f3549b696bb36a06db7c90747740c5302f74a9e9fa14b13"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ead44c85f8ab905852d3de8d86f6f8baf77109f9da589cb4fa142bd3b57b475"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8320f64b777d00dd7ccdade271eaf0cad6636343293a25074cc5566160e4de7b"}, + {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6510bf84a6b643dabba74d3049ead221257603a253d0a9873f55f6a59a65f822"}, + {file = "greenlet-3.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:04b013dc07c96f83134b1e99888e7a79979f1a247e2a9f59697fa14b5862ed01"}, + {file = "greenlet-3.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:411f015496fec93c1c8cd4e5238da364e1da7a124bcb293f085bf2860c32c6f6"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47da355d8687fd65240c364c90a31569a133b7b60de111c255ef5b606f2ae291"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:98884ecf2ffb7d7fe6bd517e8eb99d31ff7855a840fa6d0d63cd07c037f6a981"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1d4aeb8891338e60d1ab6127af1fe45def5259def8094b9c7e34690c8858803"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db32b5348615a04b82240cc67983cb315309e88d444a288934ee6ceaebcad6cc"}, + {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dcc62f31eae24de7f8dce72134c8651c58000d3b1868e01392baea7c32c247de"}, + {file = "greenlet-3.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1d3755bcb2e02de341c55b4fca7a745a24a9e7212ac953f6b3a48d117d7257aa"}, + {file = "greenlet-3.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:b8da394b34370874b4572676f36acabac172602abf054cbc4ac910219f3340af"}, + {file = "greenlet-3.1.1-cp37-cp37m-win32.whl", hash = "sha256:a0dfc6c143b519113354e780a50381508139b07d2177cb6ad6a08278ec655798"}, + {file = "greenlet-3.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:54558ea205654b50c438029505def3834e80f0869a70fb15b871c29b4575ddef"}, + {file = "greenlet-3.1.1-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:346bed03fe47414091be4ad44786d1bd8bef0c3fcad6ed3dee074a032ab408a9"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfc59d69fc48664bc693842bd57acfdd490acafda1ab52c7836e3fc75c90a111"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d21e10da6ec19b457b82636209cbe2331ff4306b54d06fa04b7c138ba18c8a81"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:37b9de5a96111fc15418819ab4c4432e4f3c2ede61e660b1e33971eba26ef9ba"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ef9ea3f137e5711f0dbe5f9263e8c009b7069d8a1acea822bd5e9dae0ae49c8"}, + {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85f3ff71e2e60bd4b4932a043fbbe0f499e263c628390b285cb599154a3b03b1"}, + {file = "greenlet-3.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:95ffcf719966dd7c453f908e208e14cde192e09fde6c7186c8f1896ef778d8cd"}, + {file = "greenlet-3.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:03a088b9de532cbfe2ba2034b2b85e82df37874681e8c470d6fb2f8c04d7e4b7"}, + {file = "greenlet-3.1.1-cp38-cp38-win32.whl", hash = "sha256:8b8b36671f10ba80e159378df9c4f15c14098c4fd73a36b9ad715f057272fbef"}, + {file = "greenlet-3.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:7017b2be767b9d43cc31416aba48aab0d2309ee31b4dbf10a1d38fb7972bdf9d"}, + {file = "greenlet-3.1.1-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:396979749bd95f018296af156201d6211240e7a23090f50a8d5d18c370084dc3"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca9d0ff5ad43e785350894d97e13633a66e2b50000e8a183a50a88d834752d42"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f6ff3b14f2df4c41660a7dec01045a045653998784bf8cfcb5a525bdffffbc8f"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94ebba31df2aa506d7b14866fed00ac141a867e63143fe5bca82a8e503b36437"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73aaad12ac0ff500f62cebed98d8789198ea0e6f233421059fa68a5aa7220145"}, + {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:63e4844797b975b9af3a3fb8f7866ff08775f5426925e1e0bbcfe7932059a12c"}, + {file = "greenlet-3.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7939aa3ca7d2a1593596e7ac6d59391ff30281ef280d8632fa03d81f7c5f955e"}, + {file = "greenlet-3.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d0028e725ee18175c6e422797c407874da24381ce0690d6b9396c204c7f7276e"}, + {file = "greenlet-3.1.1-cp39-cp39-win32.whl", hash = "sha256:5e06afd14cbaf9e00899fae69b24a32f2196c19de08fcb9f4779dd4f004e5e7c"}, + {file = "greenlet-3.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:3319aa75e0e0639bc15ff54ca327e8dc7a6fe404003496e3c6925cd3142e0e22"}, + {file = "greenlet-3.1.1.tar.gz", hash = "sha256:4ce3ac6cdb6adf7946475d7ef31777c26d94bccc377e070a7986bd2d5c515467"}, ] [package.extras] docs = ["Sphinx", "furo"] test = ["objgraph", "psutil"] +[[package]] +name = "griffe" +version = "1.3.1" +description = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API." +optional = false +python-versions = ">=3.8" +files = [ + {file = "griffe-1.3.1-py3-none-any.whl", hash = "sha256:940aeb630bc3054b4369567f150b6365be6f11eef46b0ed8623aea96e6d17b19"}, + {file = "griffe-1.3.1.tar.gz", hash = "sha256:3f86a716b631a4c0f96a43cb75d05d3c85975003c20540426c0eba3b0581c56a"}, +] + +[package.dependencies] +colorama = ">=0.4" + [[package]] name = "grpcio" version = "1.66.1" @@ -1489,13 +1526,13 @@ protobuf = ["grpcio-tools (>=1.66.1)"] [[package]] name = "gunicorn" -version = "22.0.0" +version = "23.0.0" description = "WSGI HTTP Server for UNIX" optional = false python-versions = ">=3.7" files = [ - {file = "gunicorn-22.0.0-py3-none-any.whl", hash = "sha256:350679f91b24062c86e386e198a15438d53a7a8207235a78ba1b53df4c4378d9"}, - {file = "gunicorn-22.0.0.tar.gz", hash = "sha256:4a0b436239ff76fb33f11c07a16482c521a7e09c1ce3cc293c2330afe01bec63"}, + {file = "gunicorn-23.0.0-py3-none-any.whl", hash = "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d"}, + {file = "gunicorn-23.0.0.tar.gz", hash = "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec"}, ] [package.dependencies] @@ -1519,6 +1556,21 @@ files = [ {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, ] +[[package]] +name = "h2" +version = "4.1.0" +description = "HTTP/2 State-Machine based protocol implementation" +optional = false +python-versions = ">=3.6.1" +files = [ + {file = "h2-4.1.0-py3-none-any.whl", hash = "sha256:03a46bcf682256c95b5fd9e9a99c1323584c3eec6440d379b9903d709476bc6d"}, + {file = "h2-4.1.0.tar.gz", hash = "sha256:a83aca08fbe7aacb79fec788c9c0bac936343560ed9ec18b82a13a12c28d2abb"}, +] + +[package.dependencies] +hpack = ">=4.0,<5" +hyperframe = ">=6.0,<7" + [[package]] name = "hiredis" version = "3.0.0" @@ -1622,6 +1674,17 @@ files = [ {file = "hiredis-3.0.0.tar.gz", hash = "sha256:fed8581ae26345dea1f1e0d1a96e05041a727a45e7d8d459164583e23c6ac441"}, ] +[[package]] +name = "hpack" +version = "4.0.0" +description = "Pure-Python HPACK header compression" +optional = false +python-versions = ">=3.6.1" +files = [ + {file = "hpack-4.0.0-py3-none-any.whl", hash = "sha256:84a076fad3dc9a9f8063ccb8041ef100867b1878b25ef0ee63847a5d53818a6c"}, + {file = "hpack-4.0.0.tar.gz", hash = "sha256:fc41de0c63e687ebffde81187a948221294896f6bdc0ae2312708df339430095"}, +] + [[package]] name = "httpcore" version = "1.0.5" @@ -1705,6 +1768,7 @@ files = [ [package.dependencies] anyio = "*" certifi = "*" +h2 = {version = ">=3,<5", optional = true, markers = "extra == \"http2\""} httpcore = "==1.*" idna = "*" sniffio = "*" @@ -1716,15 +1780,40 @@ http2 = ["h2 (>=3,<5)"] socks = ["socksio (==1.*)"] zstd = ["zstandard (>=0.18.0)"] +[[package]] +name = "humanize" +version = "4.10.0" +description = "Python humanize utilities" +optional = false +python-versions = ">=3.8" +files = [ + {file = "humanize-4.10.0-py3-none-any.whl", hash = "sha256:39e7ccb96923e732b5c2e27aeaa3b10a8dfeeba3eb965ba7b74a3eb0e30040a6"}, + {file = "humanize-4.10.0.tar.gz", hash = "sha256:06b6eb0293e4b85e8d385397c5868926820db32b9b654b932f57fa41c23c9978"}, +] + +[package.extras] +tests = ["freezegun", "pytest", "pytest-cov"] + +[[package]] +name = "hyperframe" +version = "6.0.1" +description = "HTTP/2 framing layer for Python" +optional = false +python-versions = ">=3.6.1" +files = [ + {file = "hyperframe-6.0.1-py3-none-any.whl", hash = "sha256:0ec6bafd80d8ad2195c4f03aacba3a8265e57bc4cff261e802bf39970ed02a15"}, + {file = "hyperframe-6.0.1.tar.gz", hash = "sha256:ae510046231dc8e9ecb1a6586f63d2347bf4c8905914aa84ba585ae85f28a914"}, +] + [[package]] name = "identify" -version = "2.6.0" +version = "2.6.1" description = "File identification library for Python" optional = false python-versions = ">=3.8" files = [ - {file = "identify-2.6.0-py2.py3-none-any.whl", hash = "sha256:e79ae4406387a9d300332b5fd366d8994f1525e8414984e1a59e058b2eda2dd0"}, - {file = "identify-2.6.0.tar.gz", hash = "sha256:cb171c685bdc31bcc4c1734698736a7d5b6c8bf2e0c15117f4d469c8640ae5cf"}, + {file = "identify-2.6.1-py2.py3-none-any.whl", hash = "sha256:53863bcac7caf8d2ed85bd20312ea5dcfc22226800f6d6881f232d861db5a8f0"}, + {file = "identify-2.6.1.tar.gz", hash = "sha256:91478c5fb7c3aac5ff7bf9b4344f803843dc586832d5f110d672b19aa1984c98"}, ] [package.extras] @@ -1732,15 +1821,18 @@ license = ["ukkonen"] [[package]] name = "idna" -version = "3.8" +version = "3.10" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.6" files = [ - {file = "idna-3.8-py3-none-any.whl", hash = "sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac"}, - {file = "idna-3.8.tar.gz", hash = "sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603"}, + {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, + {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, ] +[package.extras] +all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + [[package]] name = "importlib-metadata" version = "7.0.0" @@ -1760,50 +1852,6 @@ docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.link perf = ["ipython"] testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] -[[package]] -name = "infrahub-sdk" -version = "0.13.1-dev0" -description = "Python Client to interact with Infrahub" -optional = false -python-versions = "^3.9" -files = [] -develop = true - -[package.dependencies] -gitpython = "^3" -graphql-core = ">=3.1,<3.3" -httpx = [ - {version = ">=0.20", markers = "python_version >= \"3.9\" and python_version < \"3.11\""}, - {version = ">=0.23", markers = "python_version >= \"3.11\""}, -] -Jinja2 = {version = "^3", optional = true} -numpy = [ - {version = ">=1.24.2,<2.0.0", optional = true, markers = "python_version >= \"3.9\" and python_version < \"3.12\""}, - {version = ">=1.26.2,<2.0.0", optional = true, markers = "python_version >= \"3.12\""}, -] -pendulum = [ - {version = ">=2", markers = "python_version >= \"3.9\" and python_version < \"3.12\""}, - {version = ">=3", markers = "python_version >= \"3.12\""}, -] -pyarrow = {version = "^14", optional = true} -pydantic = ">=2.0.0,!=2.0.1,!=2.1.0,<3.0.0" -pydantic-settings = ">=2.0" -pytest = {version = "*", optional = true} -pyyaml = {version = "^6", optional = true} -rich = {version = "^13", optional = true} -toml = {version = "^0.10", optional = true} -typer = {version = "^0.12.3", optional = true} -ujson = "^5" - -[package.extras] -all = ["Jinja2 (>=3,<4)", "numpy (>=1.24.2,<2.0.0)", "numpy (>=1.26.2,<2.0.0)", "pyarrow (>=14,<15)", "pytest", "pyyaml (>=6,<7)", "rich (>=13,<14)", "toml (>=0.10,<0.11)", "typer (>=0.12.3,<0.13.0)"] -ctl = ["Jinja2 (>=3,<4)", "numpy (>=1.24.2,<2.0.0)", "numpy (>=1.26.2,<2.0.0)", "pyarrow (>=14,<15)", "pyyaml (>=6,<7)", "rich (>=13,<14)", "toml (>=0.10,<0.11)", "typer (>=0.12.3,<0.13.0)"] -tests = ["Jinja2 (>=3,<4)", "pytest", "pyyaml (>=6,<7)", "rich (>=13,<14)"] - -[package.source] -type = "directory" -url = "python_sdk" - [[package]] name = "iniconfig" version = "2.0.0" @@ -1878,17 +1926,6 @@ files = [ [package.extras] colors = ["colorama (>=0.4.6)"] -[[package]] -name = "itsdangerous" -version = "2.2.0" -description = "Safely pass data to untrusted environments and back." -optional = false -python-versions = ">=3.8" -files = [ - {file = "itsdangerous-2.2.0-py3-none-any.whl", hash = "sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef"}, - {file = "itsdangerous-2.2.0.tar.gz", hash = "sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173"}, -] - [[package]] name = "jedi" version = "0.19.1" @@ -1925,6 +1962,21 @@ MarkupSafe = ">=2.0" [package.extras] i18n = ["Babel (>=2.7)"] +[[package]] +name = "jinja2-humanize-extension" +version = "0.4.0" +description = "a jinja2 extension to use humanize library inside jinja2 templates" +optional = false +python-versions = ">=3.0" +files = [ + {file = "jinja2_humanize_extension-0.4.0-py3-none-any.whl", hash = "sha256:b6326e2da0f7d425338bebf58848e830421defbce785f12ae812e65128518156"}, + {file = "jinja2_humanize_extension-0.4.0.tar.gz", hash = "sha256:e7d69b1c20f32815bbec722330ee8af14b1287bb1c2b0afa590dbf031cadeaa0"}, +] + +[package.dependencies] +humanize = ">=3.14.0" +jinja2 = "*" + [[package]] name = "jmespath" version = "1.0.1" @@ -1936,6 +1988,66 @@ files = [ {file = "jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe"}, ] +[[package]] +name = "jsonpatch" +version = "1.33" +description = "Apply JSON-Patches (RFC 6902)" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*" +files = [ + {file = "jsonpatch-1.33-py2.py3-none-any.whl", hash = "sha256:0ae28c0cd062bbd8b8ecc26d7d164fbbea9652a1a3693f3b956c1eae5145dade"}, + {file = "jsonpatch-1.33.tar.gz", hash = "sha256:9fcd4009c41e6d12348b4a0ff2563ba56a2923a7dfee731d004e212e1ee5030c"}, +] + +[package.dependencies] +jsonpointer = ">=1.9" + +[[package]] +name = "jsonpointer" +version = "3.0.0" +description = "Identify specific nodes in a JSON document (RFC 6901)" +optional = false +python-versions = ">=3.7" +files = [ + {file = "jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942"}, + {file = "jsonpointer-3.0.0.tar.gz", hash = "sha256:2b2d729f2091522d61c3b31f82e11870f60b68f43fbc705cb76bf4b832af59ef"}, +] + +[[package]] +name = "jsonschema" +version = "4.23.0" +description = "An implementation of JSON Schema validation for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jsonschema-4.23.0-py3-none-any.whl", hash = "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566"}, + {file = "jsonschema-4.23.0.tar.gz", hash = "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4"}, +] + +[package.dependencies] +attrs = ">=22.2.0" +jsonschema-specifications = ">=2023.03.6" +referencing = ">=0.28.4" +rpds-py = ">=0.7.1" + +[package.extras] +format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] +format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=24.6.0)"] + +[[package]] +name = "jsonschema-specifications" +version = "2023.12.1" +description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jsonschema_specifications-2023.12.1-py3-none-any.whl", hash = "sha256:87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c"}, + {file = "jsonschema_specifications-2023.12.1.tar.gz", hash = "sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc"}, +] + +[package.dependencies] +referencing = ">=0.31.0" + [[package]] name = "kiwisolver" version = "1.4.7" @@ -2059,36 +2171,6 @@ files = [ {file = "kiwisolver-1.4.7.tar.gz", hash = "sha256:9893ff81bd7107f7b685d3017cc6583daadb4fc26e4a888350df530e41980a60"}, ] -[[package]] -name = "locust" -version = "2.31.5" -description = "Developer-friendly load testing framework" -optional = false -python-versions = ">=3.9" -files = [ - {file = "locust-2.31.5-py3-none-any.whl", hash = "sha256:2904ff6307d54d3202c9ebd776f9170214f6dfbe4059504dad9e3ffaca03f600"}, - {file = "locust-2.31.5.tar.gz", hash = "sha256:14b2fa6f95bf248668e6dc92d100a44f06c5dcb1c26f88a5442bcaaee18faceb"}, -] - -[package.dependencies] -ConfigArgParse = ">=1.5.5" -flask = ">=2.0.0" -Flask-Cors = ">=3.0.10" -Flask-Login = ">=0.6.3" -gevent = ">=22.10.2" -geventhttpclient = ">=2.3.1" -msgpack = ">=1.0.0" -psutil = ">=5.9.1" -pywin32 = {version = "*", markers = "sys_platform == \"win32\""} -pyzmq = ">=25.0.0" -requests = [ - {version = ">=2.26.0", markers = "python_full_version <= \"3.11.0\""}, - {version = ">=2.32.2", markers = "python_full_version > \"3.11.0\""}, -] -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing_extensions = {version = ">=4.6.0", markers = "python_version < \"3.11\""} -Werkzeug = ">=2.0.0" - [[package]] name = "lunr" version = "0.7.0.post1" @@ -2107,6 +2189,40 @@ languages = ["nltk"] local = ["black", "build", "flake8", "hatch-fancy-pypi-readme", "ipython", "mypy", "pdbpp", "pytest-benchmark", "twine", "wheel"] tests = ["coverage", "lunr[languages]", "pytest", "pytest-timeout", "tox"] +[[package]] +name = "mako" +version = "1.3.5" +description = "A super-fast templating language that borrows the best ideas from the existing templating languages." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Mako-1.3.5-py3-none-any.whl", hash = "sha256:260f1dbc3a519453a9c856dedfe4beb4e50bd5a26d96386cb6c80856556bb91a"}, + {file = "Mako-1.3.5.tar.gz", hash = "sha256:48dbc20568c1d276a2698b36d968fa76161bf127194907ea6fc594fa81f943bc"}, +] + +[package.dependencies] +MarkupSafe = ">=0.9.2" + +[package.extras] +babel = ["Babel"] +lingua = ["lingua"] +testing = ["pytest"] + +[[package]] +name = "markdown" +version = "3.7" +description = "Python implementation of John Gruber's Markdown." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Markdown-3.7-py3-none-any.whl", hash = "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803"}, + {file = "markdown-3.7.tar.gz", hash = "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2"}, +] + +[package.extras] +docs = ["mdx-gh-links (>=0.2)", "mkdocs (>=1.5)", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-nature (>=0.6)", "mkdocs-section-index", "mkdocstrings[python]"] +testing = ["coverage", "pyyaml"] + [[package]] name = "markdown-it-py" version = "3.0.0" @@ -2299,170 +2415,110 @@ files = [ {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, ] -[[package]] -name = "msgpack" -version = "1.0.8" -description = "MessagePack serializer" -optional = false -python-versions = ">=3.8" -files = [ - {file = "msgpack-1.0.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:505fe3d03856ac7d215dbe005414bc28505d26f0c128906037e66d98c4e95868"}, - {file = "msgpack-1.0.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6b7842518a63a9f17107eb176320960ec095a8ee3b4420b5f688e24bf50c53c"}, - {file = "msgpack-1.0.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:376081f471a2ef24828b83a641a02c575d6103a3ad7fd7dade5486cad10ea659"}, - {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e390971d082dba073c05dbd56322427d3280b7cc8b53484c9377adfbae67dc2"}, - {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e073efcba9ea99db5acef3959efa45b52bc67b61b00823d2a1a6944bf45982"}, - {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82d92c773fbc6942a7a8b520d22c11cfc8fd83bba86116bfcf962c2f5c2ecdaa"}, - {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9ee32dcb8e531adae1f1ca568822e9b3a738369b3b686d1477cbc643c4a9c128"}, - {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e3aa7e51d738e0ec0afbed661261513b38b3014754c9459508399baf14ae0c9d"}, - {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:69284049d07fce531c17404fcba2bb1df472bc2dcdac642ae71a2d079d950653"}, - {file = "msgpack-1.0.8-cp310-cp310-win32.whl", hash = "sha256:13577ec9e247f8741c84d06b9ece5f654920d8365a4b636ce0e44f15e07ec693"}, - {file = "msgpack-1.0.8-cp310-cp310-win_amd64.whl", hash = "sha256:e532dbd6ddfe13946de050d7474e3f5fb6ec774fbb1a188aaf469b08cf04189a"}, - {file = "msgpack-1.0.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9517004e21664f2b5a5fd6333b0731b9cf0817403a941b393d89a2f1dc2bd836"}, - {file = "msgpack-1.0.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d16a786905034e7e34098634b184a7d81f91d4c3d246edc6bd7aefb2fd8ea6ad"}, - {file = "msgpack-1.0.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e2872993e209f7ed04d963e4b4fbae72d034844ec66bc4ca403329db2074377b"}, - {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c330eace3dd100bdb54b5653b966de7f51c26ec4a7d4e87132d9b4f738220ba"}, - {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83b5c044f3eff2a6534768ccfd50425939e7a8b5cf9a7261c385de1e20dcfc85"}, - {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1876b0b653a808fcd50123b953af170c535027bf1d053b59790eebb0aeb38950"}, - {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:dfe1f0f0ed5785c187144c46a292b8c34c1295c01da12e10ccddfc16def4448a"}, - {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3528807cbbb7f315bb81959d5961855e7ba52aa60a3097151cb21956fbc7502b"}, - {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e2f879ab92ce502a1e65fce390eab619774dda6a6ff719718069ac94084098ce"}, - {file = "msgpack-1.0.8-cp311-cp311-win32.whl", hash = "sha256:26ee97a8261e6e35885c2ecd2fd4a6d38252246f94a2aec23665a4e66d066305"}, - {file = "msgpack-1.0.8-cp311-cp311-win_amd64.whl", hash = "sha256:eadb9f826c138e6cf3c49d6f8de88225a3c0ab181a9b4ba792e006e5292d150e"}, - {file = "msgpack-1.0.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:114be227f5213ef8b215c22dde19532f5da9652e56e8ce969bf0a26d7c419fee"}, - {file = "msgpack-1.0.8-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d661dc4785affa9d0edfdd1e59ec056a58b3dbb9f196fa43587f3ddac654ac7b"}, - {file = "msgpack-1.0.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d56fd9f1f1cdc8227d7b7918f55091349741904d9520c65f0139a9755952c9e8"}, - {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0726c282d188e204281ebd8de31724b7d749adebc086873a59efb8cf7ae27df3"}, - {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8db8e423192303ed77cff4dce3a4b88dbfaf43979d280181558af5e2c3c71afc"}, - {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99881222f4a8c2f641f25703963a5cefb076adffd959e0558dc9f803a52d6a58"}, - {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b5505774ea2a73a86ea176e8a9a4a7c8bf5d521050f0f6f8426afe798689243f"}, - {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ef254a06bcea461e65ff0373d8a0dd1ed3aa004af48839f002a0c994a6f72d04"}, - {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e1dd7839443592d00e96db831eddb4111a2a81a46b028f0facd60a09ebbdd543"}, - {file = "msgpack-1.0.8-cp312-cp312-win32.whl", hash = "sha256:64d0fcd436c5683fdd7c907eeae5e2cbb5eb872fafbc03a43609d7941840995c"}, - {file = "msgpack-1.0.8-cp312-cp312-win_amd64.whl", hash = "sha256:74398a4cf19de42e1498368c36eed45d9528f5fd0155241e82c4082b7e16cffd"}, - {file = "msgpack-1.0.8-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0ceea77719d45c839fd73abcb190b8390412a890df2f83fb8cf49b2a4b5c2f40"}, - {file = "msgpack-1.0.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1ab0bbcd4d1f7b6991ee7c753655b481c50084294218de69365f8f1970d4c151"}, - {file = "msgpack-1.0.8-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1cce488457370ffd1f953846f82323cb6b2ad2190987cd4d70b2713e17268d24"}, - {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3923a1778f7e5ef31865893fdca12a8d7dc03a44b33e2a5f3295416314c09f5d"}, - {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a22e47578b30a3e199ab067a4d43d790249b3c0587d9a771921f86250c8435db"}, - {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd739c9251d01e0279ce729e37b39d49a08c0420d3fee7f2a4968c0576678f77"}, - {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d3420522057ebab1728b21ad473aa950026d07cb09da41103f8e597dfbfaeb13"}, - {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5845fdf5e5d5b78a49b826fcdc0eb2e2aa7191980e3d2cfd2a30303a74f212e2"}, - {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a0e76621f6e1f908ae52860bdcb58e1ca85231a9b0545e64509c931dd34275a"}, - {file = "msgpack-1.0.8-cp38-cp38-win32.whl", hash = "sha256:374a8e88ddab84b9ada695d255679fb99c53513c0a51778796fcf0944d6c789c"}, - {file = "msgpack-1.0.8-cp38-cp38-win_amd64.whl", hash = "sha256:f3709997b228685fe53e8c433e2df9f0cdb5f4542bd5114ed17ac3c0129b0480"}, - {file = "msgpack-1.0.8-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f51bab98d52739c50c56658cc303f190785f9a2cd97b823357e7aeae54c8f68a"}, - {file = "msgpack-1.0.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:73ee792784d48aa338bba28063e19a27e8d989344f34aad14ea6e1b9bd83f596"}, - {file = "msgpack-1.0.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f9904e24646570539a8950400602d66d2b2c492b9010ea7e965025cb71d0c86d"}, - {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e75753aeda0ddc4c28dce4c32ba2f6ec30b1b02f6c0b14e547841ba5b24f753f"}, - {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5dbf059fb4b7c240c873c1245ee112505be27497e90f7c6591261c7d3c3a8228"}, - {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4916727e31c28be8beaf11cf117d6f6f188dcc36daae4e851fee88646f5b6b18"}, - {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7938111ed1358f536daf311be244f34df7bf3cdedb3ed883787aca97778b28d8"}, - {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:493c5c5e44b06d6c9268ce21b302c9ca055c1fd3484c25ba41d34476c76ee746"}, - {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fbb160554e319f7b22ecf530a80a3ff496d38e8e07ae763b9e82fadfe96f273"}, - {file = "msgpack-1.0.8-cp39-cp39-win32.whl", hash = "sha256:f9af38a89b6a5c04b7d18c492c8ccf2aee7048aff1ce8437c4683bb5a1df893d"}, - {file = "msgpack-1.0.8-cp39-cp39-win_amd64.whl", hash = "sha256:ed59dd52075f8fc91da6053b12e8c89e37aa043f8986efd89e61fae69dc1b011"}, - {file = "msgpack-1.0.8.tar.gz", hash = "sha256:95c02b0e27e706e48d0e5426d1710ca78e0f0628d6e89d5b5a5b91a5f12274f3"}, -] - [[package]] name = "multidict" -version = "6.0.5" +version = "6.1.0" description = "multidict implementation" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:228b644ae063c10e7f324ab1ab6b548bdf6f8b47f3ec234fef1093bc2735e5f9"}, - {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:896ebdcf62683551312c30e20614305f53125750803b614e9e6ce74a96232604"}, - {file = "multidict-6.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:411bf8515f3be9813d06004cac41ccf7d1cd46dfe233705933dd163b60e37600"}, - {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d147090048129ce3c453f0292e7697d333db95e52616b3793922945804a433c"}, - {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:215ed703caf15f578dca76ee6f6b21b7603791ae090fbf1ef9d865571039ade5"}, - {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c6390cf87ff6234643428991b7359b5f59cc15155695deb4eda5c777d2b880f"}, - {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fd81c4ebdb4f214161be351eb5bcf385426bf023041da2fd9e60681f3cebae"}, - {file = "multidict-6.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3cc2ad10255f903656017363cd59436f2111443a76f996584d1077e43ee51182"}, - {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6939c95381e003f54cd4c5516740faba40cf5ad3eeff460c3ad1d3e0ea2549bf"}, - {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:220dd781e3f7af2c2c1053da9fa96d9cf3072ca58f057f4c5adaaa1cab8fc442"}, - {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:766c8f7511df26d9f11cd3a8be623e59cca73d44643abab3f8c8c07620524e4a"}, - {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:fe5d7785250541f7f5019ab9cba2c71169dc7d74d0f45253f8313f436458a4ef"}, - {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1c1496e73051918fcd4f58ff2e0f2f3066d1c76a0c6aeffd9b45d53243702cc"}, - {file = "multidict-6.0.5-cp310-cp310-win32.whl", hash = "sha256:7afcdd1fc07befad18ec4523a782cde4e93e0a2bf71239894b8d61ee578c1319"}, - {file = "multidict-6.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:99f60d34c048c5c2fabc766108c103612344c46e35d4ed9ae0673d33c8fb26e8"}, - {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f285e862d2f153a70586579c15c44656f888806ed0e5b56b64489afe4a2dbfba"}, - {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:53689bb4e102200a4fafa9de9c7c3c212ab40a7ab2c8e474491914d2305f187e"}, - {file = "multidict-6.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:612d1156111ae11d14afaf3a0669ebf6c170dbb735e510a7438ffe2369a847fd"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7be7047bd08accdb7487737631d25735c9a04327911de89ff1b26b81745bd4e3"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de170c7b4fe6859beb8926e84f7d7d6c693dfe8e27372ce3b76f01c46e489fcf"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04bde7a7b3de05732a4eb39c94574db1ec99abb56162d6c520ad26f83267de29"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85f67aed7bb647f93e7520633d8f51d3cbc6ab96957c71272b286b2f30dc70ed"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425bf820055005bfc8aa9a0b99ccb52cc2f4070153e34b701acc98d201693733"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d3eb1ceec286eba8220c26f3b0096cf189aea7057b6e7b7a2e60ed36b373b77f"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7901c05ead4b3fb75113fb1dd33eb1253c6d3ee37ce93305acd9d38e0b5f21a4"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e0e79d91e71b9867c73323a3444724d496c037e578a0e1755ae159ba14f4f3d1"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:29bfeb0dff5cb5fdab2023a7a9947b3b4af63e9c47cae2a10ad58394b517fddc"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e030047e85cbcedbfc073f71836d62dd5dadfbe7531cae27789ff66bc551bd5e"}, - {file = "multidict-6.0.5-cp311-cp311-win32.whl", hash = "sha256:2f4848aa3baa109e6ab81fe2006c77ed4d3cd1e0ac2c1fbddb7b1277c168788c"}, - {file = "multidict-6.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:2faa5ae9376faba05f630d7e5e6be05be22913782b927b19d12b8145968a85ea"}, - {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:51d035609b86722963404f711db441cf7134f1889107fb171a970c9701f92e1e"}, - {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cbebcd5bcaf1eaf302617c114aa67569dd3f090dd0ce8ba9e35e9985b41ac35b"}, - {file = "multidict-6.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ffc42c922dbfddb4a4c3b438eb056828719f07608af27d163191cb3e3aa6cc5"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ceb3b7e6a0135e092de86110c5a74e46bda4bd4fbfeeb3a3bcec79c0f861e450"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:79660376075cfd4b2c80f295528aa6beb2058fd289f4c9252f986751a4cd0496"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4428b29611e989719874670fd152b6625500ad6c686d464e99f5aaeeaca175a"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d84a5c3a5f7ce6db1f999fb9438f686bc2e09d38143f2d93d8406ed2dd6b9226"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76c0de87358b192de7ea9649beb392f107dcad9ad27276324c24c91774ca5271"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:79a6d2ba910adb2cbafc95dad936f8b9386e77c84c35bc0add315b856d7c3abb"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:92d16a3e275e38293623ebf639c471d3e03bb20b8ebb845237e0d3664914caef"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:fb616be3538599e797a2017cccca78e354c767165e8858ab5116813146041a24"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:14c2976aa9038c2629efa2c148022ed5eb4cb939e15ec7aace7ca932f48f9ba6"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:435a0984199d81ca178b9ae2c26ec3d49692d20ee29bc4c11a2a8d4514c67eda"}, - {file = "multidict-6.0.5-cp312-cp312-win32.whl", hash = "sha256:9fe7b0653ba3d9d65cbe7698cca585bf0f8c83dbbcc710db9c90f478e175f2d5"}, - {file = "multidict-6.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:01265f5e40f5a17f8241d52656ed27192be03bfa8764d88e8220141d1e4b3556"}, - {file = "multidict-6.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:19fe01cea168585ba0f678cad6f58133db2aa14eccaf22f88e4a6dccadfad8b3"}, - {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bf7a982604375a8d49b6cc1b781c1747f243d91b81035a9b43a2126c04766f5"}, - {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:107c0cdefe028703fb5dafe640a409cb146d44a6ae201e55b35a4af8e95457dd"}, - {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:403c0911cd5d5791605808b942c88a8155c2592e05332d2bf78f18697a5fa15e"}, - {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aeaf541ddbad8311a87dd695ed9642401131ea39ad7bc8cf3ef3967fd093b626"}, - {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e4972624066095e52b569e02b5ca97dbd7a7ddd4294bf4e7247d52635630dd83"}, - {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d946b0a9eb8aaa590df1fe082cee553ceab173e6cb5b03239716338629c50c7a"}, - {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b55358304d7a73d7bdf5de62494aaf70bd33015831ffd98bc498b433dfe5b10c"}, - {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:a3145cb08d8625b2d3fee1b2d596a8766352979c9bffe5d7833e0503d0f0b5e5"}, - {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d65f25da8e248202bd47445cec78e0025c0fe7582b23ec69c3b27a640dd7a8e3"}, - {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c9bf56195c6bbd293340ea82eafd0071cb3d450c703d2c93afb89f93b8386ccc"}, - {file = "multidict-6.0.5-cp37-cp37m-win32.whl", hash = "sha256:69db76c09796b313331bb7048229e3bee7928eb62bab5e071e9f7fcc4879caee"}, - {file = "multidict-6.0.5-cp37-cp37m-win_amd64.whl", hash = "sha256:fce28b3c8a81b6b36dfac9feb1de115bab619b3c13905b419ec71d03a3fc1423"}, - {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:76f067f5121dcecf0d63a67f29080b26c43c71a98b10c701b0677e4a065fbd54"}, - {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b82cc8ace10ab5bd93235dfaab2021c70637005e1ac787031f4d1da63d493c1d"}, - {file = "multidict-6.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5cb241881eefd96b46f89b1a056187ea8e9ba14ab88ba632e68d7a2ecb7aadf7"}, - {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8e94e6912639a02ce173341ff62cc1201232ab86b8a8fcc05572741a5dc7d93"}, - {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09a892e4a9fb47331da06948690ae38eaa2426de97b4ccbfafbdcbe5c8f37ff8"}, - {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55205d03e8a598cfc688c71ca8ea5f66447164efff8869517f175ea632c7cb7b"}, - {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37b15024f864916b4951adb95d3a80c9431299080341ab9544ed148091b53f50"}, - {file = "multidict-6.0.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2a1dee728b52b33eebff5072817176c172050d44d67befd681609b4746e1c2e"}, - {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:edd08e6f2f1a390bf137080507e44ccc086353c8e98c657e666c017718561b89"}, - {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:60d698e8179a42ec85172d12f50b1668254628425a6bd611aba022257cac1386"}, - {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:3d25f19500588cbc47dc19081d78131c32637c25804df8414463ec908631e453"}, - {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:4cc0ef8b962ac7a5e62b9e826bd0cd5040e7d401bc45a6835910ed699037a461"}, - {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:eca2e9d0cc5a889850e9bbd68e98314ada174ff6ccd1129500103df7a94a7a44"}, - {file = "multidict-6.0.5-cp38-cp38-win32.whl", hash = "sha256:4a6a4f196f08c58c59e0b8ef8ec441d12aee4125a7d4f4fef000ccb22f8d7241"}, - {file = "multidict-6.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:0275e35209c27a3f7951e1ce7aaf93ce0d163b28948444bec61dd7badc6d3f8c"}, - {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e7be68734bd8c9a513f2b0cfd508802d6609da068f40dc57d4e3494cefc92929"}, - {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1d9ea7a7e779d7a3561aade7d596649fbecfa5c08a7674b11b423783217933f9"}, - {file = "multidict-6.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ea1456df2a27c73ce51120fa2f519f1bea2f4a03a917f4a43c8707cf4cbbae1a"}, - {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf590b134eb70629e350691ecca88eac3e3b8b3c86992042fb82e3cb1830d5e1"}, - {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5c0631926c4f58e9a5ccce555ad7747d9a9f8b10619621f22f9635f069f6233e"}, - {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dce1c6912ab9ff5f179eaf6efe7365c1f425ed690b03341911bf4939ef2f3046"}, - {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0868d64af83169e4d4152ec612637a543f7a336e4a307b119e98042e852ad9c"}, - {file = "multidict-6.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:141b43360bfd3bdd75f15ed811850763555a251e38b2405967f8e25fb43f7d40"}, - {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7df704ca8cf4a073334e0427ae2345323613e4df18cc224f647f251e5e75a527"}, - {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6214c5a5571802c33f80e6c84713b2c79e024995b9c5897f794b43e714daeec9"}, - {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:cd6c8fca38178e12c00418de737aef1261576bd1b6e8c6134d3e729a4e858b38"}, - {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:e02021f87a5b6932fa6ce916ca004c4d441509d33bbdbeca70d05dff5e9d2479"}, - {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ebd8d160f91a764652d3e51ce0d2956b38efe37c9231cd82cfc0bed2e40b581c"}, - {file = "multidict-6.0.5-cp39-cp39-win32.whl", hash = "sha256:04da1bb8c8dbadf2a18a452639771951c662c5ad03aefe4884775454be322c9b"}, - {file = "multidict-6.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:d6f6d4f185481c9669b9447bf9d9cf3b95a0e9df9d169bbc17e363b7d5487755"}, - {file = "multidict-6.0.5-py3-none-any.whl", hash = "sha256:0d63c74e3d7ab26de115c49bffc92cc77ed23395303d496eae515d4204a625e7"}, - {file = "multidict-6.0.5.tar.gz", hash = "sha256:f7e301075edaf50500f0b341543c41194d8df3ae5caf4702f2095f3ca73dd8da"}, + {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3380252550e372e8511d49481bd836264c009adb826b23fefcc5dd3c69692f60"}, + {file = "multidict-6.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99f826cbf970077383d7de805c0681799491cb939c25450b9b5b3ced03ca99f1"}, + {file = "multidict-6.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a114d03b938376557927ab23f1e950827c3b893ccb94b62fd95d430fd0e5cf53"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1c416351ee6271b2f49b56ad7f308072f6f44b37118d69c2cad94f3fa8a40d5"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b5d83030255983181005e6cfbac1617ce9746b219bc2aad52201ad121226581"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3e97b5e938051226dc025ec80980c285b053ffb1e25a3db2a3aa3bc046bf7f56"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d618649d4e70ac6efcbba75be98b26ef5078faad23592f9b51ca492953012429"}, + {file = "multidict-6.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10524ebd769727ac77ef2278390fb0068d83f3acb7773792a5080f2b0abf7748"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ff3827aef427c89a25cc96ded1759271a93603aba9fb977a6d264648ebf989db"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:06809f4f0f7ab7ea2cabf9caca7d79c22c0758b58a71f9d32943ae13c7ace056"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f179dee3b863ab1c59580ff60f9d99f632f34ccb38bf67a33ec6b3ecadd0fd76"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:aaed8b0562be4a0876ee3b6946f6869b7bcdb571a5d1496683505944e268b160"}, + {file = "multidict-6.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3c8b88a2ccf5493b6c8da9076fb151ba106960a2df90c2633f342f120751a9e7"}, + {file = "multidict-6.1.0-cp310-cp310-win32.whl", hash = "sha256:4a9cb68166a34117d6646c0023c7b759bf197bee5ad4272f420a0141d7eb03a0"}, + {file = "multidict-6.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:20b9b5fbe0b88d0bdef2012ef7dee867f874b72528cf1d08f1d59b0e3850129d"}, + {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3efe2c2cb5763f2f1b275ad2bf7a287d3f7ebbef35648a9726e3b69284a4f3d6"}, + {file = "multidict-6.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7053d3b0353a8b9de430a4f4b4268ac9a4fb3481af37dfe49825bf45ca24156"}, + {file = "multidict-6.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:27e5fc84ccef8dfaabb09d82b7d179c7cf1a3fbc8a966f8274fcb4ab2eb4cadb"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e2b90b43e696f25c62656389d32236e049568b39320e2735d51f08fd362761b"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d83a047959d38a7ff552ff94be767b7fd79b831ad1cd9920662db05fec24fe72"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1a9dd711d0877a1ece3d2e4fea11a8e75741ca21954c919406b44e7cf971304"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec2abea24d98246b94913b76a125e855eb5c434f7c46546046372fe60f666351"}, + {file = "multidict-6.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4867cafcbc6585e4b678876c489b9273b13e9fff9f6d6d66add5e15d11d926cb"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5b48204e8d955c47c55b72779802b219a39acc3ee3d0116d5080c388970b76e3"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d8fff389528cad1618fb4b26b95550327495462cd745d879a8c7c2115248e399"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a7a9541cd308eed5e30318430a9c74d2132e9a8cb46b901326272d780bf2d423"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:da1758c76f50c39a2efd5e9859ce7d776317eb1dd34317c8152ac9251fc574a3"}, + {file = "multidict-6.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c943a53e9186688b45b323602298ab727d8865d8c9ee0b17f8d62d14b56f0753"}, + {file = "multidict-6.1.0-cp311-cp311-win32.whl", hash = "sha256:90f8717cb649eea3504091e640a1b8568faad18bd4b9fcd692853a04475a4b80"}, + {file = "multidict-6.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:82176036e65644a6cc5bd619f65f6f19781e8ec2e5330f51aa9ada7504cc1926"}, + {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b04772ed465fa3cc947db808fa306d79b43e896beb677a56fb2347ca1a49c1fa"}, + {file = "multidict-6.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6180c0ae073bddeb5a97a38c03f30c233e0a4d39cd86166251617d1bbd0af436"}, + {file = "multidict-6.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:071120490b47aa997cca00666923a83f02c7fbb44f71cf7f136df753f7fa8761"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50b3a2710631848991d0bf7de077502e8994c804bb805aeb2925a981de58ec2e"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b58c621844d55e71c1b7f7c498ce5aa6985d743a1a59034c57a905b3f153c1ef"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55b6d90641869892caa9ca42ff913f7ff1c5ece06474fbd32fb2cf6834726c95"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b820514bfc0b98a30e3d85462084779900347e4d49267f747ff54060cc33925"}, + {file = "multidict-6.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10a9b09aba0c5b48c53761b7c720aaaf7cf236d5fe394cd399c7ba662d5f9966"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e16bf3e5fc9f44632affb159d30a437bfe286ce9e02754759be5536b169b305"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76f364861c3bfc98cbbcbd402d83454ed9e01a5224bb3a28bf70002a230f73e2"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:820c661588bd01a0aa62a1283f20d2be4281b086f80dad9e955e690c75fb54a2"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:0e5f362e895bc5b9e67fe6e4ded2492d8124bdf817827f33c5b46c2fe3ffaca6"}, + {file = "multidict-6.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ec660d19bbc671e3a6443325f07263be452c453ac9e512f5eb935e7d4ac28b3"}, + {file = "multidict-6.1.0-cp312-cp312-win32.whl", hash = "sha256:58130ecf8f7b8112cdb841486404f1282b9c86ccb30d3519faf301b2e5659133"}, + {file = "multidict-6.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:188215fc0aafb8e03341995e7c4797860181562380f81ed0a87ff455b70bf1f1"}, + {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d569388c381b24671589335a3be6e1d45546c2988c2ebe30fdcada8457a31008"}, + {file = "multidict-6.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:052e10d2d37810b99cc170b785945421141bf7bb7d2f8799d431e7db229c385f"}, + {file = "multidict-6.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f90c822a402cb865e396a504f9fc8173ef34212a342d92e362ca498cad308e28"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b225d95519a5bf73860323e633a664b0d85ad3d5bede6d30d95b35d4dfe8805b"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:23bfd518810af7de1116313ebd9092cb9aa629beb12f6ed631ad53356ed6b86c"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c09fcfdccdd0b57867577b719c69e347a436b86cd83747f179dbf0cc0d4c1f3"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf6bea52ec97e95560af5ae576bdac3aa3aae0b6758c6efa115236d9e07dae44"}, + {file = "multidict-6.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57feec87371dbb3520da6192213c7d6fc892d5589a93db548331954de8248fd2"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0c3f390dc53279cbc8ba976e5f8035eab997829066756d811616b652b00a23a3"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:59bfeae4b25ec05b34f1956eaa1cb38032282cd4dfabc5056d0a1ec4d696d3aa"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b2f59caeaf7632cc633b5cf6fc449372b83bbdf0da4ae04d5be36118e46cc0aa"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:37bb93b2178e02b7b618893990941900fd25b6b9ac0fa49931a40aecdf083fe4"}, + {file = "multidict-6.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4e9f48f58c2c523d5a06faea47866cd35b32655c46b443f163d08c6d0ddb17d6"}, + {file = "multidict-6.1.0-cp313-cp313-win32.whl", hash = "sha256:3a37ffb35399029b45c6cc33640a92bef403c9fd388acce75cdc88f58bd19a81"}, + {file = "multidict-6.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:e9aa71e15d9d9beaad2c6b9319edcdc0a49a43ef5c0a4c8265ca9ee7d6c67774"}, + {file = "multidict-6.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:db7457bac39421addd0c8449933ac32d8042aae84a14911a757ae6ca3eef1392"}, + {file = "multidict-6.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d094ddec350a2fb899fec68d8353c78233debde9b7d8b4beeafa70825f1c281a"}, + {file = "multidict-6.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5845c1fd4866bb5dd3125d89b90e57ed3138241540897de748cdf19de8a2fca2"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9079dfc6a70abe341f521f78405b8949f96db48da98aeb43f9907f342f627cdc"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3914f5aaa0f36d5d60e8ece6a308ee1c9784cd75ec8151062614657a114c4478"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c08be4f460903e5a9d0f76818db3250f12e9c344e79314d1d570fc69d7f4eae4"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d093be959277cb7dee84b801eb1af388b6ad3ca6a6b6bf1ed7585895789d027d"}, + {file = "multidict-6.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3702ea6872c5a2a4eeefa6ffd36b042e9773f05b1f37ae3ef7264b1163c2dcf6"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:2090f6a85cafc5b2db085124d752757c9d251548cedabe9bd31afe6363e0aff2"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:f67f217af4b1ff66c68a87318012de788dd95fcfeb24cc889011f4e1c7454dfd"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:189f652a87e876098bbc67b4da1049afb5f5dfbaa310dd67c594b01c10388db6"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:6bb5992037f7a9eff7991ebe4273ea7f51f1c1c511e6a2ce511d0e7bdb754492"}, + {file = "multidict-6.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f4c2b9e770c4e393876e35a7046879d195cd123b4f116d299d442b335bcd"}, + {file = "multidict-6.1.0-cp38-cp38-win32.whl", hash = "sha256:e27bbb6d14416713a8bd7aaa1313c0fc8d44ee48d74497a0ff4c3a1b6ccb5167"}, + {file = "multidict-6.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:22f3105d4fb15c8f57ff3959a58fcab6ce36814486500cd7485651230ad4d4ef"}, + {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4e18b656c5e844539d506a0a06432274d7bd52a7487e6828c63a63d69185626c"}, + {file = "multidict-6.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a185f876e69897a6f3325c3f19f26a297fa058c5e456bfcff8015e9a27e83ae1"}, + {file = "multidict-6.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ab7c4ceb38d91570a650dba194e1ca87c2b543488fe9309b4212694174fd539c"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e617fb6b0b6953fffd762669610c1c4ffd05632c138d61ac7e14ad187870669c"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16e5f4bf4e603eb1fdd5d8180f1a25f30056f22e55ce51fb3d6ad4ab29f7d96f"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4c035da3f544b1882bac24115f3e2e8760f10a0107614fc9839fd232200b875"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:957cf8e4b6e123a9eea554fa7ebc85674674b713551de587eb318a2df3e00255"}, + {file = "multidict-6.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:483a6aea59cb89904e1ceabd2b47368b5600fb7de78a6e4a2c2987b2d256cf30"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:87701f25a2352e5bf7454caa64757642734da9f6b11384c1f9d1a8e699758057"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:682b987361e5fd7a139ed565e30d81fd81e9629acc7d925a205366877d8c8657"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce2186a7df133a9c895dea3331ddc5ddad42cdd0d1ea2f0a51e5d161e4762f28"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9f636b730f7e8cb19feb87094949ba54ee5357440b9658b2a32a5ce4bce53972"}, + {file = "multidict-6.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:73eae06aa53af2ea5270cc066dcaf02cc60d2994bbb2c4ef5764949257d10f43"}, + {file = "multidict-6.1.0-cp39-cp39-win32.whl", hash = "sha256:1ca0083e80e791cffc6efce7660ad24af66c8d4079d2a750b29001b53ff59ada"}, + {file = "multidict-6.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:aa466da5b15ccea564bdab9c89175c762bc12825f4659c11227f515cee76fa4a"}, + {file = "multidict-6.1.0-py3-none-any.whl", hash = "sha256:48e171e52d1c4d33888e529b999e5900356b9ae588c2f09a52dcefb158b27506"}, + {file = "multidict-6.1.0.tar.gz", hash = "sha256:22ae2ebf9b0c69d206c003e2f6a914ea33f0a932d4aa16f236afc049d9958f4a"}, ] +[package.dependencies] +typing-extensions = {version = ">=4.1.0", markers = "python_version < \"3.11\""} + [[package]] name = "mypy" version = "1.11.2" @@ -2691,6 +2747,22 @@ files = [ {file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"}, ] +[[package]] +name = "oauthlib" +version = "3.2.2" +description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" +optional = false +python-versions = ">=3.6" +files = [ + {file = "oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca"}, + {file = "oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918"}, +] + +[package.extras] +rsa = ["cryptography (>=3.0.0)"] +signals = ["blinker (>=1.4.0)"] +signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"] + [[package]] name = "opentelemetry-api" version = "1.24.0" @@ -2906,6 +2978,72 @@ files = [ [package.extras] dev = ["black", "mypy", "pytest"] +[[package]] +name = "orjson" +version = "3.10.7" +description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" +optional = false +python-versions = ">=3.8" +files = [ + {file = "orjson-3.10.7-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:74f4544f5a6405b90da8ea724d15ac9c36da4d72a738c64685003337401f5c12"}, + {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34a566f22c28222b08875b18b0dfbf8a947e69df21a9ed5c51a6bf91cfb944ac"}, + {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bf6ba8ebc8ef5792e2337fb0419f8009729335bb400ece005606336b7fd7bab7"}, + {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac7cf6222b29fbda9e3a472b41e6a5538b48f2c8f99261eecd60aafbdb60690c"}, + {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de817e2f5fc75a9e7dd350c4b0f54617b280e26d1631811a43e7e968fa71e3e9"}, + {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:348bdd16b32556cf8d7257b17cf2bdb7ab7976af4af41ebe79f9796c218f7e91"}, + {file = "orjson-3.10.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:479fd0844ddc3ca77e0fd99644c7fe2de8e8be1efcd57705b5c92e5186e8a250"}, + {file = "orjson-3.10.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:fdf5197a21dd660cf19dfd2a3ce79574588f8f5e2dbf21bda9ee2d2b46924d84"}, + {file = "orjson-3.10.7-cp310-none-win32.whl", hash = "sha256:d374d36726746c81a49f3ff8daa2898dccab6596864ebe43d50733275c629175"}, + {file = "orjson-3.10.7-cp310-none-win_amd64.whl", hash = "sha256:cb61938aec8b0ffb6eef484d480188a1777e67b05d58e41b435c74b9d84e0b9c"}, + {file = "orjson-3.10.7-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:7db8539039698ddfb9a524b4dd19508256107568cdad24f3682d5773e60504a2"}, + {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:480f455222cb7a1dea35c57a67578848537d2602b46c464472c995297117fa09"}, + {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8a9c9b168b3a19e37fe2778c0003359f07822c90fdff8f98d9d2a91b3144d8e0"}, + {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8de062de550f63185e4c1c54151bdddfc5625e37daf0aa1e75d2a1293e3b7d9a"}, + {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6b0dd04483499d1de9c8f6203f8975caf17a6000b9c0c54630cef02e44ee624e"}, + {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b58d3795dafa334fc8fd46f7c5dc013e6ad06fd5b9a4cc98cb1456e7d3558bd6"}, + {file = "orjson-3.10.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:33cfb96c24034a878d83d1a9415799a73dc77480e6c40417e5dda0710d559ee6"}, + {file = "orjson-3.10.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e724cebe1fadc2b23c6f7415bad5ee6239e00a69f30ee423f319c6af70e2a5c0"}, + {file = "orjson-3.10.7-cp311-none-win32.whl", hash = "sha256:82763b46053727a7168d29c772ed5c870fdae2f61aa8a25994c7984a19b1021f"}, + {file = "orjson-3.10.7-cp311-none-win_amd64.whl", hash = "sha256:eb8d384a24778abf29afb8e41d68fdd9a156cf6e5390c04cc07bbc24b89e98b5"}, + {file = "orjson-3.10.7-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:44a96f2d4c3af51bfac6bc4ef7b182aa33f2f054fd7f34cc0ee9a320d051d41f"}, + {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76ac14cd57df0572453543f8f2575e2d01ae9e790c21f57627803f5e79b0d3c3"}, + {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bdbb61dcc365dd9be94e8f7df91975edc9364d6a78c8f7adb69c1cdff318ec93"}, + {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b48b3db6bb6e0a08fa8c83b47bc169623f801e5cc4f24442ab2b6617da3b5313"}, + {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:23820a1563a1d386414fef15c249040042b8e5d07b40ab3fe3efbfbbcbcb8864"}, + {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0c6a008e91d10a2564edbb6ee5069a9e66df3fbe11c9a005cb411f441fd2c09"}, + {file = "orjson-3.10.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d352ee8ac1926d6193f602cbe36b1643bbd1bbcb25e3c1a657a4390f3000c9a5"}, + {file = "orjson-3.10.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d2d9f990623f15c0ae7ac608103c33dfe1486d2ed974ac3f40b693bad1a22a7b"}, + {file = "orjson-3.10.7-cp312-none-win32.whl", hash = "sha256:7c4c17f8157bd520cdb7195f75ddbd31671997cbe10aee559c2d613592e7d7eb"}, + {file = "orjson-3.10.7-cp312-none-win_amd64.whl", hash = "sha256:1d9c0e733e02ada3ed6098a10a8ee0052dd55774de3d9110d29868d24b17faa1"}, + {file = "orjson-3.10.7-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:77d325ed866876c0fa6492598ec01fe30e803272a6e8b10e992288b009cbe149"}, + {file = "orjson-3.10.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ea2c232deedcb605e853ae1db2cc94f7390ac776743b699b50b071b02bea6fe"}, + {file = "orjson-3.10.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3dcfbede6737fdbef3ce9c37af3fb6142e8e1ebc10336daa05872bfb1d87839c"}, + {file = "orjson-3.10.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:11748c135f281203f4ee695b7f80bb1358a82a63905f9f0b794769483ea854ad"}, + {file = "orjson-3.10.7-cp313-none-win32.whl", hash = "sha256:a7e19150d215c7a13f39eb787d84db274298d3f83d85463e61d277bbd7f401d2"}, + {file = "orjson-3.10.7-cp313-none-win_amd64.whl", hash = "sha256:eef44224729e9525d5261cc8d28d6b11cafc90e6bd0be2157bde69a52ec83024"}, + {file = "orjson-3.10.7-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:6ea2b2258eff652c82652d5e0f02bd5e0463a6a52abb78e49ac288827aaa1469"}, + {file = "orjson-3.10.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:430ee4d85841e1483d487e7b81401785a5dfd69db5de01314538f31f8fbf7ee1"}, + {file = "orjson-3.10.7-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4b6146e439af4c2472c56f8540d799a67a81226e11992008cb47e1267a9b3225"}, + {file = "orjson-3.10.7-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:084e537806b458911137f76097e53ce7bf5806dda33ddf6aaa66a028f8d43a23"}, + {file = "orjson-3.10.7-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4829cf2195838e3f93b70fd3b4292156fc5e097aac3739859ac0dcc722b27ac0"}, + {file = "orjson-3.10.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1193b2416cbad1a769f868b1749535d5da47626ac29445803dae7cc64b3f5c98"}, + {file = "orjson-3.10.7-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:4e6c3da13e5a57e4b3dca2de059f243ebec705857522f188f0180ae88badd354"}, + {file = "orjson-3.10.7-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c31008598424dfbe52ce8c5b47e0752dca918a4fdc4a2a32004efd9fab41d866"}, + {file = "orjson-3.10.7-cp38-none-win32.whl", hash = "sha256:7122a99831f9e7fe977dc45784d3b2edc821c172d545e6420c375e5a935f5a1c"}, + {file = "orjson-3.10.7-cp38-none-win_amd64.whl", hash = "sha256:a763bc0e58504cc803739e7df040685816145a6f3c8a589787084b54ebc9f16e"}, + {file = "orjson-3.10.7-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:e76be12658a6fa376fcd331b1ea4e58f5a06fd0220653450f0d415b8fd0fbe20"}, + {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed350d6978d28b92939bfeb1a0570c523f6170efc3f0a0ef1f1df287cd4f4960"}, + {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:144888c76f8520e39bfa121b31fd637e18d4cc2f115727865fdf9fa325b10412"}, + {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09b2d92fd95ad2402188cf51573acde57eb269eddabaa60f69ea0d733e789fe9"}, + {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5b24a579123fa884f3a3caadaed7b75eb5715ee2b17ab5c66ac97d29b18fe57f"}, + {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591bcfe7512353bd609875ab38050efe3d55e18934e2f18950c108334b4ff"}, + {file = "orjson-3.10.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f4db56635b58cd1a200b0a23744ff44206ee6aa428185e2b6c4a65b3197abdcd"}, + {file = "orjson-3.10.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0fa5886854673222618638c6df7718ea7fe2f3f2384c452c9ccedc70b4a510a5"}, + {file = "orjson-3.10.7-cp39-none-win32.whl", hash = "sha256:8272527d08450ab16eb405f47e0f4ef0e5ff5981c3d82afe0efd25dcbef2bcd2"}, + {file = "orjson-3.10.7-cp39-none-win_amd64.whl", hash = "sha256:974683d4618c0c7dbf4f69c95a979734bf183d0658611760017f6e70a145af58"}, + {file = "orjson-3.10.7.tar.gz", hash = "sha256:75ef0640403f945f3a1f9f6400686560dbfb0fb5b16589ad62cd477043c4eee3"}, +] + [[package]] name = "packaging" version = "24.1" @@ -2934,40 +3072,53 @@ testing = ["coverage", "flake8", "flake8-comprehensions", "flake8-deprecated", " [[package]] name = "pandas" -version = "2.2.2" +version = "2.2.3" description = "Powerful data structures for data analysis, time series, and statistics" optional = false python-versions = ">=3.9" files = [ - {file = "pandas-2.2.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90c6fca2acf139569e74e8781709dccb6fe25940488755716d1d354d6bc58bce"}, - {file = "pandas-2.2.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c7adfc142dac335d8c1e0dcbd37eb8617eac386596eb9e1a1b77791cf2498238"}, - {file = "pandas-2.2.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4abfe0be0d7221be4f12552995e58723c7422c80a659da13ca382697de830c08"}, - {file = "pandas-2.2.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8635c16bf3d99040fdf3ca3db669a7250ddf49c55dc4aa8fe0ae0fa8d6dcc1f0"}, - {file = "pandas-2.2.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:40ae1dffb3967a52203105a077415a86044a2bea011b5f321c6aa64b379a3f51"}, - {file = "pandas-2.2.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8e5a0b00e1e56a842f922e7fae8ae4077aee4af0acb5ae3622bd4b4c30aedf99"}, - {file = "pandas-2.2.2-cp310-cp310-win_amd64.whl", hash = "sha256:ddf818e4e6c7c6f4f7c8a12709696d193976b591cc7dc50588d3d1a6b5dc8772"}, - {file = "pandas-2.2.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:696039430f7a562b74fa45f540aca068ea85fa34c244d0deee539cb6d70aa288"}, - {file = "pandas-2.2.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e90497254aacacbc4ea6ae5e7a8cd75629d6ad2b30025a4a8b09aa4faf55151"}, - {file = "pandas-2.2.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58b84b91b0b9f4bafac2a0ac55002280c094dfc6402402332c0913a59654ab2b"}, - {file = "pandas-2.2.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2123dc9ad6a814bcdea0f099885276b31b24f7edf40f6cdbc0912672e22eee"}, - {file = "pandas-2.2.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2925720037f06e89af896c70bca73459d7e6a4be96f9de79e2d440bd499fe0db"}, - {file = "pandas-2.2.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0cace394b6ea70c01ca1595f839cf193df35d1575986e484ad35c4aeae7266c1"}, - {file = "pandas-2.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:873d13d177501a28b2756375d59816c365e42ed8417b41665f346289adc68d24"}, - {file = "pandas-2.2.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9dfde2a0ddef507a631dc9dc4af6a9489d5e2e740e226ad426a05cabfbd7c8ef"}, - {file = "pandas-2.2.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e9b79011ff7a0f4b1d6da6a61aa1aa604fb312d6647de5bad20013682d1429ce"}, - {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cb51fe389360f3b5a4d57dbd2848a5f033350336ca3b340d1c53a1fad33bcad"}, - {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eee3a87076c0756de40b05c5e9a6069c035ba43e8dd71c379e68cab2c20f16ad"}, - {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3e374f59e440d4ab45ca2fffde54b81ac3834cf5ae2cdfa69c90bc03bde04d76"}, - {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:43498c0bdb43d55cb162cdc8c06fac328ccb5d2eabe3cadeb3529ae6f0517c32"}, - {file = "pandas-2.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:d187d355ecec3629624fccb01d104da7d7f391db0311145817525281e2804d23"}, - {file = "pandas-2.2.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0ca6377b8fca51815f382bd0b697a0814c8bda55115678cbc94c30aacbb6eff2"}, - {file = "pandas-2.2.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9057e6aa78a584bc93a13f0a9bf7e753a5e9770a30b4d758b8d5f2a62a9433cd"}, - {file = "pandas-2.2.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:001910ad31abc7bf06f49dcc903755d2f7f3a9186c0c040b827e522e9cef0863"}, - {file = "pandas-2.2.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66b479b0bd07204e37583c191535505410daa8df638fd8e75ae1b383851fe921"}, - {file = "pandas-2.2.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a77e9d1c386196879aa5eb712e77461aaee433e54c68cf253053a73b7e49c33a"}, - {file = "pandas-2.2.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:92fd6b027924a7e178ac202cfbe25e53368db90d56872d20ffae94b96c7acc57"}, - {file = "pandas-2.2.2-cp39-cp39-win_amd64.whl", hash = "sha256:640cef9aa381b60e296db324337a554aeeb883ead99dc8f6c18e81a93942f5f4"}, - {file = "pandas-2.2.2.tar.gz", hash = "sha256:9e79019aba43cb4fda9e4d983f8e88ca0373adbb697ae9c6c43093218de28b54"}, + {file = "pandas-2.2.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1948ddde24197a0f7add2bdc4ca83bf2b1ef84a1bc8ccffd95eda17fd836ecb5"}, + {file = "pandas-2.2.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:381175499d3802cde0eabbaf6324cce0c4f5d52ca6f8c377c29ad442f50f6348"}, + {file = "pandas-2.2.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d9c45366def9a3dd85a6454c0e7908f2b3b8e9c138f5dc38fed7ce720d8453ed"}, + {file = "pandas-2.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86976a1c5b25ae3f8ccae3a5306e443569ee3c3faf444dfd0f41cda24667ad57"}, + {file = "pandas-2.2.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b8661b0238a69d7aafe156b7fa86c44b881387509653fdf857bebc5e4008ad42"}, + {file = "pandas-2.2.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:37e0aced3e8f539eccf2e099f65cdb9c8aa85109b0be6e93e2baff94264bdc6f"}, + {file = "pandas-2.2.3-cp310-cp310-win_amd64.whl", hash = "sha256:56534ce0746a58afaf7942ba4863e0ef81c9c50d3f0ae93e9497d6a41a057645"}, + {file = "pandas-2.2.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:66108071e1b935240e74525006034333f98bcdb87ea116de573a6a0dccb6c039"}, + {file = "pandas-2.2.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7c2875855b0ff77b2a64a0365e24455d9990730d6431b9e0ee18ad8acee13dbd"}, + {file = "pandas-2.2.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd8d0c3be0515c12fed0bdbae072551c8b54b7192c7b1fda0ba56059a0179698"}, + {file = "pandas-2.2.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c124333816c3a9b03fbeef3a9f230ba9a737e9e5bb4060aa2107a86cc0a497fc"}, + {file = "pandas-2.2.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:63cc132e40a2e084cf01adf0775b15ac515ba905d7dcca47e9a251819c575ef3"}, + {file = "pandas-2.2.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:29401dbfa9ad77319367d36940cd8a0b3a11aba16063e39632d98b0e931ddf32"}, + {file = "pandas-2.2.3-cp311-cp311-win_amd64.whl", hash = "sha256:3fc6873a41186404dad67245896a6e440baacc92f5b716ccd1bc9ed2995ab2c5"}, + {file = "pandas-2.2.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b1d432e8d08679a40e2a6d8b2f9770a5c21793a6f9f47fdd52c5ce1948a5a8a9"}, + {file = "pandas-2.2.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a5a1595fe639f5988ba6a8e5bc9649af3baf26df3998a0abe56c02609392e0a4"}, + {file = "pandas-2.2.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5de54125a92bb4d1c051c0659e6fcb75256bf799a732a87184e5ea503965bce3"}, + {file = "pandas-2.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fffb8ae78d8af97f849404f21411c95062db1496aeb3e56f146f0355c9989319"}, + {file = "pandas-2.2.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dfcb5ee8d4d50c06a51c2fffa6cff6272098ad6540aed1a76d15fb9318194d8"}, + {file = "pandas-2.2.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:062309c1b9ea12a50e8ce661145c6aab431b1e99530d3cd60640e255778bd43a"}, + {file = "pandas-2.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:59ef3764d0fe818125a5097d2ae867ca3fa64df032331b7e0917cf5d7bf66b13"}, + {file = "pandas-2.2.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f00d1345d84d8c86a63e476bb4955e46458b304b9575dcf71102b5c705320015"}, + {file = "pandas-2.2.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3508d914817e153ad359d7e069d752cdd736a247c322d932eb89e6bc84217f28"}, + {file = "pandas-2.2.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22a9d949bfc9a502d320aa04e5d02feab689d61da4e7764b62c30b991c42c5f0"}, + {file = "pandas-2.2.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3a255b2c19987fbbe62a9dfd6cff7ff2aa9ccab3fc75218fd4b7530f01efa24"}, + {file = "pandas-2.2.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:800250ecdadb6d9c78eae4990da62743b857b470883fa27f652db8bdde7f6659"}, + {file = "pandas-2.2.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6374c452ff3ec675a8f46fd9ab25c4ad0ba590b71cf0656f8b6daa5202bca3fb"}, + {file = "pandas-2.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:61c5ad4043f791b61dd4752191d9f07f0ae412515d59ba8f005832a532f8736d"}, + {file = "pandas-2.2.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3b71f27954685ee685317063bf13c7709a7ba74fc996b84fc6821c59b0f06468"}, + {file = "pandas-2.2.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:38cf8125c40dae9d5acc10fa66af8ea6fdf760b2714ee482ca691fc66e6fcb18"}, + {file = "pandas-2.2.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ba96630bc17c875161df3818780af30e43be9b166ce51c9a18c1feae342906c2"}, + {file = "pandas-2.2.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db71525a1538b30142094edb9adc10be3f3e176748cd7acc2240c2f2e5aa3a4"}, + {file = "pandas-2.2.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:15c0e1e02e93116177d29ff83e8b1619c93ddc9c49083f237d4312337a61165d"}, + {file = "pandas-2.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ad5b65698ab28ed8d7f18790a0dc58005c7629f227be9ecc1072aa74c0c1d43a"}, + {file = "pandas-2.2.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc6b93f9b966093cb0fd62ff1a7e4c09e6d546ad7c1de191767baffc57628f39"}, + {file = "pandas-2.2.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5dbca4c1acd72e8eeef4753eeca07de9b1db4f398669d5994086f788a5d7cc30"}, + {file = "pandas-2.2.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8cd6d7cc958a3910f934ea8dbdf17b2364827bb4dafc38ce6eef6bb3d65ff09c"}, + {file = "pandas-2.2.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99df71520d25fade9db7c1076ac94eb994f4d2673ef2aa2e86ee039b6746d20c"}, + {file = "pandas-2.2.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:31d0ced62d4ea3e231a9f228366919a5ea0b07440d9d4dac345376fd8e1477ea"}, + {file = "pandas-2.2.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7eee9e7cea6adf3e3d24e304ac6b8300646e2a5d1cd3a3c2abed9101b0846761"}, + {file = "pandas-2.2.3-cp39-cp39-win_amd64.whl", hash = "sha256:4850ba03528b6dd51d6c5d273c46f183f39a9baf3f0143e566b89450965b105e"}, + {file = "pandas-2.2.3.tar.gz", hash = "sha256:4f18ba62b61d7e192368b84517265a99b4d7ee8912f8708660fb4a366cc82667"}, ] [package.dependencies] @@ -3243,13 +3394,13 @@ xmp = ["defusedxml"] [[package]] name = "platformdirs" -version = "4.3.2" +version = "4.3.6" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.8" files = [ - {file = "platformdirs-4.3.2-py3-none-any.whl", hash = "sha256:eb1c8582560b34ed4ba105009a4badf7f6f85768b30126f351328507b2beb617"}, - {file = "platformdirs-4.3.2.tar.gz", hash = "sha256:9e5e27a08aa095dd127b9f2e764d74254f482fef22b0970773bfba79d091ab8c"}, + {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, + {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, ] [package.extras] @@ -3274,13 +3425,13 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "polyfactory" -version = "2.16.2" +version = "2.17.0" description = "Mock data generation factories" optional = false python-versions = "<4.0,>=3.8" files = [ - {file = "polyfactory-2.16.2-py3-none-any.whl", hash = "sha256:e5eaf97358fee07d0d8de86a93e81dc56e3be1e1514d145fea6c5f486cda6ea1"}, - {file = "polyfactory-2.16.2.tar.gz", hash = "sha256:6d0d90deb85e5bb1733ea8744c2d44eea2b31656e11b4fa73832d2e2ab5422da"}, + {file = "polyfactory-2.17.0-py3-none-any.whl", hash = "sha256:71b677c17bb7cebad9a5631b1aca7718280bdcedc1c25278253717882d1ac294"}, + {file = "polyfactory-2.17.0.tar.gz", hash = "sha256:099d86f7c79c51a2caaf7c8598cc56e7b0a57c11b5918ddf699e82380735b6b7"}, ] [package.dependencies] @@ -3325,15 +3476,100 @@ nodeenv = ">=0.11.1" pyyaml = ">=5.1" virtualenv = ">=20.10.0" +[[package]] +name = "prefect" +version = "3.0.3" +description = "Workflow orchestration and management." +optional = false +python-versions = ">=3.9" +files = [ + {file = "prefect-3.0.3-py3-none-any.whl", hash = "sha256:3291a5e687ff968249ab8996702cbf07f665b96cc1b9b996dc4dfc975810a5bd"}, + {file = "prefect-3.0.3.tar.gz", hash = "sha256:69759beb93f974467f13df9e31ff047b77c71b1dab5869ed67207ab477b7e1be"}, +] + +[package.dependencies] +aiosqlite = ">=0.17.0,<1.0.0" +alembic = ">=1.7.5,<2.0.0" +anyio = ">=4.4.0,<5.0.0" +apprise = ">=1.1.0,<2.0.0" +asgi-lifespan = ">=1.0,<3.0" +asyncpg = ">=0.23,<1.0.0" +cachetools = ">=5.3,<6.0" +click = ">=8.0,<8.2" +cloudpickle = ">=2.0,<4.0" +coolname = ">=1.0.4,<3.0.0" +croniter = ">=1.0.12,<4.0.0" +cryptography = ">=36.0.1" +dateparser = ">=1.1.1,<2.0.0" +docker = ">=4.0,<8.0" +exceptiongroup = ">=1.0.0" +fastapi = ">=0.111.0,<1.0.0" +fsspec = ">=2022.5.0" +graphviz = ">=0.20.1" +griffe = ">=0.49.0,<2.0.0" +httpcore = ">=1.0.5,<2.0.0" +httpx = {version = ">=0.23,<0.23.2 || >0.23.2", extras = ["http2"]} +humanize = ">=4.9.0,<5.0.0" +jinja2 = ">=3.0.0,<4.0.0" +jinja2-humanize-extension = ">=0.4.0" +jsonpatch = ">=1.32,<2.0" +jsonschema = ">=4.0.0,<5.0.0" +orjson = ">=3.7,<4.0" +packaging = ">=21.3,<24.3" +pathspec = ">=0.8.0" +pendulum = ">=3.0.0,<4" +prometheus-client = ">=0.20.0" +pydantic = ">=2.7,<3.0.0" +pydantic-core = ">=2.12.0,<3.0.0" +pydantic-extra-types = ">=2.8.2,<3.0.0" +pydantic-settings = "*" +python-dateutil = ">=2.8.2,<3.0.0" +python-slugify = ">=5.0,<9.0" +pytz = ">=2021.1,<2025" +pyyaml = ">=5.4.1,<7.0.0" +readchar = ">=4.0.0,<5.0.0" +rfc3339-validator = ">=0.1.4,<0.2.0" +rich = ">=11.0,<14.0" +"ruamel.yaml" = ">=0.17.0" +sniffio = ">=1.3.0,<2.0.0" +sqlalchemy = {version = ">=2.0,<3.0.0", extras = ["asyncio"]} +toml = ">=0.10.0" +typer = ">=0.12.0,<0.12.2 || >0.12.2,<0.13.0" +typing-extensions = ">=4.5.0,<5.0.0" +ujson = ">=5.8.0,<6.0.0" +uvicorn = ">=0.14.0,<0.29.0 || >0.29.0" +websockets = ">=10.4,<14.0" + +[package.extras] +aws = ["prefect-aws (>=0.5.0rc1)"] +azure = ["prefect-azure (>=0.4.0rc1)"] +bitbucket = ["prefect-bitbucket (>=0.3.0rc1)"] +dask = ["prefect-dask (>=0.3.0rc1)"] +databricks = ["prefect-databricks (>=0.3.0rc1)"] +dbt = ["prefect-dbt (>=0.6.0rc1)"] +dev = ["cairosvg", "codespell (>=2.2.6)", "ipython", "jinja2", "mkdocs", "mkdocs-gen-files", "mkdocs-material", "mkdocstrings[python]", "moto (>=5)", "mypy (>=1.9.0)", "numpy", "pillow", "pluggy (>=1.4.0)", "pre-commit", "pytest (>7,<9)", "pytest-asyncio (>=0.18.2,!=0.22.0,<0.23.0)", "pytest-benchmark", "pytest-codspeed", "pytest-cov", "pytest-env", "pytest-flakefinder", "pytest-timeout", "pytest-xdist (>=3.6.1)", "pyyaml", "redis (>=5.0.1)", "respx", "ruff", "setuptools", "types-PyYAML", "types-cachetools", "uv (>=0.4.5)", "vale", "vermin", "virtualenv", "watchfiles"] +docker = ["prefect-docker (>=0.6.0rc1)"] +email = ["prefect-email (>=0.4.0rc1)"] +gcp = ["prefect-gcp (>=0.6.0rc1)"] +github = ["prefect-github (>=0.3.0rc1)"] +gitlab = ["prefect-gitlab (>=0.3.0rc1)"] +kubernetes = ["prefect-kubernetes (>=0.4.0rc1)"] +ray = ["prefect-ray (>=0.4.0rc1)"] +redis = ["prefect-redis (>=0.2.0)"] +shell = ["prefect-shell (>=0.3.0rc1)"] +slack = ["prefect-slack (>=0.3.0rc1)"] +snowflake = ["prefect-snowflake (>=0.28.0rc1)"] +sqlalchemy = ["prefect-sqlalchemy (>=0.5.0rc1)"] + [[package]] name = "prometheus-client" -version = "0.20.0" +version = "0.21.0" description = "Python client for the Prometheus monitoring system." optional = false python-versions = ">=3.8" files = [ - {file = "prometheus_client-0.20.0-py3-none-any.whl", hash = "sha256:cde524a85bce83ca359cc837f28b8c0db5cac7aa653a588fd7e84ba061c329e7"}, - {file = "prometheus_client-0.20.0.tar.gz", hash = "sha256:287629d00b147a32dcb2be0b9df905da599b2d82f80377083ec8463309a4bb89"}, + {file = "prometheus_client-0.21.0-py3-none-any.whl", hash = "sha256:4fa6b4dd0ac16d58bb587c04b1caae65b8c5043e85f778f42f5f632f6af2e166"}, + {file = "prometheus_client-0.21.0.tar.gz", hash = "sha256:96c83c606b71ff2b0a433c98889d275f51ffec6c5e267de37c7a2b5c9aa9233e"}, ] [package.extras] @@ -3355,53 +3591,24 @@ wcwidth = "*" [[package]] name = "protobuf" -version = "4.25.4" +version = "4.25.5" description = "" optional = false python-versions = ">=3.8" files = [ - {file = "protobuf-4.25.4-cp310-abi3-win32.whl", hash = "sha256:db9fd45183e1a67722cafa5c1da3e85c6492a5383f127c86c4c4aa4845867dc4"}, - {file = "protobuf-4.25.4-cp310-abi3-win_amd64.whl", hash = "sha256:ba3d8504116a921af46499471c63a85260c1a5fc23333154a427a310e015d26d"}, - {file = "protobuf-4.25.4-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:eecd41bfc0e4b1bd3fa7909ed93dd14dd5567b98c941d6c1ad08fdcab3d6884b"}, - {file = "protobuf-4.25.4-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:4c8a70fdcb995dcf6c8966cfa3a29101916f7225e9afe3ced4395359955d3835"}, - {file = "protobuf-4.25.4-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:3319e073562e2515c6ddc643eb92ce20809f5d8f10fead3332f71c63be6a7040"}, - {file = "protobuf-4.25.4-cp38-cp38-win32.whl", hash = "sha256:7e372cbbda66a63ebca18f8ffaa6948455dfecc4e9c1029312f6c2edcd86c4e1"}, - {file = "protobuf-4.25.4-cp38-cp38-win_amd64.whl", hash = "sha256:051e97ce9fa6067a4546e75cb14f90cf0232dcb3e3d508c448b8d0e4265b61c1"}, - {file = "protobuf-4.25.4-cp39-cp39-win32.whl", hash = "sha256:90bf6fd378494eb698805bbbe7afe6c5d12c8e17fca817a646cd6a1818c696ca"}, - {file = "protobuf-4.25.4-cp39-cp39-win_amd64.whl", hash = "sha256:ac79a48d6b99dfed2729ccccee547b34a1d3d63289c71cef056653a846a2240f"}, - {file = "protobuf-4.25.4-py3-none-any.whl", hash = "sha256:bfbebc1c8e4793cfd58589acfb8a1026be0003e852b9da7db5a4285bde996978"}, - {file = "protobuf-4.25.4.tar.gz", hash = "sha256:0dc4a62cc4052a036ee2204d26fe4d835c62827c855c8a03f29fe6da146b380d"}, -] - -[[package]] -name = "psutil" -version = "6.0.0" -description = "Cross-platform lib for process and system monitoring in Python." -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" -files = [ - {file = "psutil-6.0.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a021da3e881cd935e64a3d0a20983bda0bb4cf80e4f74fa9bfcb1bc5785360c6"}, - {file = "psutil-6.0.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:1287c2b95f1c0a364d23bc6f2ea2365a8d4d9b726a3be7294296ff7ba97c17f0"}, - {file = "psutil-6.0.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:a9a3dbfb4de4f18174528d87cc352d1f788b7496991cca33c6996f40c9e3c92c"}, - {file = "psutil-6.0.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:6ec7588fb3ddaec7344a825afe298db83fe01bfaaab39155fa84cf1c0d6b13c3"}, - {file = "psutil-6.0.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:1e7c870afcb7d91fdea2b37c24aeb08f98b6d67257a5cb0a8bc3ac68d0f1a68c"}, - {file = "psutil-6.0.0-cp27-none-win32.whl", hash = "sha256:02b69001f44cc73c1c5279d02b30a817e339ceb258ad75997325e0e6169d8b35"}, - {file = "psutil-6.0.0-cp27-none-win_amd64.whl", hash = "sha256:21f1fb635deccd510f69f485b87433460a603919b45e2a324ad65b0cc74f8fb1"}, - {file = "psutil-6.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c588a7e9b1173b6e866756dde596fd4cad94f9399daf99ad8c3258b3cb2b47a0"}, - {file = "psutil-6.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ed2440ada7ef7d0d608f20ad89a04ec47d2d3ab7190896cd62ca5fc4fe08bf0"}, - {file = "psutil-6.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fd9a97c8e94059b0ef54a7d4baf13b405011176c3b6ff257c247cae0d560ecd"}, - {file = "psutil-6.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e8d0054fc88153ca0544f5c4d554d42e33df2e009c4ff42284ac9ebdef4132"}, - {file = "psutil-6.0.0-cp36-cp36m-win32.whl", hash = "sha256:fc8c9510cde0146432bbdb433322861ee8c3efbf8589865c8bf8d21cb30c4d14"}, - {file = "psutil-6.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:34859b8d8f423b86e4385ff3665d3f4d94be3cdf48221fbe476e883514fdb71c"}, - {file = "psutil-6.0.0-cp37-abi3-win32.whl", hash = "sha256:a495580d6bae27291324fe60cea0b5a7c23fa36a7cd35035a16d93bdcf076b9d"}, - {file = "psutil-6.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:33ea5e1c975250a720b3a6609c490db40dae5d83a4eb315170c4fe0d8b1f34b3"}, - {file = "psutil-6.0.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:ffe7fc9b6b36beadc8c322f84e1caff51e8703b88eee1da46d1e3a6ae11b4fd0"}, - {file = "psutil-6.0.0.tar.gz", hash = "sha256:8faae4f310b6d969fa26ca0545338b21f73c6b15db7c4a8d934a5482faa818f2"}, + {file = "protobuf-4.25.5-cp310-abi3-win32.whl", hash = "sha256:5e61fd921603f58d2f5acb2806a929b4675f8874ff5f330b7d6f7e2e784bbcd8"}, + {file = "protobuf-4.25.5-cp310-abi3-win_amd64.whl", hash = "sha256:4be0571adcbe712b282a330c6e89eae24281344429ae95c6d85e79e84780f5ea"}, + {file = "protobuf-4.25.5-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:b2fde3d805354df675ea4c7c6338c1aecd254dfc9925e88c6d31a2bcb97eb173"}, + {file = "protobuf-4.25.5-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:919ad92d9b0310070f8356c24b855c98df2b8bd207ebc1c0c6fcc9ab1e007f3d"}, + {file = "protobuf-4.25.5-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:fe14e16c22be926d3abfcb500e60cab068baf10b542b8c858fa27e098123e331"}, + {file = "protobuf-4.25.5-cp38-cp38-win32.whl", hash = "sha256:98d8d8aa50de6a2747efd9cceba361c9034050ecce3e09136f90de37ddba66e1"}, + {file = "protobuf-4.25.5-cp38-cp38-win_amd64.whl", hash = "sha256:b0234dd5a03049e4ddd94b93400b67803c823cfc405689688f59b34e0742381a"}, + {file = "protobuf-4.25.5-cp39-cp39-win32.whl", hash = "sha256:abe32aad8561aa7cc94fc7ba4fdef646e576983edb94a73381b03c53728a626f"}, + {file = "protobuf-4.25.5-cp39-cp39-win_amd64.whl", hash = "sha256:7a183f592dc80aa7c8da7ad9e55091c4ffc9497b3054452d629bb85fa27c2a45"}, + {file = "protobuf-4.25.5-py3-none-any.whl", hash = "sha256:0aebecb809cae990f8129ada5ca273d9d670b76d9bfc9b1809f0a9c02b7dbf41"}, + {file = "protobuf-4.25.5.tar.gz", hash = "sha256:7f8249476b4a9473645db7f8ab42b02fe1488cbe5fb72fddd445e0665afd8584"}, ] -[package.extras] -test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] - [[package]] name = "ptyprocess" version = "0.7.0" @@ -3607,6 +3814,28 @@ files = [ [package.dependencies] typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" +[[package]] +name = "pydantic-extra-types" +version = "2.9.0" +description = "Extra Pydantic types." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pydantic_extra_types-2.9.0-py3-none-any.whl", hash = "sha256:f0bb975508572ba7bf3390b7337807588463b7248587e69f43b1ad7c797530d0"}, + {file = "pydantic_extra_types-2.9.0.tar.gz", hash = "sha256:e061c01636188743bb69f368dcd391f327b8cfbfede2fe1cbb1211b06601ba3b"}, +] + +[package.dependencies] +pydantic = ">=2.5.2" + +[package.extras] +all = ["pendulum (>=3.0.0,<4.0.0)", "phonenumbers (>=8,<9)", "pycountry (>=23)", "python-ulid (>=1,<2)", "python-ulid (>=1,<3)", "pytz (>=2024.1)", "semver (>=3.0.2)", "tzdata (>=2024.1)"] +pendulum = ["pendulum (>=3.0.0,<4.0.0)"] +phonenumbers = ["phonenumbers (>=8,<9)"] +pycountry = ["pycountry (>=23)"] +python-ulid = ["python-ulid (>=1,<2)", "python-ulid (>=1,<3)"] +semver = ["semver (>=3.0.2)"] + [[package]] name = "pydantic-settings" version = "2.2.1" @@ -3688,13 +3917,13 @@ testutils = ["gitpython (>3)"] [[package]] name = "pyparsing" -version = "3.1.4" +version = "3.2.0" description = "pyparsing module - Classes and methods to define and execute parsing grammars" optional = false -python-versions = ">=3.6.8" +python-versions = ">=3.9" files = [ - {file = "pyparsing-3.1.4-py3-none-any.whl", hash = "sha256:a6a7ee4235a3f944aa1fa2249307708f893fe5717dc603503c6c7969c070fb7c"}, - {file = "pyparsing-3.1.4.tar.gz", hash = "sha256:f86ec8d1a83f11977c9a6ea7598e8c27fc5cddfa5b07ea2241edbbde1d7bc032"}, + {file = "pyparsing-3.2.0-py3-none-any.whl", hash = "sha256:93d9577b88da0bbea8cc8334ee8b918ed014968fd2ec383e868fb8afb1ccef84"}, + {file = "pyparsing-3.2.0.tar.gz", hash = "sha256:cbf74e27246d595d9a74b186b810f6fbb86726dbf3b9532efb343f6d7294fe9c"}, ] [package.extras] @@ -3815,6 +4044,24 @@ pytest = ">=4.6" [package.extras] testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] +[[package]] +name = "pytest-env" +version = "1.1.3" +description = "pytest plugin that allows you to add environment variables." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest_env-1.1.3-py3-none-any.whl", hash = "sha256:aada77e6d09fcfb04540a6e462c58533c37df35fa853da78707b17ec04d17dfc"}, + {file = "pytest_env-1.1.3.tar.gz", hash = "sha256:fcd7dc23bb71efd3d35632bde1bbe5ee8c8dc4489d6617fb010674880d96216b"}, +] + +[package.dependencies] +pytest = ">=7.4.3" +tomli = {version = ">=2.0.1", markers = "python_version < \"3.11\""} + +[package.extras] +test = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "pytest-mock (>=3.12)"] + [[package]] name = "pytest-httpx" version = "0.30.0" @@ -3833,6 +4080,20 @@ pytest = ">=7,<9" [package.extras] testing = ["pytest-asyncio (==0.23.*)", "pytest-cov (==4.*)"] +[[package]] +name = "pytest-timeout" +version = "2.3.1" +description = "pytest plugin to abort hanging tests" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pytest-timeout-2.3.1.tar.gz", hash = "sha256:12397729125c6ecbdaca01035b9e5239d4db97352320af155b3f5de1ba5165d9"}, + {file = "pytest_timeout-2.3.1-py3-none-any.whl", hash = "sha256:68188cb703edfc6a18fad98dc25a3c61e9f24d644b0b70f33af545219fc7813e"}, +] + +[package.dependencies] +pytest = ">=7.0.0" + [[package]] name = "pytest-xdist" version = "3.4.0" @@ -3895,15 +4156,32 @@ files = [ [package.extras] dev = ["atomicwrites (==1.4.1)", "attrs (==23.2.0)", "coverage (==7.4.1)", "hatch", "invoke (==2.2.0)", "more-itertools (==10.2.0)", "pbr (==6.0.0)", "pluggy (==1.4.0)", "py (==1.11.0)", "pytest (==8.0.0)", "pytest-cov (==4.1.0)", "pytest-timeout (==2.2.0)", "pyyaml (==6.0.1)", "ruff (==0.2.1)"] +[[package]] +name = "python-slugify" +version = "8.0.4" +description = "A Python slugify application that also handles Unicode" +optional = false +python-versions = ">=3.7" +files = [ + {file = "python-slugify-8.0.4.tar.gz", hash = "sha256:59202371d1d05b54a9e7720c5e038f928f45daaffe41dd10822f3907b937c856"}, + {file = "python_slugify-8.0.4-py2.py3-none-any.whl", hash = "sha256:276540b79961052b66b7d116620b36518847f52d5fd9e3a70164fc8c50faa6b8"}, +] + +[package.dependencies] +text-unidecode = ">=1.3" + +[package.extras] +unidecode = ["Unidecode (>=1.1.1)"] + [[package]] name = "pytz" -version = "2024.1" +version = "2024.2" description = "World timezone definitions, modern and historical" optional = false python-versions = "*" files = [ - {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, - {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, + {file = "pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725"}, + {file = "pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a"}, ] [[package]] @@ -3992,126 +4270,16 @@ files = [ ] [[package]] -name = "pyzmq" -version = "26.2.0" -description = "Python bindings for 0MQ" +name = "readchar" +version = "4.2.0" +description = "Library to easily read single chars and key strokes" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pyzmq-26.2.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:ddf33d97d2f52d89f6e6e7ae66ee35a4d9ca6f36eda89c24591b0c40205a3629"}, - {file = "pyzmq-26.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dacd995031a01d16eec825bf30802fceb2c3791ef24bcce48fa98ce40918c27b"}, - {file = "pyzmq-26.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89289a5ee32ef6c439086184529ae060c741334b8970a6855ec0b6ad3ff28764"}, - {file = "pyzmq-26.2.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5506f06d7dc6ecf1efacb4a013b1f05071bb24b76350832c96449f4a2d95091c"}, - {file = "pyzmq-26.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ea039387c10202ce304af74def5021e9adc6297067f3441d348d2b633e8166a"}, - {file = "pyzmq-26.2.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a2224fa4a4c2ee872886ed00a571f5e967c85e078e8e8c2530a2fb01b3309b88"}, - {file = "pyzmq-26.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:28ad5233e9c3b52d76196c696e362508959741e1a005fb8fa03b51aea156088f"}, - {file = "pyzmq-26.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:1c17211bc037c7d88e85ed8b7d8f7e52db6dc8eca5590d162717c654550f7282"}, - {file = "pyzmq-26.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b8f86dd868d41bea9a5f873ee13bf5551c94cf6bc51baebc6f85075971fe6eea"}, - {file = "pyzmq-26.2.0-cp310-cp310-win32.whl", hash = "sha256:46a446c212e58456b23af260f3d9fb785054f3e3653dbf7279d8f2b5546b21c2"}, - {file = "pyzmq-26.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:49d34ab71db5a9c292a7644ce74190b1dd5a3475612eefb1f8be1d6961441971"}, - {file = "pyzmq-26.2.0-cp310-cp310-win_arm64.whl", hash = "sha256:bfa832bfa540e5b5c27dcf5de5d82ebc431b82c453a43d141afb1e5d2de025fa"}, - {file = "pyzmq-26.2.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:8f7e66c7113c684c2b3f1c83cdd3376103ee0ce4c49ff80a648643e57fb22218"}, - {file = "pyzmq-26.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3a495b30fc91db2db25120df5847d9833af237546fd59170701acd816ccc01c4"}, - {file = "pyzmq-26.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77eb0968da535cba0470a5165468b2cac7772cfb569977cff92e240f57e31bef"}, - {file = "pyzmq-26.2.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ace4f71f1900a548f48407fc9be59c6ba9d9aaf658c2eea6cf2779e72f9f317"}, - {file = "pyzmq-26.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:92a78853d7280bffb93df0a4a6a2498cba10ee793cc8076ef797ef2f74d107cf"}, - {file = "pyzmq-26.2.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:689c5d781014956a4a6de61d74ba97b23547e431e9e7d64f27d4922ba96e9d6e"}, - {file = "pyzmq-26.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0aca98bc423eb7d153214b2df397c6421ba6373d3397b26c057af3c904452e37"}, - {file = "pyzmq-26.2.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1f3496d76b89d9429a656293744ceca4d2ac2a10ae59b84c1da9b5165f429ad3"}, - {file = "pyzmq-26.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5c2b3bfd4b9689919db068ac6c9911f3fcb231c39f7dd30e3138be94896d18e6"}, - {file = "pyzmq-26.2.0-cp311-cp311-win32.whl", hash = "sha256:eac5174677da084abf378739dbf4ad245661635f1600edd1221f150b165343f4"}, - {file = "pyzmq-26.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:5a509df7d0a83a4b178d0f937ef14286659225ef4e8812e05580776c70e155d5"}, - {file = "pyzmq-26.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:c0e6091b157d48cbe37bd67233318dbb53e1e6327d6fc3bb284afd585d141003"}, - {file = "pyzmq-26.2.0-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:ded0fc7d90fe93ae0b18059930086c51e640cdd3baebdc783a695c77f123dcd9"}, - {file = "pyzmq-26.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:17bf5a931c7f6618023cdacc7081f3f266aecb68ca692adac015c383a134ca52"}, - {file = "pyzmq-26.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55cf66647e49d4621a7e20c8d13511ef1fe1efbbccf670811864452487007e08"}, - {file = "pyzmq-26.2.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4661c88db4a9e0f958c8abc2b97472e23061f0bc737f6f6179d7a27024e1faa5"}, - {file = "pyzmq-26.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea7f69de383cb47522c9c208aec6dd17697db7875a4674c4af3f8cfdac0bdeae"}, - {file = "pyzmq-26.2.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:7f98f6dfa8b8ccaf39163ce872bddacca38f6a67289116c8937a02e30bbe9711"}, - {file = "pyzmq-26.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e3e0210287329272539eea617830a6a28161fbbd8a3271bf4150ae3e58c5d0e6"}, - {file = "pyzmq-26.2.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6b274e0762c33c7471f1a7471d1a2085b1a35eba5cdc48d2ae319f28b6fc4de3"}, - {file = "pyzmq-26.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:29c6a4635eef69d68a00321e12a7d2559fe2dfccfa8efae3ffb8e91cd0b36a8b"}, - {file = "pyzmq-26.2.0-cp312-cp312-win32.whl", hash = "sha256:989d842dc06dc59feea09e58c74ca3e1678c812a4a8a2a419046d711031f69c7"}, - {file = "pyzmq-26.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:2a50625acdc7801bc6f74698c5c583a491c61d73c6b7ea4dee3901bb99adb27a"}, - {file = "pyzmq-26.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:4d29ab8592b6ad12ebbf92ac2ed2bedcfd1cec192d8e559e2e099f648570e19b"}, - {file = "pyzmq-26.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9dd8cd1aeb00775f527ec60022004d030ddc51d783d056e3e23e74e623e33726"}, - {file = "pyzmq-26.2.0-cp313-cp313-macosx_10_15_universal2.whl", hash = "sha256:28c812d9757fe8acecc910c9ac9dafd2ce968c00f9e619db09e9f8f54c3a68a3"}, - {file = "pyzmq-26.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d80b1dd99c1942f74ed608ddb38b181b87476c6a966a88a950c7dee118fdf50"}, - {file = "pyzmq-26.2.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c997098cc65e3208eca09303630e84d42718620e83b733d0fd69543a9cab9cb"}, - {file = "pyzmq-26.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ad1bc8d1b7a18497dda9600b12dc193c577beb391beae5cd2349184db40f187"}, - {file = "pyzmq-26.2.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:bea2acdd8ea4275e1278350ced63da0b166421928276c7c8e3f9729d7402a57b"}, - {file = "pyzmq-26.2.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:23f4aad749d13698f3f7b64aad34f5fc02d6f20f05999eebc96b89b01262fb18"}, - {file = "pyzmq-26.2.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:a4f96f0d88accc3dbe4a9025f785ba830f968e21e3e2c6321ccdfc9aef755115"}, - {file = "pyzmq-26.2.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ced65e5a985398827cc9276b93ef6dfabe0273c23de8c7931339d7e141c2818e"}, - {file = "pyzmq-26.2.0-cp313-cp313-win32.whl", hash = "sha256:31507f7b47cc1ead1f6e86927f8ebb196a0bab043f6345ce070f412a59bf87b5"}, - {file = "pyzmq-26.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:70fc7fcf0410d16ebdda9b26cbd8bf8d803d220a7f3522e060a69a9c87bf7bad"}, - {file = "pyzmq-26.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:c3789bd5768ab5618ebf09cef6ec2b35fed88709b104351748a63045f0ff9797"}, - {file = "pyzmq-26.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:034da5fc55d9f8da09015d368f519478a52675e558c989bfcb5cf6d4e16a7d2a"}, - {file = "pyzmq-26.2.0-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:c92d73464b886931308ccc45b2744e5968cbaade0b1d6aeb40d8ab537765f5bc"}, - {file = "pyzmq-26.2.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:794a4562dcb374f7dbbfb3f51d28fb40123b5a2abadee7b4091f93054909add5"}, - {file = "pyzmq-26.2.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aee22939bb6075e7afededabad1a56a905da0b3c4e3e0c45e75810ebe3a52672"}, - {file = "pyzmq-26.2.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ae90ff9dad33a1cfe947d2c40cb9cb5e600d759ac4f0fd22616ce6540f72797"}, - {file = "pyzmq-26.2.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:43a47408ac52647dfabbc66a25b05b6a61700b5165807e3fbd40063fcaf46386"}, - {file = "pyzmq-26.2.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:25bf2374a2a8433633c65ccb9553350d5e17e60c8eb4de4d92cc6bd60f01d306"}, - {file = "pyzmq-26.2.0-cp313-cp313t-musllinux_1_1_i686.whl", hash = "sha256:007137c9ac9ad5ea21e6ad97d3489af654381324d5d3ba614c323f60dab8fae6"}, - {file = "pyzmq-26.2.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:470d4a4f6d48fb34e92d768b4e8a5cc3780db0d69107abf1cd7ff734b9766eb0"}, - {file = "pyzmq-26.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3b55a4229ce5da9497dd0452b914556ae58e96a4381bb6f59f1305dfd7e53fc8"}, - {file = "pyzmq-26.2.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9cb3a6460cdea8fe8194a76de8895707e61ded10ad0be97188cc8463ffa7e3a8"}, - {file = "pyzmq-26.2.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8ab5cad923cc95c87bffee098a27856c859bd5d0af31bd346035aa816b081fe1"}, - {file = "pyzmq-26.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9ed69074a610fad1c2fda66180e7b2edd4d31c53f2d1872bc2d1211563904cd9"}, - {file = "pyzmq-26.2.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:cccba051221b916a4f5e538997c45d7d136a5646442b1231b916d0164067ea27"}, - {file = "pyzmq-26.2.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:0eaa83fc4c1e271c24eaf8fb083cbccef8fde77ec8cd45f3c35a9a123e6da097"}, - {file = "pyzmq-26.2.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:9edda2df81daa129b25a39b86cb57dfdfe16f7ec15b42b19bfac503360d27a93"}, - {file = "pyzmq-26.2.0-cp37-cp37m-win32.whl", hash = "sha256:ea0eb6af8a17fa272f7b98d7bebfab7836a0d62738e16ba380f440fceca2d951"}, - {file = "pyzmq-26.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:4ff9dc6bc1664bb9eec25cd17506ef6672d506115095411e237d571e92a58231"}, - {file = "pyzmq-26.2.0-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:2eb7735ee73ca1b0d71e0e67c3739c689067f055c764f73aac4cc8ecf958ee3f"}, - {file = "pyzmq-26.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1a534f43bc738181aa7cbbaf48e3eca62c76453a40a746ab95d4b27b1111a7d2"}, - {file = "pyzmq-26.2.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:aedd5dd8692635813368e558a05266b995d3d020b23e49581ddd5bbe197a8ab6"}, - {file = "pyzmq-26.2.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8be4700cd8bb02cc454f630dcdf7cfa99de96788b80c51b60fe2fe1dac480289"}, - {file = "pyzmq-26.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fcc03fa4997c447dce58264e93b5aa2d57714fbe0f06c07b7785ae131512732"}, - {file = "pyzmq-26.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:402b190912935d3db15b03e8f7485812db350d271b284ded2b80d2e5704be780"}, - {file = "pyzmq-26.2.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8685fa9c25ff00f550c1fec650430c4b71e4e48e8d852f7ddcf2e48308038640"}, - {file = "pyzmq-26.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:76589c020680778f06b7e0b193f4b6dd66d470234a16e1df90329f5e14a171cd"}, - {file = "pyzmq-26.2.0-cp38-cp38-win32.whl", hash = "sha256:8423c1877d72c041f2c263b1ec6e34360448decfb323fa8b94e85883043ef988"}, - {file = "pyzmq-26.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:76589f2cd6b77b5bdea4fca5992dc1c23389d68b18ccc26a53680ba2dc80ff2f"}, - {file = "pyzmq-26.2.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:b1d464cb8d72bfc1a3adc53305a63a8e0cac6bc8c5a07e8ca190ab8d3faa43c2"}, - {file = "pyzmq-26.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4da04c48873a6abdd71811c5e163bd656ee1b957971db7f35140a2d573f6949c"}, - {file = "pyzmq-26.2.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d049df610ac811dcffdc147153b414147428567fbbc8be43bb8885f04db39d98"}, - {file = "pyzmq-26.2.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:05590cdbc6b902101d0e65d6a4780af14dc22914cc6ab995d99b85af45362cc9"}, - {file = "pyzmq-26.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c811cfcd6a9bf680236c40c6f617187515269ab2912f3d7e8c0174898e2519db"}, - {file = "pyzmq-26.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6835dd60355593de10350394242b5757fbbd88b25287314316f266e24c61d073"}, - {file = "pyzmq-26.2.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc6bee759a6bddea5db78d7dcd609397449cb2d2d6587f48f3ca613b19410cfc"}, - {file = "pyzmq-26.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c530e1eecd036ecc83c3407f77bb86feb79916d4a33d11394b8234f3bd35b940"}, - {file = "pyzmq-26.2.0-cp39-cp39-win32.whl", hash = "sha256:367b4f689786fca726ef7a6c5ba606958b145b9340a5e4808132cc65759abd44"}, - {file = "pyzmq-26.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:e6fa2e3e683f34aea77de8112f6483803c96a44fd726d7358b9888ae5bb394ec"}, - {file = "pyzmq-26.2.0-cp39-cp39-win_arm64.whl", hash = "sha256:7445be39143a8aa4faec43b076e06944b8f9d0701b669df4af200531b21e40bb"}, - {file = "pyzmq-26.2.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:706e794564bec25819d21a41c31d4df2d48e1cc4b061e8d345d7fb4dd3e94072"}, - {file = "pyzmq-26.2.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b435f2753621cd36e7c1762156815e21c985c72b19135dac43a7f4f31d28dd1"}, - {file = "pyzmq-26.2.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:160c7e0a5eb178011e72892f99f918c04a131f36056d10d9c1afb223fc952c2d"}, - {file = "pyzmq-26.2.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c4a71d5d6e7b28a47a394c0471b7e77a0661e2d651e7ae91e0cab0a587859ca"}, - {file = "pyzmq-26.2.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:90412f2db8c02a3864cbfc67db0e3dcdbda336acf1c469526d3e869394fe001c"}, - {file = "pyzmq-26.2.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2ea4ad4e6a12e454de05f2949d4beddb52460f3de7c8b9d5c46fbb7d7222e02c"}, - {file = "pyzmq-26.2.0-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:fc4f7a173a5609631bb0c42c23d12c49df3966f89f496a51d3eb0ec81f4519d6"}, - {file = "pyzmq-26.2.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:878206a45202247781472a2d99df12a176fef806ca175799e1c6ad263510d57c"}, - {file = "pyzmq-26.2.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:17c412bad2eb9468e876f556eb4ee910e62d721d2c7a53c7fa31e643d35352e6"}, - {file = "pyzmq-26.2.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:0d987a3ae5a71c6226b203cfd298720e0086c7fe7c74f35fa8edddfbd6597eed"}, - {file = "pyzmq-26.2.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:39887ac397ff35b7b775db7201095fc6310a35fdbae85bac4523f7eb3b840e20"}, - {file = "pyzmq-26.2.0-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:fdb5b3e311d4d4b0eb8b3e8b4d1b0a512713ad7e6a68791d0923d1aec433d919"}, - {file = "pyzmq-26.2.0-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:226af7dcb51fdb0109f0016449b357e182ea0ceb6b47dfb5999d569e5db161d5"}, - {file = "pyzmq-26.2.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bed0e799e6120b9c32756203fb9dfe8ca2fb8467fed830c34c877e25638c3fc"}, - {file = "pyzmq-26.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:29c7947c594e105cb9e6c466bace8532dc1ca02d498684128b339799f5248277"}, - {file = "pyzmq-26.2.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:cdeabcff45d1c219636ee2e54d852262e5c2e085d6cb476d938aee8d921356b3"}, - {file = "pyzmq-26.2.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35cffef589bcdc587d06f9149f8d5e9e8859920a071df5a2671de2213bef592a"}, - {file = "pyzmq-26.2.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18c8dc3b7468d8b4bdf60ce9d7141897da103c7a4690157b32b60acb45e333e6"}, - {file = "pyzmq-26.2.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7133d0a1677aec369d67dd78520d3fa96dd7f3dcec99d66c1762870e5ea1a50a"}, - {file = "pyzmq-26.2.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6a96179a24b14fa6428cbfc08641c779a53f8fcec43644030328f44034c7f1f4"}, - {file = "pyzmq-26.2.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4f78c88905461a9203eac9faac157a2a0dbba84a0fd09fd29315db27be40af9f"}, - {file = "pyzmq-26.2.0.tar.gz", hash = "sha256:070672c258581c8e4f640b5159297580a9974b026043bd4ab0470be9ed324f1f"}, + {file = "readchar-4.2.0-py3-none-any.whl", hash = "sha256:2a587a27c981e6d25a518730ad4c88c429c315439baa6fda55d7a8b3ac4cb62a"}, + {file = "readchar-4.2.0.tar.gz", hash = "sha256:44807cbbe377b72079fea6cba8aa91c809982d7d727b2f0dbb2d1a8084914faa"}, ] -[package.dependencies] -cffi = {version = "*", markers = "implementation_name == \"pypy\""} - [[package]] name = "redis" version = "5.0.8" @@ -4131,6 +4299,124 @@ hiredis = {version = ">1.0.0", optional = true, markers = "extra == \"hiredis\"" hiredis = ["hiredis (>1.0.0)"] ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)"] +[[package]] +name = "referencing" +version = "0.35.1" +description = "JSON Referencing + Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "referencing-0.35.1-py3-none-any.whl", hash = "sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de"}, + {file = "referencing-0.35.1.tar.gz", hash = "sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c"}, +] + +[package.dependencies] +attrs = ">=22.2.0" +rpds-py = ">=0.7.0" + +[[package]] +name = "regex" +version = "2024.9.11" +description = "Alternative regular expression module, to replace re." +optional = false +python-versions = ">=3.8" +files = [ + {file = "regex-2024.9.11-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1494fa8725c285a81d01dc8c06b55287a1ee5e0e382d8413adc0a9197aac6408"}, + {file = "regex-2024.9.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0e12c481ad92d129c78f13a2a3662317e46ee7ef96c94fd332e1c29131875b7d"}, + {file = "regex-2024.9.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:16e13a7929791ac1216afde26f712802e3df7bf0360b32e4914dca3ab8baeea5"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46989629904bad940bbec2106528140a218b4a36bb3042d8406980be1941429c"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a906ed5e47a0ce5f04b2c981af1c9acf9e8696066900bf03b9d7879a6f679fc8"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e9a091b0550b3b0207784a7d6d0f1a00d1d1c8a11699c1a4d93db3fbefc3ad35"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ddcd9a179c0a6fa8add279a4444015acddcd7f232a49071ae57fa6e278f1f71"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6b41e1adc61fa347662b09398e31ad446afadff932a24807d3ceb955ed865cc8"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ced479f601cd2f8ca1fd7b23925a7e0ad512a56d6e9476f79b8f381d9d37090a"}, + {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:635a1d96665f84b292e401c3d62775851aedc31d4f8784117b3c68c4fcd4118d"}, + {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c0256beda696edcf7d97ef16b2a33a8e5a875affd6fa6567b54f7c577b30a137"}, + {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:3ce4f1185db3fbde8ed8aa223fc9620f276c58de8b0d4f8cc86fd1360829edb6"}, + {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:09d77559e80dcc9d24570da3745ab859a9cf91953062e4ab126ba9d5993688ca"}, + {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7a22ccefd4db3f12b526eccb129390942fe874a3a9fdbdd24cf55773a1faab1a"}, + {file = "regex-2024.9.11-cp310-cp310-win32.whl", hash = "sha256:f745ec09bc1b0bd15cfc73df6fa4f726dcc26bb16c23a03f9e3367d357eeedd0"}, + {file = "regex-2024.9.11-cp310-cp310-win_amd64.whl", hash = "sha256:01c2acb51f8a7d6494c8c5eafe3d8e06d76563d8a8a4643b37e9b2dd8a2ff623"}, + {file = "regex-2024.9.11-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2cce2449e5927a0bf084d346da6cd5eb016b2beca10d0013ab50e3c226ffc0df"}, + {file = "regex-2024.9.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3b37fa423beefa44919e009745ccbf353d8c981516e807995b2bd11c2c77d268"}, + {file = "regex-2024.9.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:64ce2799bd75039b480cc0360907c4fb2f50022f030bf9e7a8705b636e408fad"}, + {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a4cc92bb6db56ab0c1cbd17294e14f5e9224f0cc6521167ef388332604e92679"}, + {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d05ac6fa06959c4172eccd99a222e1fbf17b5670c4d596cb1e5cde99600674c4"}, + {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:040562757795eeea356394a7fb13076ad4f99d3c62ab0f8bdfb21f99a1f85664"}, + {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6113c008a7780792efc80f9dfe10ba0cd043cbf8dc9a76ef757850f51b4edc50"}, + {file = "regex-2024.9.11-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e5fb5f77c8745a60105403a774fe2c1759b71d3e7b4ca237a5e67ad066c7199"}, + {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:54d9ff35d4515debf14bc27f1e3b38bfc453eff3220f5bce159642fa762fe5d4"}, + {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:df5cbb1fbc74a8305b6065d4ade43b993be03dbe0f8b30032cced0d7740994bd"}, + {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:7fb89ee5d106e4a7a51bce305ac4efb981536301895f7bdcf93ec92ae0d91c7f"}, + {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a738b937d512b30bf75995c0159c0ddf9eec0775c9d72ac0202076c72f24aa96"}, + {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e28f9faeb14b6f23ac55bfbbfd3643f5c7c18ede093977f1df249f73fd22c7b1"}, + {file = "regex-2024.9.11-cp311-cp311-win32.whl", hash = "sha256:18e707ce6c92d7282dfce370cd205098384b8ee21544e7cb29b8aab955b66fa9"}, + {file = "regex-2024.9.11-cp311-cp311-win_amd64.whl", hash = "sha256:313ea15e5ff2a8cbbad96ccef6be638393041b0a7863183c2d31e0c6116688cf"}, + {file = "regex-2024.9.11-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b0d0a6c64fcc4ef9c69bd5b3b3626cc3776520a1637d8abaa62b9edc147a58f7"}, + {file = "regex-2024.9.11-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:49b0e06786ea663f933f3710a51e9385ce0cba0ea56b67107fd841a55d56a231"}, + {file = "regex-2024.9.11-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5b513b6997a0b2f10e4fd3a1313568e373926e8c252bd76c960f96fd039cd28d"}, + {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee439691d8c23e76f9802c42a95cfeebf9d47cf4ffd06f18489122dbb0a7ad64"}, + {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a8f877c89719d759e52783f7fe6e1c67121076b87b40542966c02de5503ace42"}, + {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:23b30c62d0f16827f2ae9f2bb87619bc4fba2044911e2e6c2eb1af0161cdb766"}, + {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85ab7824093d8f10d44330fe1e6493f756f252d145323dd17ab6b48733ff6c0a"}, + {file = "regex-2024.9.11-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8dee5b4810a89447151999428fe096977346cf2f29f4d5e29609d2e19e0199c9"}, + {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:98eeee2f2e63edae2181c886d7911ce502e1292794f4c5ee71e60e23e8d26b5d"}, + {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:57fdd2e0b2694ce6fc2e5ccf189789c3e2962916fb38779d3e3521ff8fe7a822"}, + {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d552c78411f60b1fdaafd117a1fca2f02e562e309223b9d44b7de8be451ec5e0"}, + {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a0b2b80321c2ed3fcf0385ec9e51a12253c50f146fddb2abbb10f033fe3d049a"}, + {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:18406efb2f5a0e57e3a5881cd9354c1512d3bb4f5c45d96d110a66114d84d23a"}, + {file = "regex-2024.9.11-cp312-cp312-win32.whl", hash = "sha256:e464b467f1588e2c42d26814231edecbcfe77f5ac414d92cbf4e7b55b2c2a776"}, + {file = "regex-2024.9.11-cp312-cp312-win_amd64.whl", hash = "sha256:9e8719792ca63c6b8340380352c24dcb8cd7ec49dae36e963742a275dfae6009"}, + {file = "regex-2024.9.11-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c157bb447303070f256e084668b702073db99bbb61d44f85d811025fcf38f784"}, + {file = "regex-2024.9.11-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4db21ece84dfeefc5d8a3863f101995de646c6cb0536952c321a2650aa202c36"}, + {file = "regex-2024.9.11-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:220e92a30b426daf23bb67a7962900ed4613589bab80382be09b48896d211e92"}, + {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb1ae19e64c14c7ec1995f40bd932448713d3c73509e82d8cd7744dc00e29e86"}, + {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f47cd43a5bfa48f86925fe26fbdd0a488ff15b62468abb5d2a1e092a4fb10e85"}, + {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9d4a76b96f398697fe01117093613166e6aa8195d63f1b4ec3f21ab637632963"}, + {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ea51dcc0835eea2ea31d66456210a4e01a076d820e9039b04ae8d17ac11dee6"}, + {file = "regex-2024.9.11-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7aaa315101c6567a9a45d2839322c51c8d6e81f67683d529512f5bcfb99c802"}, + {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c57d08ad67aba97af57a7263c2d9006d5c404d721c5f7542f077f109ec2a4a29"}, + {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f8404bf61298bb6f8224bb9176c1424548ee1181130818fcd2cbffddc768bed8"}, + {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:dd4490a33eb909ef5078ab20f5f000087afa2a4daa27b4c072ccb3cb3050ad84"}, + {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:eee9130eaad130649fd73e5cd92f60e55708952260ede70da64de420cdcad554"}, + {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6a2644a93da36c784e546de579ec1806bfd2763ef47babc1b03d765fe560c9f8"}, + {file = "regex-2024.9.11-cp313-cp313-win32.whl", hash = "sha256:e997fd30430c57138adc06bba4c7c2968fb13d101e57dd5bb9355bf8ce3fa7e8"}, + {file = "regex-2024.9.11-cp313-cp313-win_amd64.whl", hash = "sha256:042c55879cfeb21a8adacc84ea347721d3d83a159da6acdf1116859e2427c43f"}, + {file = "regex-2024.9.11-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:35f4a6f96aa6cb3f2f7247027b07b15a374f0d5b912c0001418d1d55024d5cb4"}, + {file = "regex-2024.9.11-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:55b96e7ce3a69a8449a66984c268062fbaa0d8ae437b285428e12797baefce7e"}, + {file = "regex-2024.9.11-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cb130fccd1a37ed894824b8c046321540263013da72745d755f2d35114b81a60"}, + {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:323c1f04be6b2968944d730e5c2091c8c89767903ecaa135203eec4565ed2b2b"}, + {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be1c8ed48c4c4065ecb19d882a0ce1afe0745dfad8ce48c49586b90a55f02366"}, + {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b5b029322e6e7b94fff16cd120ab35a253236a5f99a79fb04fda7ae71ca20ae8"}, + {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6fff13ef6b5f29221d6904aa816c34701462956aa72a77f1f151a8ec4f56aeb"}, + {file = "regex-2024.9.11-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:587d4af3979376652010e400accc30404e6c16b7df574048ab1f581af82065e4"}, + {file = "regex-2024.9.11-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:079400a8269544b955ffa9e31f186f01d96829110a3bf79dc338e9910f794fca"}, + {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:f9268774428ec173654985ce55fc6caf4c6d11ade0f6f914d48ef4719eb05ebb"}, + {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:23f9985c8784e544d53fc2930fc1ac1a7319f5d5332d228437acc9f418f2f168"}, + {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:ae2941333154baff9838e88aa71c1d84f4438189ecc6021a12c7573728b5838e"}, + {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:e93f1c331ca8e86fe877a48ad64e77882c0c4da0097f2212873a69bbfea95d0c"}, + {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:846bc79ee753acf93aef4184c040d709940c9d001029ceb7b7a52747b80ed2dd"}, + {file = "regex-2024.9.11-cp38-cp38-win32.whl", hash = "sha256:c94bb0a9f1db10a1d16c00880bdebd5f9faf267273b8f5bd1878126e0fbde771"}, + {file = "regex-2024.9.11-cp38-cp38-win_amd64.whl", hash = "sha256:2b08fce89fbd45664d3df6ad93e554b6c16933ffa9d55cb7e01182baaf971508"}, + {file = "regex-2024.9.11-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:07f45f287469039ffc2c53caf6803cd506eb5f5f637f1d4acb37a738f71dd066"}, + {file = "regex-2024.9.11-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4838e24ee015101d9f901988001038f7f0d90dc0c3b115541a1365fb439add62"}, + {file = "regex-2024.9.11-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6edd623bae6a737f10ce853ea076f56f507fd7726bee96a41ee3d68d347e4d16"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c69ada171c2d0e97a4b5aa78fbb835e0ffbb6b13fc5da968c09811346564f0d3"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:02087ea0a03b4af1ed6ebab2c54d7118127fee8d71b26398e8e4b05b78963199"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:69dee6a020693d12a3cf892aba4808fe168d2a4cef368eb9bf74f5398bfd4ee8"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:297f54910247508e6e5cae669f2bc308985c60540a4edd1c77203ef19bfa63ca"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ecea58b43a67b1b79805f1a0255730edaf5191ecef84dbc4cc85eb30bc8b63b9"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:eab4bb380f15e189d1313195b062a6aa908f5bd687a0ceccd47c8211e9cf0d4a"}, + {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0cbff728659ce4bbf4c30b2a1be040faafaa9eca6ecde40aaff86f7889f4ab39"}, + {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:54c4a097b8bc5bb0dfc83ae498061d53ad7b5762e00f4adaa23bee22b012e6ba"}, + {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:73d6d2f64f4d894c96626a75578b0bf7d9e56dcda8c3d037a2118fdfe9b1c664"}, + {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:e53b5fbab5d675aec9f0c501274c467c0f9a5d23696cfc94247e1fb56501ed89"}, + {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0ffbcf9221e04502fc35e54d1ce9567541979c3fdfb93d2c554f0ca583a19b35"}, + {file = "regex-2024.9.11-cp39-cp39-win32.whl", hash = "sha256:e4c22e1ac1f1ec1e09f72e6c44d8f2244173db7eb9629cc3a346a8d7ccc31142"}, + {file = "regex-2024.9.11-cp39-cp39-win_amd64.whl", hash = "sha256:faa3c142464efec496967359ca99696c896c591c56c53506bac1ad465f66e919"}, + {file = "regex-2024.9.11.tar.gz", hash = "sha256:6c188c307e8433bcb63dc1915022deb553b4203a70722fc542c363bf120a01fd"}, +] + [[package]] name = "requests" version = "2.32.3" @@ -4152,15 +4438,47 @@ urllib3 = ">=1.21.1,<3" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] +[[package]] +name = "requests-oauthlib" +version = "2.0.0" +description = "OAuthlib authentication support for Requests." +optional = false +python-versions = ">=3.4" +files = [ + {file = "requests-oauthlib-2.0.0.tar.gz", hash = "sha256:b3dffaebd884d8cd778494369603a9e7b58d29111bf6b41bdc2dcd87203af4e9"}, + {file = "requests_oauthlib-2.0.0-py2.py3-none-any.whl", hash = "sha256:7dd8a5c40426b779b0868c404bdef9768deccf22749cde15852df527e6269b36"}, +] + +[package.dependencies] +oauthlib = ">=3.0.0" +requests = ">=2.0.0" + +[package.extras] +rsa = ["oauthlib[signedtoken] (>=3.0.0)"] + +[[package]] +name = "rfc3339-validator" +version = "0.1.4" +description = "A pure python RFC3339 validator" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "rfc3339_validator-0.1.4-py2.py3-none-any.whl", hash = "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa"}, + {file = "rfc3339_validator-0.1.4.tar.gz", hash = "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b"}, +] + +[package.dependencies] +six = "*" + [[package]] name = "rich" -version = "13.8.0" +version = "13.8.1" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.7.0" files = [ - {file = "rich-13.8.0-py3-none-any.whl", hash = "sha256:2e85306a063b9492dffc86278197a60cbece75bcb766022f3436f567cae11bdc"}, - {file = "rich-13.8.0.tar.gz", hash = "sha256:a5ac1f1cd448ade0d59cc3356f7db7a7ccda2c8cbae9c7a90c28ff463d3e91f4"}, + {file = "rich-13.8.1-py3-none-any.whl", hash = "sha256:1760a3c0848469b97b558fc61c85233e3dafb69c7a071b4d60c38099d3cd4c06"}, + {file = "rich-13.8.1.tar.gz", hash = "sha256:8260cda28e3db6bf04d2d1ef4dbc03ba80a824c88b0e7668a0f23126a424844a"}, ] [package.dependencies] @@ -4170,31 +4488,220 @@ pygments = ">=2.13.0,<3.0.0" [package.extras] jupyter = ["ipywidgets (>=7.5.1,<9)"] +[[package]] +name = "rpds-py" +version = "0.20.0" +description = "Python bindings to Rust's persistent data structures (rpds)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "rpds_py-0.20.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3ad0fda1635f8439cde85c700f964b23ed5fc2d28016b32b9ee5fe30da5c84e2"}, + {file = "rpds_py-0.20.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9bb4a0d90fdb03437c109a17eade42dfbf6190408f29b2744114d11586611d6f"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6377e647bbfd0a0b159fe557f2c6c602c159fc752fa316572f012fc0bf67150"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb851b7df9dda52dc1415ebee12362047ce771fc36914586b2e9fcbd7d293b3e"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e0f80b739e5a8f54837be5d5c924483996b603d5502bfff79bf33da06164ee2"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a8c94dad2e45324fc74dce25e1645d4d14df9a4e54a30fa0ae8bad9a63928e3"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8e604fe73ba048c06085beaf51147eaec7df856824bfe7b98657cf436623daf"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:df3de6b7726b52966edf29663e57306b23ef775faf0ac01a3e9f4012a24a4140"}, + {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf258ede5bc22a45c8e726b29835b9303c285ab46fc7c3a4cc770736b5304c9f"}, + {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:55fea87029cded5df854ca7e192ec7bdb7ecd1d9a3f63d5c4eb09148acf4a7ce"}, + {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ae94bd0b2f02c28e199e9bc51485d0c5601f58780636185660f86bf80c89af94"}, + {file = "rpds_py-0.20.0-cp310-none-win32.whl", hash = "sha256:28527c685f237c05445efec62426d285e47a58fb05ba0090a4340b73ecda6dee"}, + {file = "rpds_py-0.20.0-cp310-none-win_amd64.whl", hash = "sha256:238a2d5b1cad28cdc6ed15faf93a998336eb041c4e440dd7f902528b8891b399"}, + {file = "rpds_py-0.20.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ac2f4f7a98934c2ed6505aead07b979e6f999389f16b714448fb39bbaa86a489"}, + {file = "rpds_py-0.20.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:220002c1b846db9afd83371d08d239fdc865e8f8c5795bbaec20916a76db3318"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d7919548df3f25374a1f5d01fbcd38dacab338ef5f33e044744b5c36729c8db"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:758406267907b3781beee0f0edfe4a179fbd97c0be2e9b1154d7f0a1279cf8e5"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3d61339e9f84a3f0767b1995adfb171a0d00a1185192718a17af6e124728e0f5"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1259c7b3705ac0a0bd38197565a5d603218591d3f6cee6e614e380b6ba61c6f6"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c1dc0f53856b9cc9a0ccca0a7cc61d3d20a7088201c0937f3f4048c1718a209"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7e60cb630f674a31f0368ed32b2a6b4331b8350d67de53c0359992444b116dd3"}, + {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dbe982f38565bb50cb7fb061ebf762c2f254ca3d8c20d4006878766e84266272"}, + {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:514b3293b64187172bc77c8fb0cdae26981618021053b30d8371c3a902d4d5ad"}, + {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d0a26ffe9d4dd35e4dfdd1e71f46401cff0181c75ac174711ccff0459135fa58"}, + {file = "rpds_py-0.20.0-cp311-none-win32.whl", hash = "sha256:89c19a494bf3ad08c1da49445cc5d13d8fefc265f48ee7e7556839acdacf69d0"}, + {file = "rpds_py-0.20.0-cp311-none-win_amd64.whl", hash = "sha256:c638144ce971df84650d3ed0096e2ae7af8e62ecbbb7b201c8935c370df00a2c"}, + {file = "rpds_py-0.20.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a84ab91cbe7aab97f7446652d0ed37d35b68a465aeef8fc41932a9d7eee2c1a6"}, + {file = "rpds_py-0.20.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:56e27147a5a4c2c21633ff8475d185734c0e4befd1c989b5b95a5d0db699b21b"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2580b0c34583b85efec8c5c5ec9edf2dfe817330cc882ee972ae650e7b5ef739"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b80d4a7900cf6b66bb9cee5c352b2d708e29e5a37fe9bf784fa97fc11504bf6c"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50eccbf054e62a7b2209b28dc7a22d6254860209d6753e6b78cfaeb0075d7bee"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:49a8063ea4296b3a7e81a5dfb8f7b2d73f0b1c20c2af401fb0cdf22e14711a96"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea438162a9fcbee3ecf36c23e6c68237479f89f962f82dae83dc15feeceb37e4"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:18d7585c463087bddcfa74c2ba267339f14f2515158ac4db30b1f9cbdb62c8ef"}, + {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d4c7d1a051eeb39f5c9547e82ea27cbcc28338482242e3e0b7768033cb083821"}, + {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e4df1e3b3bec320790f699890d41c59d250f6beda159ea3c44c3f5bac1976940"}, + {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2cf126d33a91ee6eedc7f3197b53e87a2acdac63602c0f03a02dd69e4b138174"}, + {file = "rpds_py-0.20.0-cp312-none-win32.whl", hash = "sha256:8bc7690f7caee50b04a79bf017a8d020c1f48c2a1077ffe172abec59870f1139"}, + {file = "rpds_py-0.20.0-cp312-none-win_amd64.whl", hash = "sha256:0e13e6952ef264c40587d510ad676a988df19adea20444c2b295e536457bc585"}, + {file = "rpds_py-0.20.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:aa9a0521aeca7d4941499a73ad7d4f8ffa3d1affc50b9ea11d992cd7eff18a29"}, + {file = "rpds_py-0.20.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1f1d51eccb7e6c32ae89243cb352389228ea62f89cd80823ea7dd1b98e0b91"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a86a9b96070674fc88b6f9f71a97d2c1d3e5165574615d1f9168ecba4cecb24"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6c8ef2ebf76df43f5750b46851ed1cdf8f109d7787ca40035fe19fbdc1acc5a7"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b74b25f024b421d5859d156750ea9a65651793d51b76a2e9238c05c9d5f203a9"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57eb94a8c16ab08fef6404301c38318e2c5a32216bf5de453e2714c964c125c8"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1940dae14e715e2e02dfd5b0f64a52e8374a517a1e531ad9412319dc3ac7879"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d20277fd62e1b992a50c43f13fbe13277a31f8c9f70d59759c88f644d66c619f"}, + {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:06db23d43f26478303e954c34c75182356ca9aa7797d22c5345b16871ab9c45c"}, + {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b2a5db5397d82fa847e4c624b0c98fe59d2d9b7cf0ce6de09e4d2e80f8f5b3f2"}, + {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5a35df9f5548fd79cb2f52d27182108c3e6641a4feb0f39067911bf2adaa3e57"}, + {file = "rpds_py-0.20.0-cp313-none-win32.whl", hash = "sha256:fd2d84f40633bc475ef2d5490b9c19543fbf18596dcb1b291e3a12ea5d722f7a"}, + {file = "rpds_py-0.20.0-cp313-none-win_amd64.whl", hash = "sha256:9bc2d153989e3216b0559251b0c260cfd168ec78b1fac33dd485750a228db5a2"}, + {file = "rpds_py-0.20.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:f2fbf7db2012d4876fb0d66b5b9ba6591197b0f165db8d99371d976546472a24"}, + {file = "rpds_py-0.20.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1e5f3cd7397c8f86c8cc72d5a791071431c108edd79872cdd96e00abd8497d29"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce9845054c13696f7af7f2b353e6b4f676dab1b4b215d7fe5e05c6f8bb06f965"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c3e130fd0ec56cb76eb49ef52faead8ff09d13f4527e9b0c400307ff72b408e1"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b16aa0107ecb512b568244ef461f27697164d9a68d8b35090e9b0c1c8b27752"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aa7f429242aae2947246587d2964fad750b79e8c233a2367f71b554e9447949c"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af0fc424a5842a11e28956e69395fbbeab2c97c42253169d87e90aac2886d751"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b8c00a3b1e70c1d3891f0db1b05292747f0dbcfb49c43f9244d04c70fbc40eb8"}, + {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:40ce74fc86ee4645d0a225498d091d8bc61f39b709ebef8204cb8b5a464d3c0e"}, + {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:4fe84294c7019456e56d93e8ababdad5a329cd25975be749c3f5f558abb48253"}, + {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:338ca4539aad4ce70a656e5187a3a31c5204f261aef9f6ab50e50bcdffaf050a"}, + {file = "rpds_py-0.20.0-cp38-none-win32.whl", hash = "sha256:54b43a2b07db18314669092bb2de584524d1ef414588780261e31e85846c26a5"}, + {file = "rpds_py-0.20.0-cp38-none-win_amd64.whl", hash = "sha256:a1862d2d7ce1674cffa6d186d53ca95c6e17ed2b06b3f4c476173565c862d232"}, + {file = "rpds_py-0.20.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:3fde368e9140312b6e8b6c09fb9f8c8c2f00999d1823403ae90cc00480221b22"}, + {file = "rpds_py-0.20.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9824fb430c9cf9af743cf7aaf6707bf14323fb51ee74425c380f4c846ea70789"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11ef6ce74616342888b69878d45e9f779b95d4bd48b382a229fe624a409b72c5"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c52d3f2f82b763a24ef52f5d24358553e8403ce05f893b5347098014f2d9eff2"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d35cef91e59ebbeaa45214861874bc6f19eb35de96db73e467a8358d701a96c"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d72278a30111e5b5525c1dd96120d9e958464316f55adb030433ea905866f4de"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4c29cbbba378759ac5786730d1c3cb4ec6f8ababf5c42a9ce303dc4b3d08cda"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6632f2d04f15d1bd6fe0eedd3b86d9061b836ddca4c03d5cf5c7e9e6b7c14580"}, + {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d0b67d87bb45ed1cd020e8fbf2307d449b68abc45402fe1a4ac9e46c3c8b192b"}, + {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ec31a99ca63bf3cd7f1a5ac9fe95c5e2d060d3c768a09bc1d16e235840861420"}, + {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:22e6c9976e38f4d8c4a63bd8a8edac5307dffd3ee7e6026d97f3cc3a2dc02a0b"}, + {file = "rpds_py-0.20.0-cp39-none-win32.whl", hash = "sha256:569b3ea770c2717b730b61998b6c54996adee3cef69fc28d444f3e7920313cf7"}, + {file = "rpds_py-0.20.0-cp39-none-win_amd64.whl", hash = "sha256:e6900ecdd50ce0facf703f7a00df12374b74bbc8ad9fe0f6559947fb20f82364"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:617c7357272c67696fd052811e352ac54ed1d9b49ab370261a80d3b6ce385045"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9426133526f69fcaba6e42146b4e12d6bc6c839b8b555097020e2b78ce908dcc"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:deb62214c42a261cb3eb04d474f7155279c1a8a8c30ac89b7dcb1721d92c3c02"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fcaeb7b57f1a1e071ebd748984359fef83ecb026325b9d4ca847c95bc7311c92"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d454b8749b4bd70dd0a79f428731ee263fa6995f83ccb8bada706e8d1d3ff89d"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d807dc2051abe041b6649681dce568f8e10668e3c1c6543ebae58f2d7e617855"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3c20f0ddeb6e29126d45f89206b8291352b8c5b44384e78a6499d68b52ae511"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b7f19250ceef892adf27f0399b9e5afad019288e9be756d6919cb58892129f51"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:4f1ed4749a08379555cebf4650453f14452eaa9c43d0a95c49db50c18b7da075"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:dcedf0b42bcb4cfff4101d7771a10532415a6106062f005ab97d1d0ab5681c60"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:39ed0d010457a78f54090fafb5d108501b5aa5604cc22408fc1c0c77eac14344"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:bb273176be34a746bdac0b0d7e4e2c467323d13640b736c4c477881a3220a989"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f918a1a130a6dfe1d7fe0f105064141342e7dd1611f2e6a21cd2f5c8cb1cfb3e"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:f60012a73aa396be721558caa3a6fd49b3dd0033d1675c6d59c4502e870fcf0c"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d2b1ad682a3dfda2a4e8ad8572f3100f95fad98cb99faf37ff0ddfe9cbf9d03"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:614fdafe9f5f19c63ea02817fa4861c606a59a604a77c8cdef5aa01d28b97921"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa518bcd7600c584bf42e6617ee8132869e877db2f76bcdc281ec6a4113a53ab"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0475242f447cc6cb8a9dd486d68b2ef7fbee84427124c232bff5f63b1fe11e5"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f90a4cd061914a60bd51c68bcb4357086991bd0bb93d8aa66a6da7701370708f"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:def7400461c3a3f26e49078302e1c1b38f6752342c77e3cf72ce91ca69fb1bc1"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:65794e4048ee837494aea3c21a28ad5fc080994dfba5b036cf84de37f7ad5074"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:faefcc78f53a88f3076b7f8be0a8f8d35133a3ecf7f3770895c25f8813460f08"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:5b4f105deeffa28bbcdff6c49b34e74903139afa690e35d2d9e3c2c2fba18cec"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:fdfc3a892927458d98f3d55428ae46b921d1f7543b89382fdb483f5640daaec8"}, + {file = "rpds_py-0.20.0.tar.gz", hash = "sha256:d72a210824facfdaf8768cf2d7ca25a042c30320b3020de2fa04640920d4e121"}, +] + +[[package]] +name = "ruamel-yaml" +version = "0.18.6" +description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruamel.yaml-0.18.6-py3-none-any.whl", hash = "sha256:57b53ba33def16c4f3d807c0ccbc00f8a6081827e81ba2491691b76882d0c636"}, + {file = "ruamel.yaml-0.18.6.tar.gz", hash = "sha256:8b27e6a217e786c6fbe5634d8f3f11bc63e0f80f6a5890f28863d9c45aac311b"}, +] + +[package.dependencies] +"ruamel.yaml.clib" = {version = ">=0.2.7", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.13\""} + +[package.extras] +docs = ["mercurial (>5.7)", "ryd"] +jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"] + +[[package]] +name = "ruamel-yaml-clib" +version = "0.2.8" +description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml" +optional = false +python-versions = ">=3.6" +files = [ + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b42169467c42b692c19cf539c38d4602069d8c1505e97b86387fcf7afb766e1d"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:07238db9cbdf8fc1e9de2489a4f68474e70dffcb32232db7c08fa61ca0c7c462"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:d92f81886165cb14d7b067ef37e142256f1c6a90a65cd156b063a43da1708cfd"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:fff3573c2db359f091e1589c3d7c5fc2f86f5bdb6f24252c2d8e539d4e45f412"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:840f0c7f194986a63d2c2465ca63af8ccbbc90ab1c6001b1978f05119b5e7334"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:024cfe1fc7c7f4e1aff4a81e718109e13409767e4f871443cbff3dba3578203d"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-win32.whl", hash = "sha256:c69212f63169ec1cfc9bb44723bf2917cbbd8f6191a00ef3410f5a7fe300722d"}, + {file = "ruamel.yaml.clib-0.2.8-cp310-cp310-win_amd64.whl", hash = "sha256:cabddb8d8ead485e255fe80429f833172b4cadf99274db39abc080e068cbcc31"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bef08cd86169d9eafb3ccb0a39edb11d8e25f3dae2b28f5c52fd997521133069"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:b16420e621d26fdfa949a8b4b47ade8810c56002f5389970db4ddda51dbff248"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:b5edda50e5e9e15e54a6a8a0070302b00c518a9d32accc2346ad6c984aacd279"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:25c515e350e5b739842fc3228d662413ef28f295791af5e5110b543cf0b57d9b"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:46d378daaac94f454b3a0e3d8d78cafd78a026b1d71443f4966c696b48a6d899"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:09b055c05697b38ecacb7ac50bdab2240bfca1a0c4872b0fd309bb07dc9aa3a9"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-win32.whl", hash = "sha256:53a300ed9cea38cf5a2a9b069058137c2ca1ce658a874b79baceb8f892f915a7"}, + {file = "ruamel.yaml.clib-0.2.8-cp311-cp311-win_amd64.whl", hash = "sha256:c2a72e9109ea74e511e29032f3b670835f8a59bbdc9ce692c5b4ed91ccf1eedb"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:ebc06178e8821efc9692ea7544aa5644217358490145629914d8020042c24aa1"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-macosx_13_0_arm64.whl", hash = "sha256:edaef1c1200c4b4cb914583150dcaa3bc30e592e907c01117c08b13a07255ec2"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux2014_aarch64.whl", hash = "sha256:7048c338b6c86627afb27faecf418768acb6331fc24cfa56c93e8c9780f815fa"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d176b57452ab5b7028ac47e7b3cf644bcfdc8cacfecf7e71759f7f51a59e5c92"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3213ece08ea033eb159ac52ae052a4899b56ecc124bb80020d9bbceeb50258e9"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:aab7fd643f71d7946f2ee58cc88c9b7bfc97debd71dcc93e03e2d174628e7e2d"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-win32.whl", hash = "sha256:5c365d91c88390c8d0a8545df0b5857172824b1c604e867161e6b3d59a827eaa"}, + {file = "ruamel.yaml.clib-0.2.8-cp312-cp312-win_amd64.whl", hash = "sha256:1758ce7d8e1a29d23de54a16ae867abd370f01b5a69e1a3ba75223eaa3ca1a1b"}, + {file = "ruamel.yaml.clib-0.2.8-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a5aa27bad2bb83670b71683aae140a1f52b0857a2deff56ad3f6c13a017a26ed"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c58ecd827313af6864893e7af0a3bb85fd529f862b6adbefe14643947cfe2942"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-macosx_12_0_arm64.whl", hash = "sha256:f481f16baec5290e45aebdc2a5168ebc6d35189ae6fea7a58787613a25f6e875"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:3fcc54cb0c8b811ff66082de1680b4b14cf8a81dce0d4fbf665c2265a81e07a1"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7f67a1ee819dc4562d444bbafb135832b0b909f81cc90f7aa00260968c9ca1b3"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4ecbf9c3e19f9562c7fdd462e8d18dd902a47ca046a2e64dba80699f0b6c09b7"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:87ea5ff66d8064301a154b3933ae406b0863402a799b16e4a1d24d9fbbcbe0d3"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-win32.whl", hash = "sha256:75e1ed13e1f9de23c5607fe6bd1aeaae21e523b32d83bb33918245361e9cc51b"}, + {file = "ruamel.yaml.clib-0.2.8-cp37-cp37m-win_amd64.whl", hash = "sha256:3f215c5daf6a9d7bbed4a0a4f760f3113b10e82ff4c5c44bec20a68c8014f675"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1b617618914cb00bf5c34d4357c37aa15183fa229b24767259657746c9077615"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:a6a9ffd280b71ad062eae53ac1659ad86a17f59a0fdc7699fd9be40525153337"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:665f58bfd29b167039f714c6998178d27ccd83984084c286110ef26b230f259f"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:700e4ebb569e59e16a976857c8798aee258dceac7c7d6b50cab63e080058df91"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:e2b4c44b60eadec492926a7270abb100ef9f72798e18743939bdbf037aab8c28"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e79e5db08739731b0ce4850bed599235d601701d5694c36570a99a0c5ca41a9d"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-win32.whl", hash = "sha256:955eae71ac26c1ab35924203fda6220f84dce57d6d7884f189743e2abe3a9fbe"}, + {file = "ruamel.yaml.clib-0.2.8-cp38-cp38-win_amd64.whl", hash = "sha256:56f4252222c067b4ce51ae12cbac231bce32aee1d33fbfc9d17e5b8d6966c312"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:03d1162b6d1df1caa3a4bd27aa51ce17c9afc2046c31b0ad60a0a96ec22f8001"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:bba64af9fa9cebe325a62fa398760f5c7206b215201b0ec825005f1b18b9bccf"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:9eb5dee2772b0f704ca2e45b1713e4e5198c18f515b52743576d196348f374d3"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:da09ad1c359a728e112d60116f626cc9f29730ff3e0e7db72b9a2dbc2e4beed5"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:184565012b60405d93838167f425713180b949e9d8dd0bbc7b49f074407c5a8b"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a75879bacf2c987c003368cf14bed0ffe99e8e85acfa6c0bfffc21a090f16880"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-win32.whl", hash = "sha256:84b554931e932c46f94ab306913ad7e11bba988104c5cff26d90d03f68258cd5"}, + {file = "ruamel.yaml.clib-0.2.8-cp39-cp39-win_amd64.whl", hash = "sha256:25ac8c08322002b06fa1d49d1646181f0b2c72f5cbc15a85e80b4c30a544bb15"}, + {file = "ruamel.yaml.clib-0.2.8.tar.gz", hash = "sha256:beb2e0404003de9a4cab9753a8805a8fe9320ee6673136ed7f04255fe60bb512"}, +] + [[package]] name = "ruff" -version = "0.5.0" +version = "0.7.1" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.5.0-py3-none-linux_armv6l.whl", hash = "sha256:ee770ea8ab38918f34e7560a597cc0a8c9a193aaa01bfbd879ef43cb06bd9c4c"}, - {file = "ruff-0.5.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:38f3b8327b3cb43474559d435f5fa65dacf723351c159ed0dc567f7ab735d1b6"}, - {file = "ruff-0.5.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7594f8df5404a5c5c8f64b8311169879f6cf42142da644c7e0ba3c3f14130370"}, - {file = "ruff-0.5.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:adc7012d6ec85032bc4e9065110df205752d64010bed5f958d25dbee9ce35de3"}, - {file = "ruff-0.5.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d505fb93b0fabef974b168d9b27c3960714d2ecda24b6ffa6a87ac432905ea38"}, - {file = "ruff-0.5.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9dc5cfd3558f14513ed0d5b70ce531e28ea81a8a3b1b07f0f48421a3d9e7d80a"}, - {file = "ruff-0.5.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:db3ca35265de239a1176d56a464b51557fce41095c37d6c406e658cf80bbb362"}, - {file = "ruff-0.5.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b1a321c4f68809fddd9b282fab6a8d8db796b270fff44722589a8b946925a2a8"}, - {file = "ruff-0.5.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2c4dfcd8d34b143916994b3876b63d53f56724c03f8c1a33a253b7b1e6bf2a7d"}, - {file = "ruff-0.5.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81e5facfc9f4a674c6a78c64d38becfbd5e4f739c31fcd9ce44c849f1fad9e4c"}, - {file = "ruff-0.5.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e589e27971c2a3efff3fadafb16e5aef7ff93250f0134ec4b52052b673cf988d"}, - {file = "ruff-0.5.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d2ffbc3715a52b037bcb0f6ff524a9367f642cdc5817944f6af5479bbb2eb50e"}, - {file = "ruff-0.5.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:cd096e23c6a4f9c819525a437fa0a99d1c67a1b6bb30948d46f33afbc53596cf"}, - {file = "ruff-0.5.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:46e193b36f2255729ad34a49c9a997d506e58f08555366b2108783b3064a0e1e"}, - {file = "ruff-0.5.0-py3-none-win32.whl", hash = "sha256:49141d267100f5ceff541b4e06552e98527870eafa1acc9dec9139c9ec5af64c"}, - {file = "ruff-0.5.0-py3-none-win_amd64.whl", hash = "sha256:e9118f60091047444c1b90952736ee7b1792910cab56e9b9a9ac20af94cd0440"}, - {file = "ruff-0.5.0-py3-none-win_arm64.whl", hash = "sha256:ed5c4df5c1fb4518abcb57725b576659542bdbe93366f4f329e8f398c4b71178"}, - {file = "ruff-0.5.0.tar.gz", hash = "sha256:eb641b5873492cf9bd45bc9c5ae5320648218e04386a5f0c264ad6ccce8226a1"}, + {file = "ruff-0.7.1-py3-none-linux_armv6l.whl", hash = "sha256:cb1bc5ed9403daa7da05475d615739cc0212e861b7306f314379d958592aaa89"}, + {file = "ruff-0.7.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:27c1c52a8d199a257ff1e5582d078eab7145129aa02721815ca8fa4f9612dc35"}, + {file = "ruff-0.7.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:588a34e1ef2ea55b4ddfec26bbe76bc866e92523d8c6cdec5e8aceefeff02d99"}, + {file = "ruff-0.7.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94fc32f9cdf72dc75c451e5f072758b118ab8100727168a3df58502b43a599ca"}, + {file = "ruff-0.7.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:985818742b833bffa543a84d1cc11b5e6871de1b4e0ac3060a59a2bae3969250"}, + {file = "ruff-0.7.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32f1e8a192e261366c702c5fb2ece9f68d26625f198a25c408861c16dc2dea9c"}, + {file = "ruff-0.7.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:699085bf05819588551b11751eff33e9ca58b1b86a6843e1b082a7de40da1565"}, + {file = "ruff-0.7.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:344cc2b0814047dc8c3a8ff2cd1f3d808bb23c6658db830d25147339d9bf9ea7"}, + {file = "ruff-0.7.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4316bbf69d5a859cc937890c7ac7a6551252b6a01b1d2c97e8fc96e45a7c8b4a"}, + {file = "ruff-0.7.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:79d3af9dca4c56043e738a4d6dd1e9444b6d6c10598ac52d146e331eb155a8ad"}, + {file = "ruff-0.7.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:c5c121b46abde94a505175524e51891f829414e093cd8326d6e741ecfc0a9112"}, + {file = "ruff-0.7.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8422104078324ea250886954e48f1373a8fe7de59283d747c3a7eca050b4e378"}, + {file = "ruff-0.7.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:56aad830af8a9db644e80098fe4984a948e2b6fc2e73891538f43bbe478461b8"}, + {file = "ruff-0.7.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:658304f02f68d3a83c998ad8bf91f9b4f53e93e5412b8f2388359d55869727fd"}, + {file = "ruff-0.7.1-py3-none-win32.whl", hash = "sha256:b517a2011333eb7ce2d402652ecaa0ac1a30c114fbbd55c6b8ee466a7f600ee9"}, + {file = "ruff-0.7.1-py3-none-win_amd64.whl", hash = "sha256:f38c41fcde1728736b4eb2b18850f6d1e3eedd9678c914dede554a70d5241307"}, + {file = "ruff-0.7.1-py3-none-win_arm64.whl", hash = "sha256:19aa200ec824c0f36d0c9114c8ec0087082021732979a359d6f3c390a6ff2a37"}, + {file = "ruff-0.7.1.tar.gz", hash = "sha256:9d8a41d4aa2dad1575adb98a82870cf5db5f76b2938cf2206c22c940034a36f4"}, ] [[package]] @@ -4216,18 +4723,18 @@ crt = ["botocore[crt] (>=1.33.2,<2.0a.0)"] [[package]] name = "setuptools" -version = "74.1.2" +version = "75.1.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-74.1.2-py3-none-any.whl", hash = "sha256:5f4c08aa4d3ebcb57a50c33b1b07e94315d7fc7230f7115e47fc99776c8ce308"}, - {file = "setuptools-74.1.2.tar.gz", hash = "sha256:95b40ed940a1c67eb70fc099094bd6e99c6ee7c23aa2306f4d2697ba7916f9c6"}, + {file = "setuptools-75.1.0-py3-none-any.whl", hash = "sha256:35ab7fd3bcd95e6b7fd704e4a1539513edad446c097797f2985e0e4b960772f2"}, + {file = "setuptools-75.1.0.tar.gz", hash = "sha256:d59a21b17a275fb872a9c3dae73963160ae079f1049ed956880cd7c09b120538"}, ] [package.extras] check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.5.2)"] -core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.text (>=3.7)", "more-itertools (>=8.8)", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.collections", "jaraco.functools", "jaraco.text (>=3.7)", "more-itertools", "more-itertools (>=8.8)", "packaging", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] enabler = ["pytest-enabler (>=2.2)"] @@ -4278,6 +4785,93 @@ files = [ {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, ] +[[package]] +name = "sqlalchemy" +version = "2.0.35" +description = "Database Abstraction Library" +optional = false +python-versions = ">=3.7" +files = [ + {file = "SQLAlchemy-2.0.35-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:67219632be22f14750f0d1c70e62f204ba69d28f62fd6432ba05ab295853de9b"}, + {file = "SQLAlchemy-2.0.35-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4668bd8faf7e5b71c0319407b608f278f279668f358857dbfd10ef1954ac9f90"}, + {file = "SQLAlchemy-2.0.35-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb8bea573863762bbf45d1e13f87c2d2fd32cee2dbd50d050f83f87429c9e1ea"}, + {file = "SQLAlchemy-2.0.35-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f552023710d4b93d8fb29a91fadf97de89c5926c6bd758897875435f2a939f33"}, + {file = "SQLAlchemy-2.0.35-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:016b2e665f778f13d3c438651dd4de244214b527a275e0acf1d44c05bc6026a9"}, + {file = "SQLAlchemy-2.0.35-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7befc148de64b6060937231cbff8d01ccf0bfd75aa26383ffdf8d82b12ec04ff"}, + {file = "SQLAlchemy-2.0.35-cp310-cp310-win32.whl", hash = "sha256:22b83aed390e3099584b839b93f80a0f4a95ee7f48270c97c90acd40ee646f0b"}, + {file = "SQLAlchemy-2.0.35-cp310-cp310-win_amd64.whl", hash = "sha256:a29762cd3d116585278ffb2e5b8cc311fb095ea278b96feef28d0b423154858e"}, + {file = "SQLAlchemy-2.0.35-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e21f66748ab725ade40fa7af8ec8b5019c68ab00b929f6643e1b1af461eddb60"}, + {file = "SQLAlchemy-2.0.35-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8a6219108a15fc6d24de499d0d515c7235c617b2540d97116b663dade1a54d62"}, + {file = "SQLAlchemy-2.0.35-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:042622a5306c23b972192283f4e22372da3b8ddf5f7aac1cc5d9c9b222ab3ff6"}, + {file = "SQLAlchemy-2.0.35-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:627dee0c280eea91aed87b20a1f849e9ae2fe719d52cbf847c0e0ea34464b3f7"}, + {file = "SQLAlchemy-2.0.35-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4fdcd72a789c1c31ed242fd8c1bcd9ea186a98ee8e5408a50e610edfef980d71"}, + {file = "SQLAlchemy-2.0.35-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:89b64cd8898a3a6f642db4eb7b26d1b28a497d4022eccd7717ca066823e9fb01"}, + {file = "SQLAlchemy-2.0.35-cp311-cp311-win32.whl", hash = "sha256:6a93c5a0dfe8d34951e8a6f499a9479ffb9258123551fa007fc708ae2ac2bc5e"}, + {file = "SQLAlchemy-2.0.35-cp311-cp311-win_amd64.whl", hash = "sha256:c68fe3fcde03920c46697585620135b4ecfdfc1ed23e75cc2c2ae9f8502c10b8"}, + {file = "SQLAlchemy-2.0.35-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:eb60b026d8ad0c97917cb81d3662d0b39b8ff1335e3fabb24984c6acd0c900a2"}, + {file = "SQLAlchemy-2.0.35-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6921ee01caf375363be5e9ae70d08ce7ca9d7e0e8983183080211a062d299468"}, + {file = "SQLAlchemy-2.0.35-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8cdf1a0dbe5ced887a9b127da4ffd7354e9c1a3b9bb330dce84df6b70ccb3a8d"}, + {file = "SQLAlchemy-2.0.35-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93a71c8601e823236ac0e5d087e4f397874a421017b3318fd92c0b14acf2b6db"}, + {file = "SQLAlchemy-2.0.35-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e04b622bb8a88f10e439084486f2f6349bf4d50605ac3e445869c7ea5cf0fa8c"}, + {file = "SQLAlchemy-2.0.35-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1b56961e2d31389aaadf4906d453859f35302b4eb818d34a26fab72596076bb8"}, + {file = "SQLAlchemy-2.0.35-cp312-cp312-win32.whl", hash = "sha256:0f9f3f9a3763b9c4deb8c5d09c4cc52ffe49f9876af41cc1b2ad0138878453cf"}, + {file = "SQLAlchemy-2.0.35-cp312-cp312-win_amd64.whl", hash = "sha256:25b0f63e7fcc2a6290cb5f7f5b4fc4047843504983a28856ce9b35d8f7de03cc"}, + {file = "SQLAlchemy-2.0.35-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:f021d334f2ca692523aaf7bbf7592ceff70c8594fad853416a81d66b35e3abf9"}, + {file = "SQLAlchemy-2.0.35-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05c3f58cf91683102f2f0265c0db3bd3892e9eedabe059720492dbaa4f922da1"}, + {file = "SQLAlchemy-2.0.35-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:032d979ce77a6c2432653322ba4cbeabf5a6837f704d16fa38b5a05d8e21fa00"}, + {file = "SQLAlchemy-2.0.35-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:2e795c2f7d7249b75bb5f479b432a51b59041580d20599d4e112b5f2046437a3"}, + {file = "SQLAlchemy-2.0.35-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:cc32b2990fc34380ec2f6195f33a76b6cdaa9eecf09f0c9404b74fc120aef36f"}, + {file = "SQLAlchemy-2.0.35-cp37-cp37m-win32.whl", hash = "sha256:9509c4123491d0e63fb5e16199e09f8e262066e58903e84615c301dde8fa2e87"}, + {file = "SQLAlchemy-2.0.35-cp37-cp37m-win_amd64.whl", hash = "sha256:3655af10ebcc0f1e4e06c5900bb33e080d6a1fa4228f502121f28a3b1753cde5"}, + {file = "SQLAlchemy-2.0.35-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4c31943b61ed8fdd63dfd12ccc919f2bf95eefca133767db6fbbd15da62078ec"}, + {file = "SQLAlchemy-2.0.35-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a62dd5d7cc8626a3634208df458c5fe4f21200d96a74d122c83bc2015b333bc1"}, + {file = "SQLAlchemy-2.0.35-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0630774b0977804fba4b6bbea6852ab56c14965a2b0c7fc7282c5f7d90a1ae72"}, + {file = "SQLAlchemy-2.0.35-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d625eddf7efeba2abfd9c014a22c0f6b3796e0ffb48f5d5ab106568ef01ff5a"}, + {file = "SQLAlchemy-2.0.35-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:ada603db10bb865bbe591939de854faf2c60f43c9b763e90f653224138f910d9"}, + {file = "SQLAlchemy-2.0.35-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c41411e192f8d3ea39ea70e0fae48762cd11a2244e03751a98bd3c0ca9a4e936"}, + {file = "SQLAlchemy-2.0.35-cp38-cp38-win32.whl", hash = "sha256:d299797d75cd747e7797b1b41817111406b8b10a4f88b6e8fe5b5e59598b43b0"}, + {file = "SQLAlchemy-2.0.35-cp38-cp38-win_amd64.whl", hash = "sha256:0375a141e1c0878103eb3d719eb6d5aa444b490c96f3fedab8471c7f6ffe70ee"}, + {file = "SQLAlchemy-2.0.35-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ccae5de2a0140d8be6838c331604f91d6fafd0735dbdcee1ac78fc8fbaba76b4"}, + {file = "SQLAlchemy-2.0.35-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2a275a806f73e849e1c309ac11108ea1a14cd7058577aba962cd7190e27c9e3c"}, + {file = "SQLAlchemy-2.0.35-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:732e026240cdd1c1b2e3ac515c7a23820430ed94292ce33806a95869c46bd139"}, + {file = "SQLAlchemy-2.0.35-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:890da8cd1941fa3dab28c5bac3b9da8502e7e366f895b3b8e500896f12f94d11"}, + {file = "SQLAlchemy-2.0.35-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:c0d8326269dbf944b9201911b0d9f3dc524d64779a07518199a58384c3d37a44"}, + {file = "SQLAlchemy-2.0.35-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b76d63495b0508ab9fc23f8152bac63205d2a704cd009a2b0722f4c8e0cba8e0"}, + {file = "SQLAlchemy-2.0.35-cp39-cp39-win32.whl", hash = "sha256:69683e02e8a9de37f17985905a5eca18ad651bf592314b4d3d799029797d0eb3"}, + {file = "SQLAlchemy-2.0.35-cp39-cp39-win_amd64.whl", hash = "sha256:aee110e4ef3c528f3abbc3c2018c121e708938adeeff9006428dd7c8555e9b3f"}, + {file = "SQLAlchemy-2.0.35-py3-none-any.whl", hash = "sha256:2ab3f0336c0387662ce6221ad30ab3a5e6499aab01b9790879b6578fd9b8faa1"}, + {file = "sqlalchemy-2.0.35.tar.gz", hash = "sha256:e11d7ea4d24f0a262bccf9a7cd6284c976c5369dac21db237cff59586045ab9f"}, +] + +[package.dependencies] +greenlet = {version = "!=0.4.17", optional = true, markers = "python_version < \"3.13\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\") or extra == \"asyncio\""} +typing-extensions = ">=4.6.0" + +[package.extras] +aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"] +aioodbc = ["aioodbc", "greenlet (!=0.4.17)"] +aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"] +asyncio = ["greenlet (!=0.4.17)"] +asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (!=0.4.17)"] +mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5)"] +mssql = ["pyodbc"] +mssql-pymssql = ["pymssql"] +mssql-pyodbc = ["pyodbc"] +mypy = ["mypy (>=0.910)"] +mysql = ["mysqlclient (>=1.4.0)"] +mysql-connector = ["mysql-connector-python"] +oracle = ["cx_oracle (>=8)"] +oracle-oracledb = ["oracledb (>=1.0.1)"] +postgresql = ["psycopg2 (>=2.7)"] +postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] +postgresql-pg8000 = ["pg8000 (>=1.29.1)"] +postgresql-psycopg = ["psycopg (>=3.0.7)"] +postgresql-psycopg2binary = ["psycopg2-binary"] +postgresql-psycopg2cffi = ["psycopg2cffi"] +postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"] +pymysql = ["pymysql"] +sqlcipher = ["sqlcipher3_binary"] + [[package]] name = "stack-data" version = "0.6.3" @@ -4299,13 +4893,13 @@ tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] [[package]] name = "starlette" -version = "0.38.5" +version = "0.40.0" description = "The little ASGI library that shines." optional = false python-versions = ">=3.8" files = [ - {file = "starlette-0.38.5-py3-none-any.whl", hash = "sha256:632f420a9d13e3ee2a6f18f437b0a9f1faecb0bc42e1942aa2ea0e379a4c4206"}, - {file = "starlette-0.38.5.tar.gz", hash = "sha256:04a92830a9b6eb1442c766199d62260c3d4dc9c4f9188360626b1e0273cb7077"}, + {file = "starlette-0.40.0-py3-none-any.whl", hash = "sha256:c494a22fae73805376ea6bf88439783ecfba9aac88a43911b48c653437e784c4"}, + {file = "starlette-0.40.0.tar.gz", hash = "sha256:1a3139688fb298ce5e2d661d37046a66ad996ce94be4d4983be019a23a04ea35"}, ] [package.dependencies] @@ -4346,6 +4940,69 @@ docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib- tests = ["freezegun (>=0.2.8)", "pretend", "pytest (>=6.0)", "pytest-asyncio (>=0.17)", "simplejson"] typing = ["mypy (>=1.4)", "rich", "twisted"] +[[package]] +name = "testcontainers" +version = "4.8.1" +description = "Python library for throwaway instances of anything that can run in a Docker container" +optional = false +python-versions = "<4.0,>=3.9" +files = [ + {file = "testcontainers-4.8.1-py3-none-any.whl", hash = "sha256:d8ae43e8fe34060fcd5c3f494e0b7652b7774beabe94568a2283d0881e94d489"}, + {file = "testcontainers-4.8.1.tar.gz", hash = "sha256:5ded4820b7227ad526857eb3caaafcabce1bbac05d22ad194849b136ffae3cb0"}, +] + +[package.dependencies] +docker = "*" +typing-extensions = "*" +urllib3 = "*" +wrapt = "*" + +[package.extras] +arangodb = ["python-arango (>=7.8,<8.0)"] +aws = ["boto3", "httpx"] +azurite = ["azure-storage-blob (>=12.19,<13.0)"] +chroma = ["chromadb-client"] +clickhouse = ["clickhouse-driver"] +cosmosdb = ["azure-cosmos"] +db2 = ["ibm_db_sa", "sqlalchemy"] +generic = ["httpx", "redis"] +google = ["google-cloud-datastore (>=2)", "google-cloud-pubsub (>=2)"] +influxdb = ["influxdb", "influxdb-client"] +k3s = ["kubernetes", "pyyaml"] +keycloak = ["python-keycloak"] +localstack = ["boto3"] +mailpit = ["cryptography"] +minio = ["minio"] +mongodb = ["pymongo"] +mssql = ["pymssql", "sqlalchemy"] +mysql = ["pymysql[rsa]", "sqlalchemy"] +nats = ["nats-py"] +neo4j = ["neo4j"] +opensearch = ["opensearch-py"] +oracle = ["oracledb", "sqlalchemy"] +oracle-free = ["oracledb", "sqlalchemy"] +qdrant = ["qdrant-client"] +rabbitmq = ["pika"] +redis = ["redis"] +registry = ["bcrypt"] +scylla = ["cassandra-driver (==3.29.1)"] +selenium = ["selenium"] +sftp = ["cryptography"] +test-module-import = ["httpx"] +trino = ["trino"] +weaviate = ["weaviate-client (>=4.5.4,<5.0.0)"] + +[[package]] +name = "text-unidecode" +version = "1.3" +description = "The most basic Text::Unidecode port" +optional = false +python-versions = "*" +files = [ + {file = "text-unidecode-1.3.tar.gz", hash = "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93"}, + {file = "text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8"}, +] + [[package]] name = "toml" version = "0.10.2" @@ -4415,13 +5072,13 @@ test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0, [[package]] name = "typer" -version = "0.12.3" +version = "0.12.5" description = "Typer, build great CLIs. Easy to code. Based on Python type hints." optional = false python-versions = ">=3.7" files = [ - {file = "typer-0.12.3-py3-none-any.whl", hash = "sha256:070d7ca53f785acbccba8e7d28b08dcd88f79f1fbda035ade0aecec71ca5c914"}, - {file = "typer-0.12.3.tar.gz", hash = "sha256:49e73131481d804288ef62598d97a1ceef3058905aa536a1134f90891ba35482"}, + {file = "typer-0.12.5-py3-none-any.whl", hash = "sha256:62fe4e471711b147e3365034133904df3e235698399bc4de2b36c8579298d52b"}, + {file = "typer-0.12.5.tar.gz", hash = "sha256:f592f089bedcc8ec1b974125d64851029c3b1af145f04aca64d69410f0c9b722"}, ] [package.dependencies] @@ -4443,13 +5100,13 @@ files = [ [[package]] name = "types-pyyaml" -version = "6.0.12.20240808" +version = "6.0.12.20240917" description = "Typing stubs for PyYAML" optional = false python-versions = ">=3.8" files = [ - {file = "types-PyYAML-6.0.12.20240808.tar.gz", hash = "sha256:b8f76ddbd7f65440a8bda5526a9607e4c7a322dc2f8e1a8c405644f9a6f4b9af"}, - {file = "types_PyYAML-6.0.12.20240808-py3-none-any.whl", hash = "sha256:deda34c5c655265fc517b546c902aa6eed2ef8d3e921e4765fe606fe2afe8d35"}, + {file = "types-PyYAML-6.0.12.20240917.tar.gz", hash = "sha256:d1405a86f9576682234ef83bcb4e6fff7c9305c8b1fbad5e0bcd4f7dbdc9c587"}, + {file = "types_PyYAML-6.0.12.20240917-py3-none-any.whl", hash = "sha256:392b267f1c0fe6022952462bf5d6523f31e37f6cea49b14cee7ad634b6301570"}, ] [[package]] @@ -4496,6 +5153,23 @@ files = [ {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, ] +[[package]] +name = "tzlocal" +version = "5.2" +description = "tzinfo object for the local timezone" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tzlocal-5.2-py3-none-any.whl", hash = "sha256:49816ef2fe65ea8ac19d19aa7a1ae0551c834303d5014c6d5a62e4cbda8047b8"}, + {file = "tzlocal-5.2.tar.gz", hash = "sha256:8d399205578f1a9342816409cc1e46a93ebd5755e39ea2d85334bea911bf0e6e"}, +] + +[package.dependencies] +tzdata = {version = "*", markers = "platform_system == \"Windows\""} + +[package.extras] +devenv = ["check-manifest", "pytest (>=4.3)", "pytest-cov", "pytest-mock (>=3.3)", "zest.releaser"] + [[package]] name = "ujson" version = "5.10.0" @@ -4585,13 +5259,13 @@ files = [ [[package]] name = "urllib3" -version = "2.2.2" +version = "2.2.3" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.8" files = [ - {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"}, - {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"}, + {file = "urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac"}, + {file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"}, ] [package.extras] @@ -4602,13 +5276,13 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "uvicorn" -version = "0.30.6" +version = "0.32.0" description = "The lightning-fast ASGI server." optional = false python-versions = ">=3.8" files = [ - {file = "uvicorn-0.30.6-py3-none-any.whl", hash = "sha256:65fd46fe3fda5bdc1b03b94eb634923ff18cd35b2f084813ea79d1f103f711b5"}, - {file = "uvicorn-0.30.6.tar.gz", hash = "sha256:4b15decdda1e72be08209e860a1e10e92439ad5b97cf44cc945fcbee66fc5788"}, + {file = "uvicorn-0.32.0-py3-none-any.whl", hash = "sha256:60b8f3a5ac027dcd31448f411ced12b5ef452c646f76f02f8cc3f25d8d26fd82"}, + {file = "uvicorn-0.32.0.tar.gz", hash = "sha256:f78b36b143c16f54ccdb8190d0a26b5f1901fe5a3c777e1ab29f26391af8551e"}, ] [package.dependencies] @@ -4672,13 +5346,13 @@ test = ["Cython (>=0.29.36,<0.30.0)", "aiohttp (==3.9.0b0)", "aiohttp (>=3.8.1)" [[package]] name = "virtualenv" -version = "20.26.4" +version = "20.26.5" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.26.4-py3-none-any.whl", hash = "sha256:48f2695d9809277003f30776d155615ffc11328e6a0a8c1f0ec80188d7874a55"}, - {file = "virtualenv-20.26.4.tar.gz", hash = "sha256:c17f4e0f3e6036e9f26700446f85c76ab11df65ff6d8a9cbfad9f71aabfcf23c"}, + {file = "virtualenv-20.26.5-py3-none-any.whl", hash = "sha256:4f3ac17b81fba3ce3bd6f4ead2749a72da5929c01774948e243db9ba41df4ff6"}, + {file = "virtualenv-20.26.5.tar.gz", hash = "sha256:ce489cac131aa58f4b25e321d6d186171f78e6cb13fafbf32a840cee67733ff4"}, ] [package.dependencies] @@ -4798,116 +5472,99 @@ files = [ [[package]] name = "websockets" -version = "13.0.1" +version = "13.1" description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" optional = false python-versions = ">=3.8" files = [ - {file = "websockets-13.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1841c9082a3ba4a05ea824cf6d99570a6a2d8849ef0db16e9c826acb28089e8f"}, - {file = "websockets-13.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c5870b4a11b77e4caa3937142b650fbbc0914a3e07a0cf3131f35c0587489c1c"}, - {file = "websockets-13.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f1d3d1f2eb79fe7b0fb02e599b2bf76a7619c79300fc55f0b5e2d382881d4f7f"}, - {file = "websockets-13.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15c7d62ee071fa94a2fc52c2b472fed4af258d43f9030479d9c4a2de885fd543"}, - {file = "websockets-13.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6724b554b70d6195ba19650fef5759ef11346f946c07dbbe390e039bcaa7cc3d"}, - {file = "websockets-13.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56a952fa2ae57a42ba7951e6b2605e08a24801a4931b5644dfc68939e041bc7f"}, - {file = "websockets-13.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:17118647c0ea14796364299e942c330d72acc4b248e07e639d34b75067b3cdd8"}, - {file = "websockets-13.0.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:64a11aae1de4c178fa653b07d90f2fb1a2ed31919a5ea2361a38760192e1858b"}, - {file = "websockets-13.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0617fd0b1d14309c7eab6ba5deae8a7179959861846cbc5cb528a7531c249448"}, - {file = "websockets-13.0.1-cp310-cp310-win32.whl", hash = "sha256:11f9976ecbc530248cf162e359a92f37b7b282de88d1d194f2167b5e7ad80ce3"}, - {file = "websockets-13.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:c3c493d0e5141ec055a7d6809a28ac2b88d5b878bb22df8c621ebe79a61123d0"}, - {file = "websockets-13.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:699ba9dd6a926f82a277063603fc8d586b89f4cb128efc353b749b641fcddda7"}, - {file = "websockets-13.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cf2fae6d85e5dc384bf846f8243ddaa9197f3a1a70044f59399af001fd1f51d4"}, - {file = "websockets-13.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:52aed6ef21a0f1a2a5e310fb5c42d7555e9c5855476bbd7173c3aa3d8a0302f2"}, - {file = "websockets-13.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8eb2b9a318542153674c6e377eb8cb9ca0fc011c04475110d3477862f15d29f0"}, - {file = "websockets-13.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5df891c86fe68b2c38da55b7aea7095beca105933c697d719f3f45f4220a5e0e"}, - {file = "websockets-13.0.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fac2d146ff30d9dd2fcf917e5d147db037a5c573f0446c564f16f1f94cf87462"}, - {file = "websockets-13.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b8ac5b46fd798bbbf2ac6620e0437c36a202b08e1f827832c4bf050da081b501"}, - {file = "websockets-13.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:46af561eba6f9b0848b2c9d2427086cabadf14e0abdd9fde9d72d447df268418"}, - {file = "websockets-13.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b5a06d7f60bc2fc378a333978470dfc4e1415ee52f5f0fce4f7853eb10c1e9df"}, - {file = "websockets-13.0.1-cp311-cp311-win32.whl", hash = "sha256:556e70e4f69be1082e6ef26dcb70efcd08d1850f5d6c5f4f2bcb4e397e68f01f"}, - {file = "websockets-13.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:67494e95d6565bf395476e9d040037ff69c8b3fa356a886b21d8422ad86ae075"}, - {file = "websockets-13.0.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f9c9e258e3d5efe199ec23903f5da0eeaad58cf6fccb3547b74fd4750e5ac47a"}, - {file = "websockets-13.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6b41a1b3b561f1cba8321fb32987552a024a8f67f0d05f06fcf29f0090a1b956"}, - {file = "websockets-13.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f73e676a46b0fe9426612ce8caeca54c9073191a77c3e9d5c94697aef99296af"}, - {file = "websockets-13.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f613289f4a94142f914aafad6c6c87903de78eae1e140fa769a7385fb232fdf"}, - {file = "websockets-13.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f52504023b1480d458adf496dc1c9e9811df4ba4752f0bc1f89ae92f4f07d0c"}, - {file = "websockets-13.0.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:139add0f98206cb74109faf3611b7783ceafc928529c62b389917a037d4cfdf4"}, - {file = "websockets-13.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:47236c13be337ef36546004ce8c5580f4b1150d9538b27bf8a5ad8edf23ccfab"}, - {file = "websockets-13.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c44ca9ade59b2e376612df34e837013e2b273e6c92d7ed6636d0556b6f4db93d"}, - {file = "websockets-13.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9bbc525f4be3e51b89b2a700f5746c2a6907d2e2ef4513a8daafc98198b92237"}, - {file = "websockets-13.0.1-cp312-cp312-win32.whl", hash = "sha256:3624fd8664f2577cf8de996db3250662e259bfbc870dd8ebdcf5d7c6ac0b5185"}, - {file = "websockets-13.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0513c727fb8adffa6d9bf4a4463b2bade0186cbd8c3604ae5540fae18a90cb99"}, - {file = "websockets-13.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:1ee4cc030a4bdab482a37462dbf3ffb7e09334d01dd37d1063be1136a0d825fa"}, - {file = "websockets-13.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dbb0b697cc0655719522406c059eae233abaa3243821cfdfab1215d02ac10231"}, - {file = "websockets-13.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:acbebec8cb3d4df6e2488fbf34702cbc37fc39ac7abf9449392cefb3305562e9"}, - {file = "websockets-13.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63848cdb6fcc0bf09d4a155464c46c64ffdb5807ede4fb251da2c2692559ce75"}, - {file = "websockets-13.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:872afa52a9f4c414d6955c365b6588bc4401272c629ff8321a55f44e3f62b553"}, - {file = "websockets-13.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05e70fec7c54aad4d71eae8e8cab50525e899791fc389ec6f77b95312e4e9920"}, - {file = "websockets-13.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e82db3756ccb66266504f5a3de05ac6b32f287faacff72462612120074103329"}, - {file = "websockets-13.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4e85f46ce287f5c52438bb3703d86162263afccf034a5ef13dbe4318e98d86e7"}, - {file = "websockets-13.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f3fea72e4e6edb983908f0db373ae0732b275628901d909c382aae3b592589f2"}, - {file = "websockets-13.0.1-cp313-cp313-win32.whl", hash = "sha256:254ecf35572fca01a9f789a1d0f543898e222f7b69ecd7d5381d8d8047627bdb"}, - {file = "websockets-13.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:ca48914cdd9f2ccd94deab5bcb5ac98025a5ddce98881e5cce762854a5de330b"}, - {file = "websockets-13.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b74593e9acf18ea5469c3edaa6b27fa7ecf97b30e9dabd5a94c4c940637ab96e"}, - {file = "websockets-13.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:132511bfd42e77d152c919147078460c88a795af16b50e42a0bd14f0ad71ddd2"}, - {file = "websockets-13.0.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:165bedf13556f985a2aa064309baa01462aa79bf6112fbd068ae38993a0e1f1b"}, - {file = "websockets-13.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e801ca2f448850685417d723ec70298feff3ce4ff687c6f20922c7474b4746ae"}, - {file = "websockets-13.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30d3a1f041360f029765d8704eae606781e673e8918e6b2c792e0775de51352f"}, - {file = "websockets-13.0.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67648f5e50231b5a7f6d83b32f9c525e319f0ddc841be0de64f24928cd75a603"}, - {file = "websockets-13.0.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:4f0426d51c8f0926a4879390f53c7f5a855e42d68df95fff6032c82c888b5f36"}, - {file = "websockets-13.0.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ef48e4137e8799998a343706531e656fdec6797b80efd029117edacb74b0a10a"}, - {file = "websockets-13.0.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:249aab278810bee585cd0d4de2f08cfd67eed4fc75bde623be163798ed4db2eb"}, - {file = "websockets-13.0.1-cp38-cp38-win32.whl", hash = "sha256:06c0a667e466fcb56a0886d924b5f29a7f0886199102f0a0e1c60a02a3751cb4"}, - {file = "websockets-13.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1f3cf6d6ec1142412d4535adabc6bd72a63f5f148c43fe559f06298bc21953c9"}, - {file = "websockets-13.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1fa082ea38d5de51dd409434edc27c0dcbd5fed2b09b9be982deb6f0508d25bc"}, - {file = "websockets-13.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4a365bcb7be554e6e1f9f3ed64016e67e2fa03d7b027a33e436aecf194febb63"}, - {file = "websockets-13.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:10a0dc7242215d794fb1918f69c6bb235f1f627aaf19e77f05336d147fce7c37"}, - {file = "websockets-13.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59197afd478545b1f73367620407b0083303569c5f2d043afe5363676f2697c9"}, - {file = "websockets-13.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d20516990d8ad557b5abeb48127b8b779b0b7e6771a265fa3e91767596d7d97"}, - {file = "websockets-13.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1a2e272d067030048e1fe41aa1ec8cfbbaabce733b3d634304fa2b19e5c897f"}, - {file = "websockets-13.0.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ad327ac80ba7ee61da85383ca8822ff808ab5ada0e4a030d66703cc025b021c4"}, - {file = "websockets-13.0.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:518f90e6dd089d34eaade01101fd8a990921c3ba18ebbe9b0165b46ebff947f0"}, - {file = "websockets-13.0.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:68264802399aed6fe9652e89761031acc734fc4c653137a5911c2bfa995d6d6d"}, - {file = "websockets-13.0.1-cp39-cp39-win32.whl", hash = "sha256:a5dc0c42ded1557cc7c3f0240b24129aefbad88af4f09346164349391dea8e58"}, - {file = "websockets-13.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:b448a0690ef43db5ef31b3a0d9aea79043882b4632cfc3eaab20105edecf6097"}, - {file = "websockets-13.0.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:faef9ec6354fe4f9a2c0bbb52fb1ff852effc897e2a4501e25eb3a47cb0a4f89"}, - {file = "websockets-13.0.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:03d3f9ba172e0a53e37fa4e636b86cc60c3ab2cfee4935e66ed1d7acaa4625ad"}, - {file = "websockets-13.0.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d450f5a7a35662a9b91a64aefa852f0c0308ee256122f5218a42f1d13577d71e"}, - {file = "websockets-13.0.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3f55b36d17ac50aa8a171b771e15fbe1561217510c8768af3d546f56c7576cdc"}, - {file = "websockets-13.0.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14b9c006cac63772b31abbcd3e3abb6228233eec966bf062e89e7fa7ae0b7333"}, - {file = "websockets-13.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:b79915a1179a91f6c5f04ece1e592e2e8a6bd245a0e45d12fd56b2b59e559a32"}, - {file = "websockets-13.0.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f40de079779acbcdbb6ed4c65af9f018f8b77c5ec4e17a4b737c05c2db554491"}, - {file = "websockets-13.0.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:80e4ba642fc87fa532bac07e5ed7e19d56940b6af6a8c61d4429be48718a380f"}, - {file = "websockets-13.0.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a02b0161c43cc9e0232711eff846569fad6ec836a7acab16b3cf97b2344c060"}, - {file = "websockets-13.0.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6aa74a45d4cdc028561a7d6ab3272c8b3018e23723100b12e58be9dfa5a24491"}, - {file = "websockets-13.0.1-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00fd961943b6c10ee6f0b1130753e50ac5dcd906130dcd77b0003c3ab797d026"}, - {file = "websockets-13.0.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:d93572720d781331fb10d3da9ca1067817d84ad1e7c31466e9f5e59965618096"}, - {file = "websockets-13.0.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:71e6e5a3a3728886caee9ab8752e8113670936a193284be9d6ad2176a137f376"}, - {file = "websockets-13.0.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:c4a6343e3b0714e80da0b0893543bf9a5b5fa71b846ae640e56e9abc6fbc4c83"}, - {file = "websockets-13.0.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a678532018e435396e37422a95e3ab87f75028ac79570ad11f5bf23cd2a7d8c"}, - {file = "websockets-13.0.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6716c087e4aa0b9260c4e579bb82e068f84faddb9bfba9906cb87726fa2e870"}, - {file = "websockets-13.0.1-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e33505534f3f673270dd67f81e73550b11de5b538c56fe04435d63c02c3f26b5"}, - {file = "websockets-13.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:acab3539a027a85d568c2573291e864333ec9d912675107d6efceb7e2be5d980"}, - {file = "websockets-13.0.1-py3-none-any.whl", hash = "sha256:b80f0c51681c517604152eb6a572f5a9378f877763231fddb883ba2f968e8817"}, - {file = "websockets-13.0.1.tar.gz", hash = "sha256:4d6ece65099411cfd9a48d13701d7438d9c34f479046b34c50ff60bb8834e43e"}, -] - -[[package]] -name = "werkzeug" -version = "3.0.4" -description = "The comprehensive WSGI web application library." -optional = false -python-versions = ">=3.8" -files = [ - {file = "werkzeug-3.0.4-py3-none-any.whl", hash = "sha256:02c9eb92b7d6c06f31a782811505d2157837cea66aaede3e217c7c27c039476c"}, - {file = "werkzeug-3.0.4.tar.gz", hash = "sha256:34f2371506b250df4d4f84bfe7b0921e4762525762bbd936614909fe25cd7306"}, + {file = "websockets-13.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f48c749857f8fb598fb890a75f540e3221d0976ed0bf879cf3c7eef34151acee"}, + {file = "websockets-13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c7e72ce6bda6fb9409cc1e8164dd41d7c91466fb599eb047cfda72fe758a34a7"}, + {file = "websockets-13.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f779498eeec470295a2b1a5d97aa1bc9814ecd25e1eb637bd9d1c73a327387f6"}, + {file = "websockets-13.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4676df3fe46956fbb0437d8800cd5f2b6d41143b6e7e842e60554398432cf29b"}, + {file = "websockets-13.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7affedeb43a70351bb811dadf49493c9cfd1ed94c9c70095fd177e9cc1541fa"}, + {file = "websockets-13.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1971e62d2caa443e57588e1d82d15f663b29ff9dfe7446d9964a4b6f12c1e700"}, + {file = "websockets-13.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5f2e75431f8dc4a47f31565a6e1355fb4f2ecaa99d6b89737527ea917066e26c"}, + {file = "websockets-13.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:58cf7e75dbf7e566088b07e36ea2e3e2bd5676e22216e4cad108d4df4a7402a0"}, + {file = "websockets-13.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c90d6dec6be2c7d03378a574de87af9b1efea77d0c52a8301dd831ece938452f"}, + {file = "websockets-13.1-cp310-cp310-win32.whl", hash = "sha256:730f42125ccb14602f455155084f978bd9e8e57e89b569b4d7f0f0c17a448ffe"}, + {file = "websockets-13.1-cp310-cp310-win_amd64.whl", hash = "sha256:5993260f483d05a9737073be197371940c01b257cc45ae3f1d5d7adb371b266a"}, + {file = "websockets-13.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:61fc0dfcda609cda0fc9fe7977694c0c59cf9d749fbb17f4e9483929e3c48a19"}, + {file = "websockets-13.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ceec59f59d092c5007e815def4ebb80c2de330e9588e101cf8bd94c143ec78a5"}, + {file = "websockets-13.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c1dca61c6db1166c48b95198c0b7d9c990b30c756fc2923cc66f68d17dc558fd"}, + {file = "websockets-13.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:308e20f22c2c77f3f39caca508e765f8725020b84aa963474e18c59accbf4c02"}, + {file = "websockets-13.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62d516c325e6540e8a57b94abefc3459d7dab8ce52ac75c96cad5549e187e3a7"}, + {file = "websockets-13.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87c6e35319b46b99e168eb98472d6c7d8634ee37750d7693656dc766395df096"}, + {file = "websockets-13.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5f9fee94ebafbc3117c30be1844ed01a3b177bb6e39088bc6b2fa1dc15572084"}, + {file = "websockets-13.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7c1e90228c2f5cdde263253fa5db63e6653f1c00e7ec64108065a0b9713fa1b3"}, + {file = "websockets-13.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6548f29b0e401eea2b967b2fdc1c7c7b5ebb3eeb470ed23a54cd45ef078a0db9"}, + {file = "websockets-13.1-cp311-cp311-win32.whl", hash = "sha256:c11d4d16e133f6df8916cc5b7e3e96ee4c44c936717d684a94f48f82edb7c92f"}, + {file = "websockets-13.1-cp311-cp311-win_amd64.whl", hash = "sha256:d04f13a1d75cb2b8382bdc16ae6fa58c97337253826dfe136195b7f89f661557"}, + {file = "websockets-13.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9d75baf00138f80b48f1eac72ad1535aac0b6461265a0bcad391fc5aba875cfc"}, + {file = "websockets-13.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9b6f347deb3dcfbfde1c20baa21c2ac0751afaa73e64e5b693bb2b848efeaa49"}, + {file = "websockets-13.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de58647e3f9c42f13f90ac7e5f58900c80a39019848c5547bc691693098ae1bd"}, + {file = "websockets-13.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1b54689e38d1279a51d11e3467dd2f3a50f5f2e879012ce8f2d6943f00e83f0"}, + {file = "websockets-13.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf1781ef73c073e6b0f90af841aaf98501f975d306bbf6221683dd594ccc52b6"}, + {file = "websockets-13.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d23b88b9388ed85c6faf0e74d8dec4f4d3baf3ecf20a65a47b836d56260d4b9"}, + {file = "websockets-13.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3c78383585f47ccb0fcf186dcb8a43f5438bd7d8f47d69e0b56f71bf431a0a68"}, + {file = "websockets-13.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d6d300f8ec35c24025ceb9b9019ae9040c1ab2f01cddc2bcc0b518af31c75c14"}, + {file = "websockets-13.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a9dcaf8b0cc72a392760bb8755922c03e17a5a54e08cca58e8b74f6902b433cf"}, + {file = "websockets-13.1-cp312-cp312-win32.whl", hash = "sha256:2f85cf4f2a1ba8f602298a853cec8526c2ca42a9a4b947ec236eaedb8f2dc80c"}, + {file = "websockets-13.1-cp312-cp312-win_amd64.whl", hash = "sha256:38377f8b0cdeee97c552d20cf1865695fcd56aba155ad1b4ca8779a5b6ef4ac3"}, + {file = "websockets-13.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a9ab1e71d3d2e54a0aa646ab6d4eebfaa5f416fe78dfe4da2839525dc5d765c6"}, + {file = "websockets-13.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b9d7439d7fab4dce00570bb906875734df13d9faa4b48e261c440a5fec6d9708"}, + {file = "websockets-13.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:327b74e915cf13c5931334c61e1a41040e365d380f812513a255aa804b183418"}, + {file = "websockets-13.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:325b1ccdbf5e5725fdcb1b0e9ad4d2545056479d0eee392c291c1bf76206435a"}, + {file = "websockets-13.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:346bee67a65f189e0e33f520f253d5147ab76ae42493804319b5716e46dddf0f"}, + {file = "websockets-13.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91a0fa841646320ec0d3accdff5b757b06e2e5c86ba32af2e0815c96c7a603c5"}, + {file = "websockets-13.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:18503d2c5f3943e93819238bf20df71982d193f73dcecd26c94514f417f6b135"}, + {file = "websockets-13.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:a9cd1af7e18e5221d2878378fbc287a14cd527fdd5939ed56a18df8a31136bb2"}, + {file = "websockets-13.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:70c5be9f416aa72aab7a2a76c90ae0a4fe2755c1816c153c1a2bcc3333ce4ce6"}, + {file = "websockets-13.1-cp313-cp313-win32.whl", hash = "sha256:624459daabeb310d3815b276c1adef475b3e6804abaf2d9d2c061c319f7f187d"}, + {file = "websockets-13.1-cp313-cp313-win_amd64.whl", hash = "sha256:c518e84bb59c2baae725accd355c8dc517b4a3ed8db88b4bc93c78dae2974bf2"}, + {file = "websockets-13.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:c7934fd0e920e70468e676fe7f1b7261c1efa0d6c037c6722278ca0228ad9d0d"}, + {file = "websockets-13.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:149e622dc48c10ccc3d2760e5f36753db9cacf3ad7bc7bbbfd7d9c819e286f23"}, + {file = "websockets-13.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a569eb1b05d72f9bce2ebd28a1ce2054311b66677fcd46cf36204ad23acead8c"}, + {file = "websockets-13.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95df24ca1e1bd93bbca51d94dd049a984609687cb2fb08a7f2c56ac84e9816ea"}, + {file = "websockets-13.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8dbb1bf0c0a4ae8b40bdc9be7f644e2f3fb4e8a9aca7145bfa510d4a374eeb7"}, + {file = "websockets-13.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:035233b7531fb92a76beefcbf479504db8c72eb3bff41da55aecce3a0f729e54"}, + {file = "websockets-13.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:e4450fc83a3df53dec45922b576e91e94f5578d06436871dce3a6be38e40f5db"}, + {file = "websockets-13.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:463e1c6ec853202dd3657f156123d6b4dad0c546ea2e2e38be2b3f7c5b8e7295"}, + {file = "websockets-13.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:6d6855bbe70119872c05107e38fbc7f96b1d8cb047d95c2c50869a46c65a8e96"}, + {file = "websockets-13.1-cp38-cp38-win32.whl", hash = "sha256:204e5107f43095012b00f1451374693267adbb832d29966a01ecc4ce1db26faf"}, + {file = "websockets-13.1-cp38-cp38-win_amd64.whl", hash = "sha256:485307243237328c022bc908b90e4457d0daa8b5cf4b3723fd3c4a8012fce4c6"}, + {file = "websockets-13.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9b37c184f8b976f0c0a231a5f3d6efe10807d41ccbe4488df8c74174805eea7d"}, + {file = "websockets-13.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:163e7277e1a0bd9fb3c8842a71661ad19c6aa7bb3d6678dc7f89b17fbcc4aeb7"}, + {file = "websockets-13.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4b889dbd1342820cc210ba44307cf75ae5f2f96226c0038094455a96e64fb07a"}, + {file = "websockets-13.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:586a356928692c1fed0eca68b4d1c2cbbd1ca2acf2ac7e7ebd3b9052582deefa"}, + {file = "websockets-13.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7bd6abf1e070a6b72bfeb71049d6ad286852e285f146682bf30d0296f5fbadfa"}, + {file = "websockets-13.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2aad13a200e5934f5a6767492fb07151e1de1d6079c003ab31e1823733ae79"}, + {file = "websockets-13.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:df01aea34b6e9e33572c35cd16bae5a47785e7d5c8cb2b54b2acdb9678315a17"}, + {file = "websockets-13.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e54affdeb21026329fb0744ad187cf812f7d3c2aa702a5edb562b325191fcab6"}, + {file = "websockets-13.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9ef8aa8bdbac47f4968a5d66462a2a0935d044bf35c0e5a8af152d58516dbeb5"}, + {file = "websockets-13.1-cp39-cp39-win32.whl", hash = "sha256:deeb929efe52bed518f6eb2ddc00cc496366a14c726005726ad62c2dd9017a3c"}, + {file = "websockets-13.1-cp39-cp39-win_amd64.whl", hash = "sha256:7c65ffa900e7cc958cd088b9a9157a8141c991f8c53d11087e6fb7277a03f81d"}, + {file = "websockets-13.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5dd6da9bec02735931fccec99d97c29f47cc61f644264eb995ad6c0c27667238"}, + {file = "websockets-13.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:2510c09d8e8df777177ee3d40cd35450dc169a81e747455cc4197e63f7e7bfe5"}, + {file = "websockets-13.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1c3cf67185543730888b20682fb186fc8d0fa6f07ccc3ef4390831ab4b388d9"}, + {file = "websockets-13.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bcc03c8b72267e97b49149e4863d57c2d77f13fae12066622dc78fe322490fe6"}, + {file = "websockets-13.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:004280a140f220c812e65f36944a9ca92d766b6cc4560be652a0a3883a79ed8a"}, + {file = "websockets-13.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e2620453c075abeb0daa949a292e19f56de518988e079c36478bacf9546ced23"}, + {file = "websockets-13.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9156c45750b37337f7b0b00e6248991a047be4aa44554c9886fe6bdd605aab3b"}, + {file = "websockets-13.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:80c421e07973a89fbdd93e6f2003c17d20b69010458d3a8e37fb47874bd67d51"}, + {file = "websockets-13.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82d0ba76371769d6a4e56f7e83bb8e81846d17a6190971e38b5de108bde9b0d7"}, + {file = "websockets-13.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e9875a0143f07d74dc5e1ded1c4581f0d9f7ab86c78994e2ed9e95050073c94d"}, + {file = "websockets-13.1-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a11e38ad8922c7961447f35c7b17bffa15de4d17c70abd07bfbe12d6faa3e027"}, + {file = "websockets-13.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:4059f790b6ae8768471cddb65d3c4fe4792b0ab48e154c9f0a04cefaabcd5978"}, + {file = "websockets-13.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:25c35bf84bf7c7369d247f0b8cfa157f989862c49104c5cf85cb5436a641d93e"}, + {file = "websockets-13.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:83f91d8a9bb404b8c2c41a707ac7f7f75b9442a0a876df295de27251a856ad09"}, + {file = "websockets-13.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a43cfdcddd07f4ca2b1afb459824dd3c6d53a51410636a2c7fc97b9a8cf4842"}, + {file = "websockets-13.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48a2ef1381632a2f0cb4efeff34efa97901c9fbc118e01951ad7cfc10601a9bb"}, + {file = "websockets-13.1-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:459bf774c754c35dbb487360b12c5727adab887f1622b8aed5755880a21c4a20"}, + {file = "websockets-13.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:95858ca14a9f6fa8413d29e0a585b31b278388aa775b8a81fa24830123874678"}, + {file = "websockets-13.1-py3-none-any.whl", hash = "sha256:a9a396a6ad26130cdae92ae10c36af09d9bfe6cafe69670fd3b6da9b07b4044f"}, + {file = "websockets-13.1.tar.gz", hash = "sha256:a3b3366087c1bc0a2795111edcadddb8b3b59509d5db5d7ea3fdd69f954a8878"}, ] -[package.dependencies] -MarkupSafe = ">=2.1.1" - -[package.extras] -watchdog = ["watchdog (>=2.3)"] - [[package]] name = "wrapt" version = "1.16.0" @@ -5007,103 +5664,103 @@ dev = ["doc8", "flake8", "flake8-import-order", "rstcheck[sphinx]", "sphinx"] [[package]] name = "yarl" -version = "1.11.0" +version = "1.11.1" description = "Yet another URL library" optional = false python-versions = ">=3.8" files = [ - {file = "yarl-1.11.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0a657db1b9982f3dac0e360614d0e8945d2873da6e681fb7fca23ef1c3eb37f8"}, - {file = "yarl-1.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:65a1a05efca52b102691e64db5fcf973030a1c88fee393804ff91f99c95a6e74"}, - {file = "yarl-1.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f4cb417d380e2d77961eecec75aaaf6f7ab14e6de26eb3a498f498029a6556a1"}, - {file = "yarl-1.11.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8aee7c8378c6aa3103b99d1eb9995268ef730fa9f88ea68b9eee4341e204eec9"}, - {file = "yarl-1.11.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84624db40e2358cfd5cf2558b1aaffd93366d27ee32228a97785f2ec87d44a17"}, - {file = "yarl-1.11.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a596bb15e036952549871a4ccd2205679902dc7f241e3ced6b2ab2e44c55795"}, - {file = "yarl-1.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9d4d2cc4b076c8ad0175a15ee9482a387b3303c97d4b71062db7356b2ac04c7"}, - {file = "yarl-1.11.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:25f8bc849004122591104793a576e9c747b0e5d9486d6a30225521b817255748"}, - {file = "yarl-1.11.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e38176a559edde0cfff4b663791a007a5f9f90c73aee1d6f7ddbcf6bfb7287b3"}, - {file = "yarl-1.11.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:706ac0f77b45e9e0278ec6c98929764e119d3ce3136792b6475e7ae961da53ec"}, - {file = "yarl-1.11.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:48bac099586cf75ae5837b0ac17a674450d01f451f38afcb02acfc940110b60b"}, - {file = "yarl-1.11.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:540fd5f62fe21f3d1d9efe8af5c4d9dbbb184ce03ce95acb0289500e46215dd2"}, - {file = "yarl-1.11.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:05ab59db0bb64e847972373c5cda8924e6605480f6b13cc04573fa0d87bfc637"}, - {file = "yarl-1.11.0-cp310-cp310-win32.whl", hash = "sha256:ddab47748933ac9cf5f29d6e9e2e2060cff40b2751d02c55129661ea4e577152"}, - {file = "yarl-1.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:976d02274e6d88b24c7131e7b26a083412b2592f2bbcef53d3b00b2508cad26c"}, - {file = "yarl-1.11.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:39e3087e1ef70862de81e22af9eb299faee580f41673ef92829949022791b521"}, - {file = "yarl-1.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7fd535cc41b81a566ad347081b671ab5c7e5f5b6a15526d85b4e748baf065cf0"}, - {file = "yarl-1.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f7cc02d8e9a612174869f4b983f159e87659096f7e2dc1fe9effd9902e408739"}, - {file = "yarl-1.11.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30f391ccf4b1b1e0ba4880075ba337d41a619a5350f67053927f67ebe764bf44"}, - {file = "yarl-1.11.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c19a0d95943bb2c914b4e71043803be34bc75c08c4a6ca232bdc649a1e9ef1b"}, - {file = "yarl-1.11.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ead4d89eade0e09b8ef97877664abb0e2e8704787db5564f83658fdee5c36497"}, - {file = "yarl-1.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:195f7791bc23d5f2480efe53f935daf8a61661000dfbfbdd70dbd06397594fff"}, - {file = "yarl-1.11.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01a7905e662665ca8e058635377522bc3c98bdb873be761ff42c86eb72b03914"}, - {file = "yarl-1.11.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:53c80b1927b75aed208d7fd965a3a705dc8c1db4d50b9112418fa0f7784363e6"}, - {file = "yarl-1.11.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:11af21bbf807688d49b7d4915bb28cbc2e3aa028a2ee194738477eabcc413c65"}, - {file = "yarl-1.11.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:732d56da00ea7a5da4f0d15adbbd22dcb37da7825510aafde40112e53f6baa52"}, - {file = "yarl-1.11.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:7bd54d79025b59d1dc5fb26a09734d6a9cc651a04bc381966ed264b28331a168"}, - {file = "yarl-1.11.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:aacd62ff67efd54cb18cea2aa7ae4fb83cfbca19a07055d4777266b70561defe"}, - {file = "yarl-1.11.0-cp311-cp311-win32.whl", hash = "sha256:68e14ae71e5b51c8282ae5db53ccb3baffc40e1551370a8a2361f1c1d8a0bf8c"}, - {file = "yarl-1.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:3ade2265716667b6bd4123d6f684b5f7cf4a8d83dcf1d5581ac44643466bb00a"}, - {file = "yarl-1.11.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:6e73dab98e3c3b5441720153e72a5f28e717aac2d22f1ec4b08ef33417d9987e"}, - {file = "yarl-1.11.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4a0d090d296ced05edfe29c6ff34869412fa6a97d0928c12b00939c4842884cd"}, - {file = "yarl-1.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d29e446cfb0a82d3df7745968b9fa286665a9be8b4d68de46bcc32d917cb218e"}, - {file = "yarl-1.11.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c8dc0efcf8266ecfe057b95e01f43eb62516196a4bbf3918fd1dcb8d0dc0dff"}, - {file = "yarl-1.11.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:202f5ec49ff163dcc767426deb55020a28078e61d6bbe1f80331d92bca53b236"}, - {file = "yarl-1.11.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8055b0d78ce1cafa657c4b455e22661e8d3b2834de66a0753c3567da47fcc4aa"}, - {file = "yarl-1.11.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60ed3c7f64e820959d7f682ec2f559b4f4df723dc09df619d269853a4214a4b4"}, - {file = "yarl-1.11.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2371510367d39d74997acfdcd1dead17938c79c99365482821627f7838a8eba0"}, - {file = "yarl-1.11.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e24bb6a8be89ccc3ce8c47e8940fdfcb7429e9efbf65ce6fa3e7d122fcf0bcf0"}, - {file = "yarl-1.11.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:18ec42da256cfcb9b4cd5d253e04c291f69911a5228d1438a7d431c15ba0ae40"}, - {file = "yarl-1.11.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:418eeb8f228ea36c368bf6782ebd6016ecebfb1a8b90145ef6726ffcbba65ef8"}, - {file = "yarl-1.11.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:07e8cfb1dd7669a129f8fd5df1da65efa73aea77582bde2a3a837412e2863543"}, - {file = "yarl-1.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3c458483711d393dad51340505c3fab3194748fd06bab311d2f8b5b7a7349e9a"}, - {file = "yarl-1.11.0-cp312-cp312-win32.whl", hash = "sha256:5b008c3127382503e7a1e12b4c3a3236e3dd833a4c62a066f4a0fbd650c655d2"}, - {file = "yarl-1.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:bc94be7472b9f88d7441340534a3ecae05c86ccfec7ba75ce5b6e4778b2bfc6e"}, - {file = "yarl-1.11.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a45e51ba3777031e0b20c1e7ab59114ed4e1884b3c1db48962c1d8d08aefb418"}, - {file = "yarl-1.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:765128029218eade3a01187cdd7f375977cc827505ed31828196c8ae9b622928"}, - {file = "yarl-1.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:2516e238daf0339c8ac4dfab9d7cda9afad652ff073517f200d653d5d8371f7e"}, - {file = "yarl-1.11.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d10be62bee117f05b1ad75a6c2538ca9e5367342dc8a4f3c206c87dadbc1189c"}, - {file = "yarl-1.11.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50ceaeda771ee3e382291168c90c7ede62b63ecf3e181024bcfeb35c0ea6c84f"}, - {file = "yarl-1.11.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3a601c99fc20fd0eea84e7bc0dc9e7f196f55a0ded67242d724988c754295538"}, - {file = "yarl-1.11.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42ff79371614764fc0a4ab8eaba9adb493bf9ad856e2a4664f6c754fc907a903"}, - {file = "yarl-1.11.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:93fca4c9f88c17ead902b3f3285b2d039fc8f26d117e1441973ba64315109b54"}, - {file = "yarl-1.11.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e7dddf5f41395c84fc59e0ed5493b24bfeb39fb04823e880b52c8c55085d4695"}, - {file = "yarl-1.11.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ea501ea07e14ba6364ff2621bfc8b2381e5b1e10353927fa9a607057fd2b98e5"}, - {file = "yarl-1.11.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:a4f7e470f2c9c8b8774a5bda72adfb8e9dc4ec32311fe9bdaa4921e36cf6659b"}, - {file = "yarl-1.11.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:361fdb3993431157302b7104d525092b5df4d7d346df5a5ffeee2d1ca8e0d15b"}, - {file = "yarl-1.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e300eaf5e0329ad31b3d53e2f3d26b4b6dff1217207c6ab1d4212967b54b2185"}, - {file = "yarl-1.11.0-cp313-cp313-win32.whl", hash = "sha256:f1e2d4ce72e06e38a16da3e9c24a0520dbc19018a69ef6ed57b6b38527cb275c"}, - {file = "yarl-1.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:fa9de2f87be58f714a230bd1f3ef3aad1ed65c9931146e3fc55f85fcbe6bacc3"}, - {file = "yarl-1.11.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:24da0b38274727fe9266d09229987e7f0efdb97beb94c0bb2d327d65f112e78d"}, - {file = "yarl-1.11.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0310eb2e63872de66047e05ad9982f2e53ad6405dc42fa60d7cc670bf6ca8aa8"}, - {file = "yarl-1.11.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:52433604340a4ab3d1f32281c6eb9ad9b47c99435b4212f763121bf7348c8c00"}, - {file = "yarl-1.11.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98e2eb182d59f0845a79434003f94b4f61cd69465248f9388c2e5bf2191c9f7f"}, - {file = "yarl-1.11.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b3dd10f0fe0e0f659926c1da791de5bef05fd48974ad74618c9168e302e2b7cc"}, - {file = "yarl-1.11.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:121d3798e4bb35a4321b2422cb887f80ea39f94bf52f0eb5cb2c168bb0043c9b"}, - {file = "yarl-1.11.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8bbac56c80610dd659ace534765d7bcd2488f6600023f6984f35108b2b3f4f0"}, - {file = "yarl-1.11.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:79d420399f0e82e302236a762d8b8ceec89761ce3b30c83ac1d4d6e29f811444"}, - {file = "yarl-1.11.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:03a726fb50588307dfe1d233b67535d493fb0bb157bdbfda6bb34e04189f2f57"}, - {file = "yarl-1.11.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:9057f5de2fade7440e6db358913bc7ae8de43ba72c83cf95420a1fc1a6c6b59e"}, - {file = "yarl-1.11.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:6471d747d0ac8059895e66d32ca8630c8db5b572ca7763150d0927eaa257df67"}, - {file = "yarl-1.11.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:d97cb22ad380850754fa16ef8d490d9340d8573d81f73429f3975e8e87db0586"}, - {file = "yarl-1.11.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:fe78dec8caeda1e7b353cbd8aa0cc5a5bc182b22998d64ec8fa9ee59c898ab3b"}, - {file = "yarl-1.11.0-cp38-cp38-win32.whl", hash = "sha256:7ff371002fbbb79613269d76a2932c99979dac15fac30107064ef70d25f35474"}, - {file = "yarl-1.11.0-cp38-cp38-win_amd64.whl", hash = "sha256:4fa9d762eee63eed767895d68b994c58e29f809292a4d0fca483e9cc6fdc22c8"}, - {file = "yarl-1.11.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4ae63bc65e5bf8843bd1eca46e75eaa9eb157e0312fb362123181512892daad8"}, - {file = "yarl-1.11.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3d1bd3262e00043907e0a6d7d4f7b7a4815281acc25699a2384552870c79f1f0"}, - {file = "yarl-1.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0c58656c2e0b41b5d325130b8da4f8e216aad10029e7de5c523a6be25faa9fe8"}, - {file = "yarl-1.11.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9425c333575fce5e0fb414b766492c6ba4aa335ef910a7540dbdefe58a78232e"}, - {file = "yarl-1.11.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9dc66e2420e1e282105071934883bbb9c37c16901b5b8aa0a8aee370b477eac6"}, - {file = "yarl-1.11.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2949067359d1ef5bf3228c7f1deb102c209832a13df5419239f99449bc1d3fa9"}, - {file = "yarl-1.11.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c006fe73f851cf20b9986b3b4cc15239795bd5da9c3fda76bb3e043da5bec4ff"}, - {file = "yarl-1.11.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:969ad4ee3892e893471b6572bbf2bbb091f93e7c81de25d6b3a5c0a5126e5ccb"}, - {file = "yarl-1.11.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:c9fbe9dc6ee8bfe1af34137e3add6f0e49799dd5467dd6af189d27616879161e"}, - {file = "yarl-1.11.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:69a45c711fea9b783b592a75f26f6dc59b2e4a923b97bf6eec357566fcb1d922"}, - {file = "yarl-1.11.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:1a29b82c42a7791ffe53ee6dfbf29acc61ea7ec05643dcacc50510ed6187b897"}, - {file = "yarl-1.11.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ed0c090f00c3fc024f7b0799cab9dd7c419fcd8f1a00634d1f9952bab7e7bfb2"}, - {file = "yarl-1.11.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:31df9d9b3fe6e15decee629fc7976a5fb21eaa39e290f60e57e1d422827194c6"}, - {file = "yarl-1.11.0-cp39-cp39-win32.whl", hash = "sha256:fcb7c36ba8b663a5900e6d40533f0e698ba0f38f744aad5410d4e38129e41a70"}, - {file = "yarl-1.11.0-cp39-cp39-win_amd64.whl", hash = "sha256:c6c0d640bad721834a737e25267fb71d296684ada21ca7d5ad2e63da7b73f1b7"}, - {file = "yarl-1.11.0-py3-none-any.whl", hash = "sha256:03717a6627e55934b2a1d9caf24f299b461a2e8d048a90920f42ad5c20ae1b82"}, - {file = "yarl-1.11.0.tar.gz", hash = "sha256:f86f4f4a57a29ef08fa70c4667d04c5e3ba513500da95586208b285437cb9592"}, + {file = "yarl-1.11.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:400cd42185f92de559d29eeb529e71d80dfbd2f45c36844914a4a34297ca6f00"}, + {file = "yarl-1.11.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8258c86f47e080a258993eed877d579c71da7bda26af86ce6c2d2d072c11320d"}, + {file = "yarl-1.11.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2164cd9725092761fed26f299e3f276bb4b537ca58e6ff6b252eae9631b5c96e"}, + {file = "yarl-1.11.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08ea567c16f140af8ddc7cb58e27e9138a1386e3e6e53982abaa6f2377b38cc"}, + {file = "yarl-1.11.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:768ecc550096b028754ea28bf90fde071c379c62c43afa574edc6f33ee5daaec"}, + {file = "yarl-1.11.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2909fa3a7d249ef64eeb2faa04b7957e34fefb6ec9966506312349ed8a7e77bf"}, + {file = "yarl-1.11.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01a8697ec24f17c349c4f655763c4db70eebc56a5f82995e5e26e837c6eb0e49"}, + {file = "yarl-1.11.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e286580b6511aac7c3268a78cdb861ec739d3e5a2a53b4809faef6b49778eaff"}, + {file = "yarl-1.11.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4179522dc0305c3fc9782549175c8e8849252fefeb077c92a73889ccbcd508ad"}, + {file = "yarl-1.11.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:27fcb271a41b746bd0e2a92182df507e1c204759f460ff784ca614e12dd85145"}, + {file = "yarl-1.11.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f61db3b7e870914dbd9434b560075e0366771eecbe6d2b5561f5bc7485f39efd"}, + {file = "yarl-1.11.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:c92261eb2ad367629dc437536463dc934030c9e7caca861cc51990fe6c565f26"}, + {file = "yarl-1.11.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d95b52fbef190ca87d8c42f49e314eace4fc52070f3dfa5f87a6594b0c1c6e46"}, + {file = "yarl-1.11.1-cp310-cp310-win32.whl", hash = "sha256:489fa8bde4f1244ad6c5f6d11bb33e09cf0d1d0367edb197619c3e3fc06f3d91"}, + {file = "yarl-1.11.1-cp310-cp310-win_amd64.whl", hash = "sha256:476e20c433b356e16e9a141449f25161e6b69984fb4cdbd7cd4bd54c17844998"}, + {file = "yarl-1.11.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:946eedc12895873891aaceb39bceb484b4977f70373e0122da483f6c38faaa68"}, + {file = "yarl-1.11.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:21a7c12321436b066c11ec19c7e3cb9aec18884fe0d5b25d03d756a9e654edfe"}, + {file = "yarl-1.11.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c35f493b867912f6fda721a59cc7c4766d382040bdf1ddaeeaa7fa4d072f4675"}, + {file = "yarl-1.11.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25861303e0be76b60fddc1250ec5986c42f0a5c0c50ff57cc30b1be199c00e63"}, + {file = "yarl-1.11.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4b53f73077e839b3f89c992223f15b1d2ab314bdbdf502afdc7bb18e95eae27"}, + {file = "yarl-1.11.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:327c724b01b8641a1bf1ab3b232fb638706e50f76c0b5bf16051ab65c868fac5"}, + {file = "yarl-1.11.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4307d9a3417eea87715c9736d050c83e8c1904e9b7aada6ce61b46361b733d92"}, + {file = "yarl-1.11.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48a28bed68ab8fb7e380775f0029a079f08a17799cb3387a65d14ace16c12e2b"}, + {file = "yarl-1.11.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:067b961853c8e62725ff2893226fef3d0da060656a9827f3f520fb1d19b2b68a"}, + {file = "yarl-1.11.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:8215f6f21394d1f46e222abeb06316e77ef328d628f593502d8fc2a9117bde83"}, + {file = "yarl-1.11.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:498442e3af2a860a663baa14fbf23fb04b0dd758039c0e7c8f91cb9279799bff"}, + {file = "yarl-1.11.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:69721b8effdb588cb055cc22f7c5105ca6fdaa5aeb3ea09021d517882c4a904c"}, + {file = "yarl-1.11.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1e969fa4c1e0b1a391f3fcbcb9ec31e84440253325b534519be0d28f4b6b533e"}, + {file = "yarl-1.11.1-cp311-cp311-win32.whl", hash = "sha256:7d51324a04fc4b0e097ff8a153e9276c2593106a811704025bbc1d6916f45ca6"}, + {file = "yarl-1.11.1-cp311-cp311-win_amd64.whl", hash = "sha256:15061ce6584ece023457fb8b7a7a69ec40bf7114d781a8c4f5dcd68e28b5c53b"}, + {file = "yarl-1.11.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:a4264515f9117be204935cd230fb2a052dd3792789cc94c101c535d349b3dab0"}, + {file = "yarl-1.11.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f41fa79114a1d2eddb5eea7b912d6160508f57440bd302ce96eaa384914cd265"}, + {file = "yarl-1.11.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:02da8759b47d964f9173c8675710720b468aa1c1693be0c9c64abb9d8d9a4867"}, + {file = "yarl-1.11.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9361628f28f48dcf8b2f528420d4d68102f593f9c2e592bfc842f5fb337e44fd"}, + {file = "yarl-1.11.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b91044952da03b6f95fdba398d7993dd983b64d3c31c358a4c89e3c19b6f7aef"}, + {file = "yarl-1.11.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:74db2ef03b442276d25951749a803ddb6e270d02dda1d1c556f6ae595a0d76a8"}, + {file = "yarl-1.11.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e975a2211952a8a083d1b9d9ba26472981ae338e720b419eb50535de3c02870"}, + {file = "yarl-1.11.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8aef97ba1dd2138112890ef848e17d8526fe80b21f743b4ee65947ea184f07a2"}, + {file = "yarl-1.11.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a7915ea49b0c113641dc4d9338efa9bd66b6a9a485ffe75b9907e8573ca94b84"}, + {file = "yarl-1.11.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:504cf0d4c5e4579a51261d6091267f9fd997ef58558c4ffa7a3e1460bd2336fa"}, + {file = "yarl-1.11.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:3de5292f9f0ee285e6bd168b2a77b2a00d74cbcfa420ed078456d3023d2f6dff"}, + {file = "yarl-1.11.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a34e1e30f1774fa35d37202bbeae62423e9a79d78d0874e5556a593479fdf239"}, + {file = "yarl-1.11.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:66b63c504d2ca43bf7221a1f72fbe981ff56ecb39004c70a94485d13e37ebf45"}, + {file = "yarl-1.11.1-cp312-cp312-win32.whl", hash = "sha256:a28b70c9e2213de425d9cba5ab2e7f7a1c8ca23a99c4b5159bf77b9c31251447"}, + {file = "yarl-1.11.1-cp312-cp312-win_amd64.whl", hash = "sha256:17b5a386d0d36fb828e2fb3ef08c8829c1ebf977eef88e5367d1c8c94b454639"}, + {file = "yarl-1.11.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:1fa2e7a406fbd45b61b4433e3aa254a2c3e14c4b3186f6e952d08a730807fa0c"}, + {file = "yarl-1.11.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:750f656832d7d3cb0c76be137ee79405cc17e792f31e0a01eee390e383b2936e"}, + {file = "yarl-1.11.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0b8486f322d8f6a38539136a22c55f94d269addb24db5cb6f61adc61eabc9d93"}, + {file = "yarl-1.11.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3fce4da3703ee6048ad4138fe74619c50874afe98b1ad87b2698ef95bf92c96d"}, + {file = "yarl-1.11.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ed653638ef669e0efc6fe2acb792275cb419bf9cb5c5049399f3556995f23c7"}, + {file = "yarl-1.11.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18ac56c9dd70941ecad42b5a906820824ca72ff84ad6fa18db33c2537ae2e089"}, + {file = "yarl-1.11.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:688654f8507464745ab563b041d1fb7dab5d9912ca6b06e61d1c4708366832f5"}, + {file = "yarl-1.11.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4973eac1e2ff63cf187073cd4e1f1148dcd119314ab79b88e1b3fad74a18c9d5"}, + {file = "yarl-1.11.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:964a428132227edff96d6f3cf261573cb0f1a60c9a764ce28cda9525f18f7786"}, + {file = "yarl-1.11.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:6d23754b9939cbab02c63434776df1170e43b09c6a517585c7ce2b3d449b7318"}, + {file = "yarl-1.11.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c2dc4250fe94d8cd864d66018f8344d4af50e3758e9d725e94fecfa27588ff82"}, + {file = "yarl-1.11.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09696438cb43ea6f9492ef237761b043f9179f455f405279e609f2bc9100212a"}, + {file = "yarl-1.11.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:999bfee0a5b7385a0af5ffb606393509cfde70ecca4f01c36985be6d33e336da"}, + {file = "yarl-1.11.1-cp313-cp313-win32.whl", hash = "sha256:ce928c9c6409c79e10f39604a7e214b3cb69552952fbda8d836c052832e6a979"}, + {file = "yarl-1.11.1-cp313-cp313-win_amd64.whl", hash = "sha256:501c503eed2bb306638ccb60c174f856cc3246c861829ff40eaa80e2f0330367"}, + {file = "yarl-1.11.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:dae7bd0daeb33aa3e79e72877d3d51052e8b19c9025ecf0374f542ea8ec120e4"}, + {file = "yarl-1.11.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3ff6b1617aa39279fe18a76c8d165469c48b159931d9b48239065767ee455b2b"}, + {file = "yarl-1.11.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3257978c870728a52dcce8c2902bf01f6c53b65094b457bf87b2644ee6238ddc"}, + {file = "yarl-1.11.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f351fa31234699d6084ff98283cb1e852270fe9e250a3b3bf7804eb493bd937"}, + {file = "yarl-1.11.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8aef1b64da41d18026632d99a06b3fefe1d08e85dd81d849fa7c96301ed22f1b"}, + {file = "yarl-1.11.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7175a87ab8f7fbde37160a15e58e138ba3b2b0e05492d7351314a250d61b1591"}, + {file = "yarl-1.11.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba444bdd4caa2a94456ef67a2f383710928820dd0117aae6650a4d17029fa25e"}, + {file = "yarl-1.11.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0ea9682124fc062e3d931c6911934a678cb28453f957ddccf51f568c2f2b5e05"}, + {file = "yarl-1.11.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:8418c053aeb236b20b0ab8fa6bacfc2feaaf7d4683dd96528610989c99723d5f"}, + {file = "yarl-1.11.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:61a5f2c14d0a1adfdd82258f756b23a550c13ba4c86c84106be4c111a3a4e413"}, + {file = "yarl-1.11.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:f3a6d90cab0bdf07df8f176eae3a07127daafcf7457b997b2bf46776da2c7eb7"}, + {file = "yarl-1.11.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:077da604852be488c9a05a524068cdae1e972b7dc02438161c32420fb4ec5e14"}, + {file = "yarl-1.11.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:15439f3c5c72686b6c3ff235279630d08936ace67d0fe5c8d5bbc3ef06f5a420"}, + {file = "yarl-1.11.1-cp38-cp38-win32.whl", hash = "sha256:238a21849dd7554cb4d25a14ffbfa0ef380bb7ba201f45b144a14454a72ffa5a"}, + {file = "yarl-1.11.1-cp38-cp38-win_amd64.whl", hash = "sha256:67459cf8cf31da0e2cbdb4b040507e535d25cfbb1604ca76396a3a66b8ba37a6"}, + {file = "yarl-1.11.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:884eab2ce97cbaf89f264372eae58388862c33c4f551c15680dd80f53c89a269"}, + {file = "yarl-1.11.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8a336eaa7ee7e87cdece3cedb395c9657d227bfceb6781295cf56abcd3386a26"}, + {file = "yarl-1.11.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:87f020d010ba80a247c4abc335fc13421037800ca20b42af5ae40e5fd75e7909"}, + {file = "yarl-1.11.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:637c7ddb585a62d4469f843dac221f23eec3cbad31693b23abbc2c366ad41ff4"}, + {file = "yarl-1.11.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:48dfd117ab93f0129084577a07287376cc69c08138694396f305636e229caa1a"}, + {file = "yarl-1.11.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75e0ae31fb5ccab6eda09ba1494e87eb226dcbd2372dae96b87800e1dcc98804"}, + {file = "yarl-1.11.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f46f81501160c28d0c0b7333b4f7be8983dbbc161983b6fb814024d1b4952f79"}, + {file = "yarl-1.11.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:04293941646647b3bfb1719d1d11ff1028e9c30199509a844da3c0f5919dc520"}, + {file = "yarl-1.11.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:250e888fa62d73e721f3041e3a9abf427788a1934b426b45e1b92f62c1f68366"}, + {file = "yarl-1.11.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e8f63904df26d1a66aabc141bfd258bf738b9bc7bc6bdef22713b4f5ef789a4c"}, + {file = "yarl-1.11.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:aac44097d838dda26526cffb63bdd8737a2dbdf5f2c68efb72ad83aec6673c7e"}, + {file = "yarl-1.11.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:267b24f891e74eccbdff42241c5fb4f974de2d6271dcc7d7e0c9ae1079a560d9"}, + {file = "yarl-1.11.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6907daa4b9d7a688063ed098c472f96e8181733c525e03e866fb5db480a424df"}, + {file = "yarl-1.11.1-cp39-cp39-win32.whl", hash = "sha256:14438dfc5015661f75f85bc5adad0743678eefee266ff0c9a8e32969d5d69f74"}, + {file = "yarl-1.11.1-cp39-cp39-win_amd64.whl", hash = "sha256:94d0caaa912bfcdc702a4204cd5e2bb01eb917fc4f5ea2315aa23962549561b0"}, + {file = "yarl-1.11.1-py3-none-any.whl", hash = "sha256:72bf26f66456baa0584eff63e44545c9f0eaed9b73cb6601b647c91f14c11f38"}, + {file = "yarl-1.11.1.tar.gz", hash = "sha256:1bb2d9e212fb7449b8fb73bc461b51eaa17cc8430b4a87d87be7b25052d92f53"}, ] [package.dependencies] @@ -5112,13 +5769,13 @@ multidict = ">=4.0" [[package]] name = "zipp" -version = "3.20.1" +version = "3.20.2" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.8" files = [ - {file = "zipp-3.20.1-py3-none-any.whl", hash = "sha256:9960cd8967c8f85a56f920d5d507274e74f9ff813a0ab8889a5b5be2daf44064"}, - {file = "zipp-3.20.1.tar.gz", hash = "sha256:c22b14cc4763c5a5b04134207736c107db42e9d3ef2d9779d465f5f1bcba572b"}, + {file = "zipp-3.20.2-py3-none-any.whl", hash = "sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350"}, + {file = "zipp-3.20.2.tar.gz", hash = "sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29"}, ] [package.extras] @@ -5129,76 +5786,7 @@ enabler = ["pytest-enabler (>=2.2)"] test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] type = ["pytest-mypy"] -[[package]] -name = "zope-event" -version = "5.0" -description = "Very basic event publishing system" -optional = false -python-versions = ">=3.7" -files = [ - {file = "zope.event-5.0-py3-none-any.whl", hash = "sha256:2832e95014f4db26c47a13fdaef84cef2f4df37e66b59d8f1f4a8f319a632c26"}, - {file = "zope.event-5.0.tar.gz", hash = "sha256:bac440d8d9891b4068e2b5a2c5e2c9765a9df762944bda6955f96bb9b91e67cd"}, -] - -[package.dependencies] -setuptools = "*" - -[package.extras] -docs = ["Sphinx"] -test = ["zope.testrunner"] - -[[package]] -name = "zope-interface" -version = "7.0.3" -description = "Interfaces for Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "zope.interface-7.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9b9369671a20b8d039b8e5a1a33abd12e089e319a3383b4cc0bf5c67bd05fe7b"}, - {file = "zope.interface-7.0.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:db6237e8fa91ea4f34d7e2d16d74741187e9105a63bbb5686c61fea04cdbacca"}, - {file = "zope.interface-7.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53d678bb1c3b784edbfb0adeebfeea6bf479f54da082854406a8f295d36f8386"}, - {file = "zope.interface-7.0.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3aa8fcbb0d3c2be1bfd013a0f0acd636f6ed570c287743ae2bbd467ee967154d"}, - {file = "zope.interface-7.0.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6195c3c03fef9f87c0dbee0b3b6451df6e056322463cf35bca9a088e564a3c58"}, - {file = "zope.interface-7.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:11fa1382c3efb34abf16becff8cb214b0b2e3144057c90611621f2d186b7e1b7"}, - {file = "zope.interface-7.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:af94e429f9d57b36e71ef4e6865182090648aada0cb2d397ae2b3f7fc478493a"}, - {file = "zope.interface-7.0.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6dd647fcd765030638577fe6984284e0ebba1a1008244c8a38824be096e37fe3"}, - {file = "zope.interface-7.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bee1b722077d08721005e8da493ef3adf0b7908e0cd85cc7dc836ac117d6f32"}, - {file = "zope.interface-7.0.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2545d6d7aac425d528cd9bf0d9e55fcd47ab7fd15f41a64b1c4bf4c6b24946dc"}, - {file = "zope.interface-7.0.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d04b11ea47c9c369d66340dbe51e9031df2a0de97d68f442305ed7625ad6493"}, - {file = "zope.interface-7.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:064ade95cb54c840647205987c7b557f75d2b2f7d1a84bfab4cf81822ef6e7d1"}, - {file = "zope.interface-7.0.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3fcdc76d0cde1c09c37b7c6b0f8beba2d857d8417b055d4f47df9c34ec518bdd"}, - {file = "zope.interface-7.0.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3d4b91821305c8d8f6e6207639abcbdaf186db682e521af7855d0bea3047c8ca"}, - {file = "zope.interface-7.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35062d93bc49bd9b191331c897a96155ffdad10744ab812485b6bad5b588d7e4"}, - {file = "zope.interface-7.0.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c96b3e6b0d4f6ddfec4e947130ec30bd2c7b19db6aa633777e46c8eecf1d6afd"}, - {file = "zope.interface-7.0.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e0c151a6c204f3830237c59ee4770cc346868a7a1af6925e5e38650141a7f05"}, - {file = "zope.interface-7.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:3de1d553ce72868b77a7e9d598c9bff6d3816ad2b4cc81c04f9d8914603814f3"}, - {file = "zope.interface-7.0.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab985c566a99cc5f73bc2741d93f1ed24a2cc9da3890144d37b9582965aff996"}, - {file = "zope.interface-7.0.3-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d976fa7b5faf5396eb18ce6c132c98e05504b52b60784e3401f4ef0b2e66709b"}, - {file = "zope.interface-7.0.3-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21a207c6b2c58def5011768140861a73f5240f4f39800625072ba84e76c9da0b"}, - {file = "zope.interface-7.0.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:382d31d1e68877061daaa6499468e9eb38eb7625d4369b1615ac08d3860fe896"}, - {file = "zope.interface-7.0.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2c4316a30e216f51acbd9fb318aa5af2e362b716596d82cbb92f9101c8f8d2e7"}, - {file = "zope.interface-7.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01e6e58078ad2799130c14a1d34ec89044ada0e1495329d72ee0407b9ae5100d"}, - {file = "zope.interface-7.0.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:799ef7a444aebbad5a145c3b34bff012b54453cddbde3332d47ca07225792ea4"}, - {file = "zope.interface-7.0.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3b7ce6d46fb0e60897d62d1ff370790ce50a57d40a651db91a3dde74f73b738"}, - {file = "zope.interface-7.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:f418c88f09c3ba159b95a9d1cfcdbe58f208443abb1f3109f4b9b12fd60b187c"}, - {file = "zope.interface-7.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:84f8794bd59ca7d09d8fce43ae1b571be22f52748169d01a13d3ece8394d8b5b"}, - {file = "zope.interface-7.0.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7d92920416f31786bc1b2f34cc4fc4263a35a407425319572cbf96b51e835cd3"}, - {file = "zope.interface-7.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95e5913ec718010dc0e7c215d79a9683b4990e7026828eedfda5268e74e73e11"}, - {file = "zope.interface-7.0.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1eeeb92cb7d95c45e726e3c1afe7707919370addae7ed14f614e22217a536958"}, - {file = "zope.interface-7.0.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ecd32f30f40bfd8511b17666895831a51b532e93fc106bfa97f366589d3e4e0e"}, - {file = "zope.interface-7.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:5112c530fa8aa2108a3196b9c2f078f5738c1c37cfc716970edc0df0414acda8"}, - {file = "zope.interface-7.0.3.tar.gz", hash = "sha256:cd2690d4b08ec9eaf47a85914fe513062b20da78d10d6d789a792c0b20307fb1"}, -] - -[package.dependencies] -setuptools = "*" - -[package.extras] -docs = ["Sphinx", "repoze.sphinx.autointerface", "sphinx-rtd-theme"] -test = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] -testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] - [metadata] lock-version = "2.0" python-versions = "^3.10, < 3.13" -content-hash = "b2cd3d01d16769e9521a97ae561aff50f1799a9814eee027306b81e972a451ab" +content-hash = "dc107e58cbb2aaba133ae8a77241e37bfa9852674c7544f107c2f5d2447620c5" diff --git a/pyproject.toml b/pyproject.toml index 09e04be239..a28910aac4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] -name = "infrahub" -version = "0.16.4" +name = "infrahub-server" +version = "1.0.0" description = "Infrahub is taking a new approach to Infrastructure Management by providing a new generation of datastore to organize and control all the data that defines how an infrastructure should run." authors = ["OpsMill "] readme = "README.md" @@ -16,20 +16,15 @@ classifiers = [ "Programming Language :: Python :: 3.12", ] -packages = [{ include = "infrahub", from = "backend" }] - - -[tool.poetry.group.test-scale] -optional = true +packages = [ + { include = "infrahub", from = "backend" }, + { include = "infrahub_sdk", from = "python_sdk" } +] [tool.poetry.dependencies] python = "^3.10, < 3.13" neo4j = "~5.24" neo4j-rust-ext = "^5.24.0.0" -infrahub-sdk = { path = "python_sdk", extras = [ - "ctl", - "tests", -], develop = true } pydantic = "2.7.2" pydantic-settings = "~2.2" pytest = "~7.4" @@ -38,26 +33,44 @@ structlog = "24.1.0" boto3 = "1.34.129" email-validator = "~2.1" redis = { version = "^5.0.0", extras = ["hiredis"] } -typer = "0.12.3" +typer = "0.12.5" +prefect = "3.0.3" +ujson = "^5" +Jinja2 = "^3" +gitpython = "^3" +pyyaml = "^6" +toml = "^0.10" # Dependencies specific to the API Server -fastapi = "~0.112" +fastapi = "~0.115" fastapi-storages = "~0.3" -graphene = "~3.3" -gunicorn = "^22.0.0" +graphene = "~3.4" +gunicorn = "^23.0.0" lunr = "^0.7.0.post1" starlette-exporter = "~0.23" python-multipart = "0.0.9" # Required by FastAPI to upload large files asgi-correlation-id = "4.2.0" # Middleware for FastAPI to generate ID per request bcrypt = "~4.1" # Used to hash and validate password pyjwt = "~2.8" # Used to manage JWT tokens -uvicorn = { version = "~0.30", extras = ["standard"] } +uvicorn = { version = "~0.32", extras = ["standard"] } opentelemetry-instrumentation-aio-pika = "^0.45b0" opentelemetry-instrumentation-fastapi = "^0.45b0" opentelemetry-exporter-otlp-proto-grpc = "^1.24.0" opentelemetry-exporter-otlp-proto-http = "^1.24.0" nats-py = "^2.7.2" netaddr = "1.3.0" +authlib = "1.3.2" + + +# Dependencies specific to the SDK +rich = "^13" +pyarrow = "^14" +numpy = [ + { version = "^1.24.2", optional = true, python = ">=3.9,<3.12" }, + { version = "^1.26.2", optional = true, python = ">=3.12" }, +] + + [tool.poetry.group.dev.dependencies] yamllint = "*" @@ -77,24 +90,36 @@ pre-commit = "^2.20.0" types-toml = "*" types-ujson = "*" types-pyyaml = "*" -ruff = "0.5.0" +ruff = "0.7.1" invoke = "2.2.0" pytest-benchmark = "^4.0.0" pytest-codspeed = "^2.2.0" deepdiff = "^6.2" polyfactory = "^2.16.2" towncrier = "^24.8" - -[tool.poetry.group.test-scale.dependencies] -locust = "^2.20.1" -docker = "^7.0.0" -matplotlib = "^3.8" -pandas = "^2.2" +pytest-env = "^1.1.3" +testcontainers = "^4.8.1" +pytest-timeout = "^2.3.1" +matplotlib = "^3.9.2" +pandas = "^2.2.3" + +# [tool.poetry.group.test-scale.dependencies] +# locust = "^2.20.1" +# docker = "^7.0.0" +# matplotlib = "^3.8" +# pandas = "^2.2" [tool.poetry.scripts] infrahub = "infrahub.cli:app" infrahub-git-credential = "infrahub.git_credential.helper:app" infrahub-git-askpass = "infrahub.git_credential.askpass:app" +infrahubctl = "infrahub_sdk.ctl.cli:app" + +[tool.poetry.plugins."pytest11"] +"pytest-infrahub" = "infrahub_sdk.pytest_plugin.plugin" + +[tool.poetry.plugins."prefect.collections"] +"infrahubasync" = "infrahub.workers.infrahub_async" [tool.coverage.run] branch = true @@ -143,6 +168,7 @@ disable = """, too-many-return-statements, unnecessary-comprehension, multiple-statements, + self-assigning-variable, """ [tool.pylint.miscellaneous] @@ -156,6 +182,8 @@ min-similarity-lines = 20 [tool.pytest.ini_options] asyncio_mode = "auto" +timeout = 300 # 5 minutes +session_timeout = 1200 # 20 minutes testpaths = ["tests"] filterwarnings = [ "ignore:Module already imported so cannot be rewritten", @@ -165,12 +193,16 @@ filterwarnings = [ addopts = "-vs --cov-report term-missing --cov-report xml --dist loadscope --junitxml=pytest-junit.xml" junit_duration_report = "call" +[tool.pytest_env] +PREFECT_LOGGING_LEVEL = "CRITICAL" +INFRAHUB_LOG_LEVEL = "CRITICAL" + [tool.mypy] pretty = true ignore_missing_imports = true disallow_untyped_defs = true disable_error_code = ["type-abstract"] -exclude = ["^backend/tests/scale", "^backend/tests/unit"] +exclude = ["^backend/tests/scale", "^backend/tests/unit", "^backend/tests/test_data", "^backend/tests/query_benchmark"] [[tool.mypy.overrides]] module = "infrahub.*" @@ -220,10 +252,6 @@ ignore_errors = true module = "infrahub.core.query" ignore_errors = true -[[tool.mypy.overrides]] -module = "infrahub.core.query.attribute" -ignore_errors = true - [[tool.mypy.overrides]] module = "infrahub.core.query.diff" ignore_errors = true @@ -253,7 +281,11 @@ module = "infrahub.core.schema.basenode_schema" ignore_errors = true [[tool.mypy.overrides]] -module = "infrahub.core.schema_manager" +module = "infrahub.core.schema.schema_branch" +ignore_errors = true + +[[tool.mypy.overrides]] +module = "infrahub.core.schema.manager" ignore_errors = true [[tool.mypy.overrides]] @@ -276,14 +308,6 @@ ignore_errors = true module = "infrahub.git_credential.askpass" ignore_errors = true -[[tool.mypy.overrides]] -module = "infrahub.graphql" -ignore_errors = true - -[[tool.mypy.overrides]] -module = "infrahub.graphql.mutations.attribute" -ignore_errors = true - [[tool.mypy.overrides]] module = "infrahub.graphql.mutations.ipam" ignore_errors = true @@ -312,23 +336,6 @@ ignore_errors = true module = "infrahub.graphql.resolver" ignore_errors = true -[[tool.mypy.overrides]] -module = "infrahub.graphql.schema" -ignore_errors = true - -[[tool.mypy.overrides]] -module = "infrahub.graphql.subscription" -ignore_errors = true - - -[[tool.mypy.overrides]] -module = "infrahub.graphql.types.attribute" -ignore_errors = true - -[[tool.mypy.overrides]] -module = "infrahub.graphql.types.mixin" -ignore_errors = true - [[tool.mypy.overrides]] module = "infrahub.graphql.types.standard_node" ignore_errors = true @@ -381,22 +388,6 @@ ignore_errors = true module = "infrahub.message_bus.operations.schema.validator" ignore_errors = true -[[tool.mypy.overrides]] -module = "infrahub.message_bus.operations.send.telemetry" -ignore_errors = true - -[[tool.mypy.overrides]] -module = "infrahub.message_bus.operations.send.webhook" -ignore_errors = true - -[[tool.mypy.overrides]] -module = "infrahub.message_bus.operations.trigger.ipam" -ignore_errors = true - -[[tool.mypy.overrides]] -module = "infrahub.test_data.dataset01" -ignore_errors = true - [[tool.mypy.overrides]] module = "infrahub.test_data.dataset03" ignore_errors = true @@ -456,6 +447,7 @@ select = [ "E", # pycodestyle errors "EXE", # flake8-executable "F", # pyflakes + "FURB", # refurb "I", # isort-like checks "ICN", # flake8-import-conventions "INP", # flake8-no-pep420 @@ -467,7 +459,9 @@ select = [ "PYI", # flake8-pyi "Q", # flake8-quotes "RET", # flake8-return + "RUF", # Ruff specific rules "S", # flake8-bandit + "SIM", # flake8-simplify "TCH", # flake8-type-checking "T10", # flake8-debugger "UP", # pyupgrade @@ -482,6 +476,7 @@ ignore = [ # like this so that we can reactivate them one by one. Alternatively ignored after further # # investigation if they are deemed to not make sense. # ################################################################################################## + "ASYNC110", # Use `anyio.Event` instead of awaiting `anyio.sleep` in a `while` loop "ASYNC230", # Async functions should not open files with blocking methods like `open` "ASYNC251", # Async functions should not call `time.sleep` "B007", # Loop control variable not used within loop body @@ -492,6 +487,13 @@ ignore = [ "C403", # Unnecessary `list` comprehension (rewrite as a `set` comprehension) "C409", # Unnecessary `list` literal passed to `tuple()` (rewrite as a `tuple` literal) "C414", # Unnecessary `list` call within `sorted()` + "C420", # Unnecessary dict comprehension for iterable; use `dict.fromkeys` instead + "FURB113", # Use `networks.extend(...)` instead of repeatedly calling `networks.append()` + "FURB116", # Replace `bin` call with f-string + "FURB118", # Use `operator.itemgetter(1)` instead of defining a lambda + "FURB140", # Use `itertools.starmap` instead of the generator + "FURB171", # Membership test against single-item container + "FURB192", # Prefer `min` over `sorted()` to compute the minimum value in a sequence "N801", # Class name should use CapWords convention "N802", # Function name should be lowercase "N805", # First argument of a method should be named self @@ -516,7 +518,6 @@ ignore = [ "PLR6301", # Method could be a function, class method, or static method "PLW0603", # Using the global statement to update `SETTINGS` is discouraged "PLW1508", # Invalid type for environment variable default; expected `str` or `None` - "PLW3201", # Bad or misspelled dunder method name `__init_subclass_with_meta__` "PTH100", # `os.path.abspath()` should be replaced by `Path.resolve()` "PTH102", # `os.mkdir()` should be replaced by `Path.mkdir()` "PTH103", # `os.makedirs()` should be replaced by `Path.mkdir(parents=True)` @@ -530,17 +531,35 @@ ignore = [ "PTH118", # `os.path.join()` should be replaced by `Path` with `/` operator "RET503", # Missing explicit `return` at the end of function able to return non-`None` value "RET504", # Unnecessary assignment before `return` statement + "RUF005", # Consider `[*list(peers.values()), rfc5735]` instead of concatenation + "RUF006", # Store a reference to the return value of `asyncio.create_task` + "RUF010", # Use explicit conversion flag + "RUF012", # Mutable class attributes should be annotated with `typing.ClassVar` + "RUF015", # Prefer `next(...)` over single element slice + "RUF021", # Parenthesize `a and b` expressions when chaining `and` and `or` together, to make the precedence clear + "RUF027", # Possible f-string without an `f` prefix + "RUF029", # Function is declared `async`, but doesn't `await` or use `async` features. "S101", # Use of `assert` detected "S105", # Possible hardcoded password assigned to: "REGEX_PASSWORD" "S108", # Probable insecure usage of temporary file or directory "S202", # Uses of `tarfile.extractall()` "S311", # Standard pseudo-random generators are not suitable for cryptographic purposes "S701", # By default, jinja2 sets `autoescape` to `False`. Consider using `autoescape=True` - "UP007", # Use X | Y for type annotations + "SIM108", # Use ternary operator `markexpr = "not neo4j" if not markexpr else f"not neo4j and ({markexpr})"` instead of `if`-`else`-block + "SIM102", # Use a single `if` statement instead of nested `if` statements + "SIM103", # Return the condition `identifier in self.sub_by_id.keys()` directly + "SIM105", # Use `contextlib.suppress(SchemaNotFoundError)` instead of `try`-`except`-`pass` + "SIM110", # Use `return any(worktree.identifier == identifier for worktree in worktrees)` instead of `for` loop + "SIM114", # Combine `if` branches using logical `or` operator + "SIM115", # Use a context manager for opening files + "SIM117", # Use a single `with` statement with multiple contexts instead of nested `with` statements + "SIM118", # Use `key in dict` instead of `key in dict.keys()` + "SIM201", # Use `backup_path.suffix != ".backup"` instead of `not backup_path.suffix == ".backup"` + "SIM401", # Use `property["items"].get("format", None)` instead of an `if` block + "SIM910", # Use `data.get("identifier")` instead of `data.get("identifier", None)` "UP012", # Unnecessary call to encode as UTF-8 "UP018", # Unnecessary {literal_type} call (rewrite as a literal) "UP031", # Use format specifiers instead of percent format - "UP034", # Avoid extraneous parentheses ] #https://docs.astral.sh/ruff/formatter/black/ @@ -560,6 +579,12 @@ max-line-length = 150 # Target max-complexity=10 max-complexity = 33 +[tool.ruff.lint.pylint] + +allow-dunder-method-names = [ + "__init_subclass_with_meta__", # Dunder method used within Graphene +] + [tool.ruff.lint.per-file-ignores] "backend/infrahub/**.py" = [ @@ -567,17 +592,21 @@ max-complexity = 33 # Review and change the below later # ################################################################################################## "ANN001", # Missing type annotation for function argument - "ANN002", # Missing type annotation for `*args` "ANN003", # Missing type annotation for `**kwargs` - "ANN201", # Missing return type annotation for public function - "ANN202", # Missing return type annotation for private function "ANN204", # Missing return type annotation for special method - "ANN206", # Missing return type annotation for classmethod "ANN401", # Dynamically typed expressions (typing.Any) are disallowed + "UP007", # Use X | Y for type annotations ] -"backend/infrahub/git/repository.py" = [ - "TCH003", # Pydantic needs UUID import to not only be available under TYPE_CHECKING clause +"backend/infrahub/config.py" = [ + "S323", # Allow users to create an SSL context that doesn't validate certificates +] + +"backend/infrahub/graphql/mutations/**.py" = [ + ################################################################################################## + # Review and change the below later # + ################################################################################################## + "ANN206", # Missing return type annotation for classmethod ] "backend/tests/**.py" = [ @@ -592,8 +621,6 @@ max-complexity = 33 "ANN003", # Missing type annotation for `**kwargs` "ANN201", # Missing return type annotation for public function "ANN202", # Missing return type annotation for private function - "ANN204", # Missing return type annotation for special method - "ANN205", # Missing return type annotation for staticmethod "ANN401", # Dynamically typed expressions (typing.Any) are disallowed ] @@ -605,27 +632,15 @@ max-complexity = 33 # like this so that we can reactivate them one by one. Alternatively ignored after further # # investigation if they are deemed to not make sense. # ################################################################################################## - "C901", # `generate_site` is too complex (34 > 33)" - "E501", # Line too long -] - -"tasks/**.py" = [ - ################################################################################################## - # Review and change the below later # - ################################################################################################## - "ANN001", # Missing type annotation for function argument - "ANN201", # Missing return type annotation for public function - "ANN202", # Missing return type annotation for private function + "C901", # `generate_site` is too complex (34 > 33)" + "E501", # Line too long + "RUF013", # PEP 484 prohibits implicit `Optional` ] - "utilities/**.py" = [ ################################################################################################## # Review and change the below later # ################################################################################################## - "ANN002", # Missing type annotation for `*args` - "ANN003", # Missing type annotation for `**kwargs` - "ANN201", # Missing return type annotation for public function "ANN401", # Dynamically typed expressions (typing.Any) are disallowed ] diff --git a/python_sdk b/python_sdk index 44a9a84f0d..5bbb22d8a9 160000 --- a/python_sdk +++ b/python_sdk @@ -1 +1 @@ -Subproject commit 44a9a84f0de0edc574a0853917dfbc63219c4f7d +Subproject commit 5bbb22d8a914611d32e51ea0e93ea8db5b4e9776 diff --git a/sync/LICENSE.txt b/sync/LICENSE.txt deleted file mode 100644 index 06154b9e1f..0000000000 --- a/sync/LICENSE.txt +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2024 OpsMill SAS - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. \ No newline at end of file diff --git a/sync/README.md b/sync/README.md deleted file mode 100644 index 97eae1f4cd..0000000000 --- a/sync/README.md +++ /dev/null @@ -1,95 +0,0 @@ - -![Infrahub Logo](https://assets-global.website-files.com/657aff4a26dd8afbab24944b/657b0e0678f7fd35ce130776_Logo%20INFRAHUB.svg) - - -# Infrahub Sync - -[Infrahub](https://github.com/opsmill/infrahub) by [OpsMill](https://opsmill.com) acts as a central hub to manage the data, templates and playbooks that powers your infrastructure. At its heart, Infrahub is built on 3 fundamental pillars: - -- **A Flexible Schema**: A model of the infrastructure and the relation between the objects in the model, that's easily extensible. -- **Version Control**: Natively integrated into the graph database which opens up some new capabilities like branching, diffing, and merging data directly in the database. -- **Unified Storage**: By combining a graph database and git, Infrahub stores data and code needed to manage the infrastructure. - -## Introduction - -Infrahub Sync is a versatile Python package that synchronizes data between a source and a destination system. It builds on the robust capabilities of `diffsync` to offer flexible and efficient data synchronization across different platforms, including Netbox, Nautobot, and Infrahub. This package features a Typer-based CLI for ease of use, supporting operations such as listing available sync projects, generating diffs, and executing sync processes. - -### Features - -- **Multiple Systems Support**: Synchronize data between Netbox, Nautobot, and Infrahub. -- **Flexible Configuration**: Define synchronization tasks with YAML configuration files. -- **CLI Interface**: Manage sync tasks directly from the command line. -- **Custom Sync Logic**: Generate Python code for custom sync adapters and models using provided Jinja templates. - -### Requirements - -Requirements - -- The two latest Infrahub releases -- Python >=3.9, <3.13 -- Python modules: - - infrahub-sdk >= 0.9.0 - -## Documentation - -Please refer to the [Infrahub Sync](https://docs.infrahub.app/integrations/sync/) documentation. - -## Project Structure - -```bash -. -├── README.md -├── examples -│ ├── nautobot-v1_to_infrahub -│ │ ├── config.yml -│ │ ├── infrahub -│ │ │ ├── __init__.py -│ │ │ ├── sync_adapter.py -│ │ │ └── sync_models.py -│ │ └── nautobot -│ │ ├── __init__.py -│ │ ├── sync_adapter.py -│ │ └── sync_models.py -│ ├── nautobot-v2_to_infrahub -│ │ ├── config.yml -│ │ ├── infrahub -│ │ │ ├── __init__.py -│ │ │ ├── sync_adapter.py -│ │ │ └── sync_models.py -│ │ └── nautobot -│ │ ├── __init__.py -│ │ ├── sync_adapter.py -│ │ └── sync_models.py -│ └── netbox_to_infrahub -│ ├── config.yml -│ ├── infrahub -│ │ ├── __init__.py -│ │ ├── sync_adapter.py -│ │ └── sync_models.py -│ └── netbox -│ ├── __init__.py -│ ├── sync_adapter.py -│ └── sync_models.py -├── infrahub-sync -│ ├── infrahub_sync -│ │ ├── __init__.py -│ │ ├── adapters -│ │ │ ├── infrahub.py -│ │ │ ├── nautobot.py -│ │ │ └── netbox.py -│ │ ├── cli.py -│ │ ├── generator -│ │ │ ├── __init__.py -│ │ │ ├── templates -│ │ │ │ ├── diffsync_adapter.j2 -│ │ │ │ └── diffsync_models.j2 -│ │ │ └── utils.py -│ │ └── utils.py -│ └── tests -│ └── __init__.py -├── poetry.lock -├── potenda -│ └── potenda -│ └── __init__.py -└── pyproject.toml -``` diff --git a/sync/examples/infrahub_to_peering-manager/config.yml b/sync/examples/infrahub_to_peering-manager/config.yml deleted file mode 100644 index 77437c7f24..0000000000 --- a/sync/examples/infrahub_to_peering-manager/config.yml +++ /dev/null @@ -1,149 +0,0 @@ ---- -name: to-peering-manager -source: - name: infrahub - settings: - url: "http://localhost:8000" - -destination: - name: peeringmanager - settings: - url: "https://demo.peering-manager.net" - # api_endpoint: "api" - # auth_method: "token" - token: "13bf6338aed52d172e33750d39717fff5a5f5d18" - -order: [ - "InfraAutonomousSystem", - "InfraBGPCommunity", - "InfraBGPRoutingPolicy", - "InfraBGPPeerGroup", - "InfraIXP", - "InfraIXPConnection", -] - -schema_mapping: - - name: InfraAutonomousSystem - mapping: peering/autonomous-systems - identifiers: ["asn"] - fields: - - name: name - mapping: name - - name: description - mapping: comments - - name: asn - mapping: asn - - name: irr_as_set - mapping: irr_as_set - - name: ipv4_max_prefixes - mapping: ipv4_max_prefixes - - name: ipv6_max_prefixes - mapping: ipv6_max_prefixes - - name: affiliated - mapping: affiliated - - - name: InfraBGPCommunity - mapping: peering/communities - identifiers: ["name"] - fields: - - name: name - mapping: slug - - name: label - mapping: name - - name: description - mapping: description - - name: value - mapping: value - - name: community_type - mapping: type - - - - name: InfraBGPRoutingPolicy - mapping: peering/routing-policies - identifiers: ["name"] - fields: - - name: name - mapping: slug - - name: label - mapping: name - - name: description - mapping: description - - name: policy_type - mapping: type - - name: weight - mapping: weight - - name: address_family - mapping: address_family - - name: bgp_communities - mapping: communities - reference: InfraBGPCommunity - - - name: InfraBGPPeerGroup - mapping: peering/bgp-groups - identifiers: ["name"] - fields: - - name: name - mapping: name - - name: description - mapping: description - - name: status - mapping: status.value - - name: import_policies - mapping: import_routing_policies - reference: InfraBGPRoutingPolicy - - name: export_policies - mapping: export_routing_policies - reference: InfraBGPRoutingPolicy - - name: bgp_communities - mapping: communities - reference: InfraBGPCommunity - - - name: InfraIXP - mapping: peering/internet-exchanges - identifiers: ["name"] - fields: - - name: name - mapping: name - - name: description - mapping: description - - name: status - mapping: status.value - - name: import_policies - mapping: import_routing_policies - reference: InfraBGPRoutingPolicy - - name: export_policies - mapping: export_routing_policies - reference: InfraBGPRoutingPolicy - - name: bgp_communities - mapping: communities - reference: InfraBGPCommunity - # Showcase Filters - filters: - - field: name - operation: contains - value: "S.H.I.E.L.D" - - - name: InfraIXPConnection - mapping: net/connections - identifiers: ["name"] - fields: - - name: name - mapping: name - - name: peeringdb_netixlan - mapping: peeringdb_netixlan.id - - name: description - mapping: description - - name: status - mapping: status.value - - name: vlan - mapping: vlan - - name: internet_exchange_point - mapping: internet_exchange_point - reference: InfraIXP - # Showcase Filters - # As we filter InfraIXP on the name S.H.I.E.L.D, we need to have the same filter here - # to avoid importing IXP Connection without IXP - filters: - - field: internet_exchange_point - operation: contains - value: "S.H.I.E.L.D" diff --git a/sync/examples/infrahub_to_peering-manager/infrahub/sync_adapter.py b/sync/examples/infrahub_to_peering-manager/infrahub/sync_adapter.py deleted file mode 100644 index 0529f5dfd0..0000000000 --- a/sync/examples/infrahub_to_peering-manager/infrahub/sync_adapter.py +++ /dev/null @@ -1,24 +0,0 @@ -from infrahub_sync.adapters.infrahub import InfrahubAdapter - -from .sync_models import ( - InfraAutonomousSystem, - InfraBGPCommunity, - InfraBGPPeerGroup, - InfraBGPRoutingPolicy, - InfraIXP, - InfraIXPConnection, -) - - -# ------------------------------------------------------- -# AUTO-GENERATED FILE, DO NOT MODIFY -# This file has been generated with the command `infrahub-sync generate` -# All modifications will be lost the next time you reexecute this command -# ------------------------------------------------------- -class InfrahubSync(InfrahubAdapter): - InfraAutonomousSystem = InfraAutonomousSystem - InfraBGPPeerGroup = InfraBGPPeerGroup - InfraBGPRoutingPolicy = InfraBGPRoutingPolicy - InfraBGPCommunity = InfraBGPCommunity - InfraIXP = InfraIXP - InfraIXPConnection = InfraIXPConnection diff --git a/sync/examples/infrahub_to_peering-manager/infrahub/sync_models.py b/sync/examples/infrahub_to_peering-manager/infrahub/sync_models.py deleted file mode 100644 index dbbc56fa12..0000000000 --- a/sync/examples/infrahub_to_peering-manager/infrahub/sync_models.py +++ /dev/null @@ -1,99 +0,0 @@ -from typing import Any, List, Optional - -from infrahub_sync.adapters.infrahub import InfrahubModel - - -# ------------------------------------------------------- -# AUTO-GENERATED FILE, DO NOT MODIFY -# This file has been generated with the command `infrahub-sync generate` -# All modifications will be lost the next time you reexecute this command -# ------------------------------------------------------- -class InfraAutonomousSystem(InfrahubModel): - _modelname = "InfraAutonomousSystem" - _identifiers = ("asn",) - _attributes = ("name", "description", "irr_as_set", "ipv4_max_prefixes", "ipv6_max_prefixes", "affiliated") - name: str - asn: int - description: Optional[str] = None - irr_as_set: Optional[str] = None - ipv4_max_prefixes: Optional[int] = None - ipv6_max_prefixes: Optional[int] = None - affiliated: Optional[bool] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraBGPPeerGroup(InfrahubModel): - _modelname = "InfraBGPPeerGroup" - _identifiers = ("name",) - _attributes = ("import_policies", "export_policies", "bgp_communities", "description", "status") - name: str - description: Optional[str] = None - status: Optional[str] = None - import_policies: Optional[List[str]] = [] - export_policies: Optional[List[str]] = [] - bgp_communities: Optional[List[str]] = [] - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraBGPRoutingPolicy(InfrahubModel): - _modelname = "InfraBGPRoutingPolicy" - _identifiers = ("name",) - _attributes = ("bgp_communities", "address_family", "label", "description", "policy_type", "weight") - address_family: int - label: Optional[str] = None - description: Optional[str] = None - name: str - policy_type: str - weight: Optional[int] = 1000 - bgp_communities: Optional[List[str]] = [] - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraBGPCommunity(InfrahubModel): - _modelname = "InfraBGPCommunity" - _identifiers = ("name",) - _attributes = ("description", "value", "label", "community_type") - description: Optional[str] = None - name: str - value: str - label: Optional[str] = None - community_type: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraIXP(InfrahubModel): - _modelname = "InfraIXP" - _identifiers = ("name",) - _attributes = ("export_policies", "bgp_communities", "import_policies", "description", "status") - description: Optional[str] = None - name: str - status: Optional[str] = "enabled" - export_policies: Optional[List[str]] = [] - bgp_communities: Optional[List[str]] = [] - import_policies: Optional[List[str]] = [] - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraIXPConnection(InfrahubModel): - _modelname = "InfraIXPConnection" - _identifiers = ("name",) - _attributes = ("internet_exchange_point", "status", "vlan", "description", "peeringdb_netixlan") - status: Optional[str] = "enabled" - vlan: Optional[int] = None - name: str - description: Optional[str] = None - peeringdb_netixlan: Optional[int] = None - internet_exchange_point: str - - local_id: Optional[str] = None - local_data: Optional[Any] = None diff --git a/sync/examples/infrahub_to_peering-manager/peeringmanager/sync_adapter.py b/sync/examples/infrahub_to_peering-manager/peeringmanager/sync_adapter.py deleted file mode 100644 index 9c2b88184a..0000000000 --- a/sync/examples/infrahub_to_peering-manager/peeringmanager/sync_adapter.py +++ /dev/null @@ -1,24 +0,0 @@ -from infrahub_sync.adapters.peeringmanager import PeeringmanagerAdapter - -from .sync_models import ( - InfraAutonomousSystem, - InfraBGPCommunity, - InfraBGPPeerGroup, - InfraBGPRoutingPolicy, - InfraIXP, - InfraIXPConnection, -) - - -# ------------------------------------------------------- -# AUTO-GENERATED FILE, DO NOT MODIFY -# This file has been generated with the command `infrahub-sync generate` -# All modifications will be lost the next time you reexecute this command -# ------------------------------------------------------- -class PeeringmanagerSync(PeeringmanagerAdapter): - InfraAutonomousSystem = InfraAutonomousSystem - InfraBGPPeerGroup = InfraBGPPeerGroup - InfraBGPRoutingPolicy = InfraBGPRoutingPolicy - InfraBGPCommunity = InfraBGPCommunity - InfraIXP = InfraIXP - InfraIXPConnection = InfraIXPConnection diff --git a/sync/examples/infrahub_to_peering-manager/peeringmanager/sync_models.py b/sync/examples/infrahub_to_peering-manager/peeringmanager/sync_models.py deleted file mode 100644 index 3ad4dd15c2..0000000000 --- a/sync/examples/infrahub_to_peering-manager/peeringmanager/sync_models.py +++ /dev/null @@ -1,99 +0,0 @@ -from typing import Any, List, Optional - -from infrahub_sync.adapters.peeringmanager import PeeringmanagerModel - - -# ------------------------------------------------------- -# AUTO-GENERATED FILE, DO NOT MODIFY -# This file has been generated with the command `infrahub-sync generate` -# All modifications will be lost the next time you reexecute this command -# ------------------------------------------------------- -class InfraAutonomousSystem(PeeringmanagerModel): - _modelname = "InfraAutonomousSystem" - _identifiers = ("asn",) - _attributes = ("name", "description", "irr_as_set", "ipv4_max_prefixes", "ipv6_max_prefixes", "affiliated") - name: str - asn: int - description: Optional[str] = None - irr_as_set: Optional[str] = None - ipv4_max_prefixes: Optional[int] = None - ipv6_max_prefixes: Optional[int] = None - affiliated: Optional[bool] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraBGPPeerGroup(PeeringmanagerModel): - _modelname = "InfraBGPPeerGroup" - _identifiers = ("name",) - _attributes = ("import_policies", "export_policies", "bgp_communities", "description", "status") - name: str - description: Optional[str] = None - status: Optional[str] = None - import_policies: Optional[List[str]] = [] - export_policies: Optional[List[str]] = [] - bgp_communities: Optional[List[str]] = [] - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraBGPRoutingPolicy(PeeringmanagerModel): - _modelname = "InfraBGPRoutingPolicy" - _identifiers = ("name",) - _attributes = ("bgp_communities", "address_family", "label", "description", "policy_type", "weight") - address_family: int - label: Optional[str] = None - description: Optional[str] = None - name: str - policy_type: str - weight: Optional[int] = 1000 - bgp_communities: Optional[List[str]] = [] - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraBGPCommunity(PeeringmanagerModel): - _modelname = "InfraBGPCommunity" - _identifiers = ("name",) - _attributes = ("description", "value", "label", "community_type") - description: Optional[str] = None - name: str - value: str - label: Optional[str] = None - community_type: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraIXP(PeeringmanagerModel): - _modelname = "InfraIXP" - _identifiers = ("name",) - _attributes = ("export_policies", "bgp_communities", "import_policies", "description", "status") - description: Optional[str] = None - name: str - status: Optional[str] = "enabled" - export_policies: Optional[List[str]] = [] - bgp_communities: Optional[List[str]] = [] - import_policies: Optional[List[str]] = [] - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraIXPConnection(PeeringmanagerModel): - _modelname = "InfraIXPConnection" - _identifiers = ("name",) - _attributes = ("internet_exchange_point", "status", "vlan", "description", "peeringdb_netixlan") - status: Optional[str] = "enabled" - vlan: Optional[int] = None - name: str - description: Optional[str] = None - peeringdb_netixlan: Optional[int] = None - internet_exchange_point: str - - local_id: Optional[str] = None - local_data: Optional[Any] = None diff --git a/sync/examples/ipfabric_to_infrahub/config.yml b/sync/examples/ipfabric_to_infrahub/config.yml deleted file mode 100644 index b5a4bc233f..0000000000 --- a/sync/examples/ipfabric_to_infrahub/config.yml +++ /dev/null @@ -1,207 +0,0 @@ ---- -name: from-ipfabric -source: - name: ipfabricsync - settings: - base_url: "https://" - auth: "" - -destination: - name: infrahub - settings: - url: "http://localhost:8000" - -order: [ - "LocationGeneric", - "OrganizationGeneric", - "InfraPlatform", - "TemplateDeviceType", - "InfraNOSVersion", - "InfraDevice", - "InfraPartNumber", - "InfraVLAN", - "InfraVRF", - "InfraInterfaceL3", - # "InfraPrefix", - # "InfraIPAddress", -] - -schema_mapping: - - name: LocationGeneric - mapping: tables/inventory/sites - fields: - - name: name - mapping: siteName - - name: description - mapping: name - - name: type - static: "Site" - - # Device (manufacturer, device types, devices, interfaces) - - name: OrganizationGeneric - mapping: tables/inventory/summary/vendors - fields: - - name: name - mapping: vendor - - name: type - static: "Vendor" - - - name: InfraPlatform - mapping: tables/inventory/summary/platforms - fields: - - name: name - mapping: platform - - name: description - static: "Platform" - - - name: TemplateDeviceType - mapping: tables/inventory/summary/models - identifiers: ["name", "manufacturer"] - fields: - - name: name - mapping: model - - name: description - static: "Device Template" - - name: manufacturer - mapping: vendor - reference: OrganizationGeneric - - - name: InfraNOSVersion - identifiers: ["manufacturer", "model", "version"] - mapping: tables/management/osver-consistency - fields: - - name: version - mapping: version - - name: manufacturer - mapping: vendor - reference: OrganizationGeneric - - name: platform - mapping: platform - reference: InfraPlatform - - name: model - mapping: model - reference: TemplateDeviceType - - - name: InfraDevice - identifiers: ["location", "hostname"] - mapping: tables/inventory/devices - fields: - - name: hostname - mapping: hostname - - name: serial_number - mapping: sn - - name: hardware_serial_number - mapping: snHw - - name: fqdn - mapping: fqdn - - name: model - mapping: model - reference: TemplateDeviceType - # - name: status - # mapping: status - # reference: StatusGeneric - - name: location - mapping: siteName - reference: LocationGeneric - - name: platform - mapping: platform - reference: InfraPlatform - - name: version - mapping: version - reference: InfraNOSVersion - - - name: InfraPartNumber - identifiers: ["device", "name"] - mapping: tables/inventory/pn - fields: - - name: name - mapping: name - - name: description - mapping: dscr - - name: part_id - mapping: pid - - name: part_sn - mapping: sn - - name: part_vid - mapping: vid - - name: manufacturer - mapping: vendor - reference: OrganizationGeneric - - name: model - mapping: model - reference: TemplateDeviceType - - name: device - mapping: hostname - reference: InfraDevice - - - name: InfraVLAN - identifiers: ["location", "vlan_id"] - mapping: tables/vlan/site-summary - fields: - - name: name - mapping: vlanName - - name: description - mapping: dscr - - name: vlan_id - mapping: vlanId - - name: location - mapping: siteName - reference: LocationGeneric - - - name: InfraVRF - identifiers: ["name"] - mapping: tables/vrf/detail - fields: - - name: name - mapping: vrf - - name: vrf_rd - mapping: rd - - - name: InfraInterfaceL3 - identifiers: ["device", "name"] - mapping: tables/inventory/interfaces - fields: - - name: device - mapping: hostname - reference: InfraDevice - - name: name - mapping: nameOriginal - - name: description - mapping: dscr - - name: speed - mapping: speedValue - - name: mtu - mapping: mtu - - name: mac_address - mapping: mac - - # - name: InfraPrefix - # identifiers: ["vrf", "prefix"] - # mapping: tables/networks - # fields: - # - name: prefix - # mapping: net - # - name: vrf - # mapping: vrf - # reference: InfraVRF - # - name: vlan - # mapping: vlanId - # reference: InfraVLAN - # - name: location - # mapping: siteName - # reference: LocationGeneric - - # - name: InfraIPAddress - # identifiers: ["address"] - # mapping: tables/addressing/managed-devs - # fields: - # - name: address - # mapping: net - # - name: description - # mapping: dscr - # - name: prefix - # mapping: net - # reference: InfraPrefix - # - name: interface - # mapping: intName - # reference: InfraInterfaceL3 diff --git a/sync/examples/ipfabric_to_infrahub/infrahub/sync_adapter.py b/sync/examples/ipfabric_to_infrahub/infrahub/sync_adapter.py deleted file mode 100644 index 6f32535c6f..0000000000 --- a/sync/examples/ipfabric_to_infrahub/infrahub/sync_adapter.py +++ /dev/null @@ -1,32 +0,0 @@ -from infrahub_sync.adapters.infrahub import InfrahubAdapter - -from .sync_models import ( - InfraDevice, - InfraInterfaceL3, - InfraNOSVersion, - InfraPartNumber, - InfraPlatform, - InfraVLAN, - InfraVRF, - LocationGeneric, - OrganizationGeneric, - TemplateDeviceType, -) - - -# ------------------------------------------------------- -# AUTO-GENERATED FILE, DO NOT MODIFY -# This file has been generated with the command `infrahub-sync generate` -# All modifications will be lost the next time you reexecute this command -# ------------------------------------------------------- -class InfrahubSync(InfrahubAdapter): - InfraDevice = InfraDevice - InfraInterfaceL3 = InfraInterfaceL3 - InfraNOSVersion = InfraNOSVersion - InfraPartNumber = InfraPartNumber - InfraPlatform = InfraPlatform - InfraVLAN = InfraVLAN - InfraVRF = InfraVRF - LocationGeneric = LocationGeneric - OrganizationGeneric = OrganizationGeneric - TemplateDeviceType = TemplateDeviceType diff --git a/sync/examples/ipfabric_to_infrahub/infrahub/sync_models.py b/sync/examples/ipfabric_to_infrahub/infrahub/sync_models.py deleted file mode 100644 index c809cc5790..0000000000 --- a/sync/examples/ipfabric_to_infrahub/infrahub/sync_models.py +++ /dev/null @@ -1,131 +0,0 @@ -from typing import Any, Optional - -from infrahub_sync.adapters.infrahub import InfrahubModel - -# ------------------------------------------------------- -# AUTO-GENERATED FILE, DO NOT MODIFY -# This file has been generated with the command `infrahub-sync generate` -# All modifications will be lost the next time you reexecute this command -# ------------------------------------------------------- - - -class InfraDevice(InfrahubModel): - _modelname = "InfraDevice" - _identifiers = ("location", "hostname") - _attributes = ("platform", "model", "version", "serial_number", "hardware_serial_number", "fqdn") - hostname: str - serial_number: str - hardware_serial_number: str - fqdn: Optional[str] = None - location: str - platform: Optional[str] = None - model: Optional[str] = None - version: Optional[str] = None - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraInterfaceL3(InfrahubModel): - _modelname = "InfraInterfaceL3" - _identifiers = ("device", "name") - _attributes = ("description", "speed", "mtu", "mac_address") - name: str - description: Optional[str] = None - speed: Optional[int] = None - mtu: Optional[int] = 1500 - mac_address: Optional[str] = None - device: str - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraNOSVersion(InfrahubModel): - _modelname = "InfraNOSVersion" - _identifiers = ("manufacturer", "model", "version") - _attributes = ("platform",) - version: str - manufacturer: str - platform: Optional[str] = None - model: Optional[str] = None - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraPartNumber(InfrahubModel): - _modelname = "InfraPartNumber" - _identifiers = ("device", "name") - _attributes = ("manufacturer", "model", "description", "part_id", "part_sn", "part_vid") - name: str - description: Optional[str] = None - part_id: Optional[str] = None - part_sn: Optional[str] = None - part_vid: Optional[str] = None - manufacturer: Optional[str] = None - model: Optional[str] = None - device: str - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraPlatform(InfrahubModel): - _modelname = "InfraPlatform" - _identifiers = ("name",) - _attributes = ("description",) - name: str - description: Optional[str] = None - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraVLAN(InfrahubModel): - _modelname = "InfraVLAN" - _identifiers = ("location", "vlan_id") - _attributes = ("name", "description") - name: Optional[str] = None - description: Optional[str] = None - vlan_id: int - location: str - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraVRF(InfrahubModel): - _modelname = "InfraVRF" - _identifiers = ("name",) - _attributes = ("vrf_rd",) - name: str - vrf_rd: Optional[str] = None - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class LocationGeneric(InfrahubModel): - _modelname = "LocationGeneric" - _identifiers = ("name",) - _attributes = ("description", "type") - name: str - description: Optional[str] = None - type: str - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class OrganizationGeneric(InfrahubModel): - _modelname = "OrganizationGeneric" - _identifiers = ("name",) - _attributes = ("type",) - name: str - type: Optional[str] = None - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class TemplateDeviceType(InfrahubModel): - _modelname = "TemplateDeviceType" - _identifiers = ("name", "manufacturer") - _attributes = ("description",) - name: str - description: Optional[str] = None - manufacturer: Optional[str] = None - local_id: Optional[str] = None - local_data: Optional[Any] = None diff --git a/sync/examples/ipfabric_to_infrahub/ipfabricsync/sync_adapter.py b/sync/examples/ipfabric_to_infrahub/ipfabricsync/sync_adapter.py deleted file mode 100644 index 94ce6b4e20..0000000000 --- a/sync/examples/ipfabric_to_infrahub/ipfabricsync/sync_adapter.py +++ /dev/null @@ -1,32 +0,0 @@ -from infrahub_sync.adapters.ipfabricsync import IpfabricsyncAdapter - -from .sync_models import ( - InfraDevice, - InfraInterfaceL3, - InfraNOSVersion, - InfraPartNumber, - InfraPlatform, - InfraVLAN, - InfraVRF, - LocationGeneric, - OrganizationGeneric, - TemplateDeviceType, -) - - -# ------------------------------------------------------- -# AUTO-GENERATED FILE, DO NOT MODIFY -# This file has been generated with the command `infrahub-sync generate` -# All modifications will be lost the next time you reexecute this command -# ------------------------------------------------------- -class IpfabricsyncSync(IpfabricsyncAdapter): - InfraDevice = InfraDevice - InfraInterfaceL3 = InfraInterfaceL3 - InfraNOSVersion = InfraNOSVersion - InfraPartNumber = InfraPartNumber - InfraPlatform = InfraPlatform - InfraVLAN = InfraVLAN - InfraVRF = InfraVRF - LocationGeneric = LocationGeneric - OrganizationGeneric = OrganizationGeneric - TemplateDeviceType = TemplateDeviceType diff --git a/sync/examples/ipfabric_to_infrahub/ipfabricsync/sync_models.py b/sync/examples/ipfabric_to_infrahub/ipfabricsync/sync_models.py deleted file mode 100644 index df726188ec..0000000000 --- a/sync/examples/ipfabric_to_infrahub/ipfabricsync/sync_models.py +++ /dev/null @@ -1,131 +0,0 @@ -from typing import Any, Optional - -from infrahub_sync.adapters.ipfabricsync import IpfabricsyncModel - -# ------------------------------------------------------- -# AUTO-GENERATED FILE, DO NOT MODIFY -# This file has been generated with the command `infrahub-sync generate` -# All modifications will be lost the next time you reexecute this command -# ------------------------------------------------------- - - -class InfraDevice(IpfabricsyncModel): - _modelname = "InfraDevice" - _identifiers = ("location", "hostname") - _attributes = ("platform", "model", "version", "serial_number", "hardware_serial_number", "fqdn") - hostname: str - serial_number: str - hardware_serial_number: str - fqdn: Optional[str] = None - location: str - platform: Optional[str] = None - model: Optional[str] = None - version: Optional[str] = None - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraInterfaceL3(IpfabricsyncModel): - _modelname = "InfraInterfaceL3" - _identifiers = ("device", "name") - _attributes = ("description", "speed", "mtu", "mac_address") - name: str - description: Optional[str] = None - speed: Optional[int] = None - mtu: Optional[int] = 1500 - mac_address: Optional[str] = None - device: str - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraNOSVersion(IpfabricsyncModel): - _modelname = "InfraNOSVersion" - _identifiers = ("manufacturer", "model", "version") - _attributes = ("platform",) - version: str - manufacturer: str - platform: Optional[str] = None - model: Optional[str] = None - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraPartNumber(IpfabricsyncModel): - _modelname = "InfraPartNumber" - _identifiers = ("device", "name") - _attributes = ("manufacturer", "model", "description", "part_id", "part_sn", "part_vid") - name: str - description: Optional[str] = None - part_id: Optional[str] = None - part_sn: Optional[str] = None - part_vid: Optional[str] = None - manufacturer: Optional[str] = None - model: Optional[str] = None - device: str - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraPlatform(IpfabricsyncModel): - _modelname = "InfraPlatform" - _identifiers = ("name",) - _attributes = ("description",) - name: str - description: Optional[str] = None - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraVLAN(IpfabricsyncModel): - _modelname = "InfraVLAN" - _identifiers = ("location", "vlan_id") - _attributes = ("name", "description") - name: Optional[str] = None - description: Optional[str] = None - vlan_id: int - location: str - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraVRF(IpfabricsyncModel): - _modelname = "InfraVRF" - _identifiers = ("name",) - _attributes = ("vrf_rd",) - name: str - vrf_rd: Optional[str] = None - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class LocationGeneric(IpfabricsyncModel): - _modelname = "LocationGeneric" - _identifiers = ("name",) - _attributes = ("description", "type") - name: str - description: Optional[str] = None - type: str - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class OrganizationGeneric(IpfabricsyncModel): - _modelname = "OrganizationGeneric" - _identifiers = ("name",) - _attributes = ("type",) - name: str - type: Optional[str] = None - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class TemplateDeviceType(IpfabricsyncModel): - _modelname = "TemplateDeviceType" - _identifiers = ("name", "manufacturer") - _attributes = ("description",) - name: str - description: Optional[str] = None - manufacturer: Optional[str] = None - local_id: Optional[str] = None - local_data: Optional[Any] = None diff --git a/sync/examples/librenms_to_infrahub/config.yml b/sync/examples/librenms_to_infrahub/config.yml deleted file mode 100644 index e794aeac29..0000000000 --- a/sync/examples/librenms_to_infrahub/config.yml +++ /dev/null @@ -1,68 +0,0 @@ ---- -name: from-librenms - -source: - name: librenms - settings: - url: "http://localhost:8080" - # api_endpoint: "api/v0" - # auth_method: "x-auth-token" - # token: "LIBRENMS_TOKEN" - -destination: - name: infrahub - settings: - url: "http://localhost:8000" - -order: [ - "CoreStandardGroup", - "LocationSite", - "IpamIPAddress", - "InfraDevice", -] - -schema_mapping: - - name: CoreStandardGroup - mapping: devicegroups - fields: - - name: name - mapping: name - - name: description - mapping: desc - - - name: LocationSite - mapping: resources/locations - identifiers: ["name"] - fields: - - name: name - mapping: location - - name: description - mapping: location - - # Primary Device IP - - name: IpamIPAddress - mapping: devices - identifiers: ["address"] - fields: - - name: address - mapping: ip - - name: description - mapping: hostname - - - name: InfraDevice - mapping: devices - identifiers: ["name"] - filters: - - field: hostname - operation: contains - value: "xxx" - fields: - - name: name - mapping: hostname - - name: serial_number - mapping: serial - - name: type - mapping: hardware - - name: site - mapping: location - reference: LocationSite diff --git a/sync/examples/librenms_to_infrahub/infrahub/sync_adapter.py b/sync/examples/librenms_to_infrahub/infrahub/sync_adapter.py deleted file mode 100644 index fa13a3a8d7..0000000000 --- a/sync/examples/librenms_to_infrahub/infrahub/sync_adapter.py +++ /dev/null @@ -1,20 +0,0 @@ -from infrahub_sync.adapters.infrahub import InfrahubAdapter - -from .sync_models import ( - CoreStandardGroup, - InfraDevice, - IpamIPAddress, - LocationSite, -) - - -# ------------------------------------------------------- -# AUTO-GENERATED FILE, DO NOT MODIFY -# This file has been generated with the command `infrahub-sync generate` -# All modifications will be lost the next time you reexecute this command -# ------------------------------------------------------- -class InfrahubSync(InfrahubAdapter): - CoreStandardGroup = CoreStandardGroup - InfraDevice = InfraDevice - IpamIPAddress = IpamIPAddress - LocationSite = LocationSite diff --git a/sync/examples/librenms_to_infrahub/infrahub/sync_models.py b/sync/examples/librenms_to_infrahub/infrahub/sync_models.py deleted file mode 100644 index 82d5ebd25d..0000000000 --- a/sync/examples/librenms_to_infrahub/infrahub/sync_models.py +++ /dev/null @@ -1,54 +0,0 @@ -from typing import Any, Optional - -from infrahub_sync.adapters.infrahub import InfrahubModel - - -# ------------------------------------------------------- -# AUTO-GENERATED FILE, DO NOT MODIFY -# This file has been generated with the command `infrahub-sync generate` -# All modifications will be lost the next time you reexecute this command -# ------------------------------------------------------- -class CoreStandardGroup(InfrahubModel): - _modelname = "CoreStandardGroup" - _identifiers = ("name",) - _attributes = ("description",) - name: str - description: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraDevice(InfrahubModel): - _modelname = "InfraDevice" - _identifiers = ("name",) - _attributes = ("site", "type", "serial_number") - name: str - type: str - serial_number: str - site: str - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class IpamIPAddress(InfrahubModel): - _modelname = "IpamIPAddress" - _identifiers = ("address",) - _attributes = ("description",) - address: str - description: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class LocationSite(InfrahubModel): - _modelname = "LocationSite" - _identifiers = ("name",) - _attributes = ("description",) - name: str - description: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None diff --git a/sync/examples/librenms_to_infrahub/librenms/sync_adapter.py b/sync/examples/librenms_to_infrahub/librenms/sync_adapter.py deleted file mode 100644 index bfe97c6af3..0000000000 --- a/sync/examples/librenms_to_infrahub/librenms/sync_adapter.py +++ /dev/null @@ -1,20 +0,0 @@ -from infrahub_sync.adapters.librenms import LibrenmsAdapter - -from .sync_models import ( - CoreStandardGroup, - InfraDevice, - IpamIPAddress, - LocationSite, -) - - -# ------------------------------------------------------- -# AUTO-GENERATED FILE, DO NOT MODIFY -# This file has been generated with the command `infrahub-sync generate` -# All modifications will be lost the next time you reexecute this command -# ------------------------------------------------------- -class LibrenmsSync(LibrenmsAdapter): - CoreStandardGroup = CoreStandardGroup - InfraDevice = InfraDevice - IpamIPAddress = IpamIPAddress - LocationSite = LocationSite diff --git a/sync/examples/librenms_to_infrahub/librenms/sync_models.py b/sync/examples/librenms_to_infrahub/librenms/sync_models.py deleted file mode 100644 index 96e3cb2b2a..0000000000 --- a/sync/examples/librenms_to_infrahub/librenms/sync_models.py +++ /dev/null @@ -1,54 +0,0 @@ -from typing import Any, Optional - -from infrahub_sync.adapters.librenms import LibrenmsModel - - -# ------------------------------------------------------- -# AUTO-GENERATED FILE, DO NOT MODIFY -# This file has been generated with the command `infrahub-sync generate` -# All modifications will be lost the next time you reexecute this command -# ------------------------------------------------------- -class CoreStandardGroup(LibrenmsModel): - _modelname = "CoreStandardGroup" - _identifiers = ("name",) - _attributes = ("description",) - name: str - description: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraDevice(LibrenmsModel): - _modelname = "InfraDevice" - _identifiers = ("name",) - _attributes = ("site", "type", "serial_number") - name: str - type: str - serial_number: str - site: str - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class IpamIPAddress(LibrenmsModel): - _modelname = "IpamIPAddress" - _identifiers = ("address",) - _attributes = ("description",) - address: str - description: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class LocationSite(LibrenmsModel): - _modelname = "LocationSite" - _identifiers = ("name",) - _attributes = ("description",) - name: str - description: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None diff --git a/sync/examples/nautobot-v1_to_infrahub/config.yml b/sync/examples/nautobot-v1_to_infrahub/config.yml deleted file mode 100644 index 3c0f8d95a0..0000000000 --- a/sync/examples/nautobot-v1_to_infrahub/config.yml +++ /dev/null @@ -1,522 +0,0 @@ ---- -name: from-nautobot-v1 -source: - name: nautobot - settings: - url: "http://nautobot:8080" - -destination: - name: infrahub - settings: - url: "http://localhost:8000" - -order: [ - "BuiltinTag", - "RoleGeneric", - # "StatusGeneric", - "CoreStandardGroup", - "TemplateLocationType", - "OgranizationGeneric", - "LocationGeneric", - "InfraRack", - "TemplateDeviceType", - "InfraPlatform", - "InfraProviderNetwork", - "TemplateCircuitType", - "InfraCircuit", - "InfraRouteTarget", - "InfraVRF", - "InfraDevice", - "InfraVLAN", - "InfraPrefix", - "InfraIPAddress", - "InfraRearPort", - "InfraFrontPort", - "InfraInterfaceL2L3" -] - -schema_mapping: - # Tags - - name: BuiltinTag - mapping: extras.tags - identifiers: ["name"] - fields: - - name: name - mapping: name - - name: description - mapping: description - - # Roles (Device Role, Rack Role, IPAM Role) - - name: RoleGeneric - mapping: dcim.device_roles - identifiers: ["name"] - fields: - - name: name - mapping: name - - name: description - mapping: description - - name: RoleGeneric - mapping: dcim.rack_roles - identifiers: ["name"] - fields: - - name: name - mapping: name - - name: description - mapping: description - - name: RoleGeneric - mapping: ipam.roles - identifiers: ["name"] - fields: - - name: name - mapping: name - - name: description - mapping: description - - # Status - - name: StatusGeneric - mapping: extras.statuses - identifiers: ["name"] - fields: - - name: name - mapping: name - - name: description - mapping: description - - name: label - mapping: display - - # Tenancy (tenants, tenant groups) - - name: CoreStandardGroup - mapping: tenancy.tenant-groups - identifiers: ["name"] - fields: - - name: name - mapping: name - - name: description - mapping: description - - name: OgranizationGeneric - mapping: tenancy.tenants - identifiers: ["name"] - fields: - - name: name - mapping: name - - name: description - mapping: description - - name: type - static: "Tenant" - - name: group - mapping: group - reference: CoreStandardGroup - - # Sites (Region, Sites, Location) - - name: TemplateLocationType - mapping: dcim.location-types - identifiers: ["name"] - fields: - - name: name - mapping: name - - name: description - mapping: description - - name: LocationGeneric - mapping: dcim.regions - fields: - - name: name - mapping: slug - - name: description - mapping: name - - name: type - static: "Region" - - name: LocationGeneric - mapping: dcim.sites - fields: - - name: name - mapping: slug - - name: description - mapping: name - - name: type - static: "Site" - - name: organization - mapping: tenant - reference: OgranizationGeneric - - name: LocationGeneric - mapping: dcim.locations - fields: - - name: name - mapping: slug - - name: description - mapping: name - - name: type - static: "Location" - - name: location_type - mapping: location_type - reference: TemplateLocationType - - name: organization - mapping: tenant - reference: OgranizationGeneric - - # Racks - - name: InfraRack - mapping: dcim.racks - identifiers: ["name"] - fields: - - name: name - mapping: name - - name: location - mapping: location - reference: LocationGeneric - - name: site - mapping: site - reference: LocationGeneric - - name: height - mapping: u_height - - name: serial_number - mapping: serial - - name: asset_tag - mapping: asset_tag - - name: facility_id - mapping: facility_id - - name: role - mapping: role - reference: RoleGeneric - - name: tags - mapping: tags - reference: BuiltinTag - - # Device (manufacturer, device types, platform, devices) - - name: OgranizationGeneric - mapping: dcim.manufacturers - identifiers: ["name"] - fields: - - name: name - mapping: name - - name: description - mapping: description - - name: type - static: "Manufacturer" - - name: InfraPlatform - mapping: dcim.platforms - identifiers: ["name", "manufacturer"] - fields: - - name: name - mapping: name - - name: description - mapping: description - - name: napalm_driver - mapping: napalm_driver - - name: manufacturer - mapping: manufacturer - reference: OgranizationGeneric - - name: TemplateDeviceType - mapping: dcim.device-types - identifiers: ["name", "manufacturer"] - fields: - - name: name - mapping: model - - name: part_number - mapping: part_number - - name: full_depth - mapping: is_full_depth - - name: height - mapping: u_height - - name: tags - mapping: tags - reference: BuiltinTag - - name: manufacturer - mapping: manufacturer - reference: OgranizationGeneric - # -> Device validate_unique() - # https://github.com/nautobot/nautobot/blob/develop/nautobot/dcim/models/devices.py#L541C9-L541C24 - - name: InfraDevice - identifiers: ["location", "organization", "name"] - mapping: dcim.devices - fields: - - name: name - mapping: name - - name: serial_number - mapping: serial - - name: asset_tag - mapping: asset_tag - - name: model - mapping: device_type - reference: TemplateDeviceType - - name: platform - mapping: platform - reference: InfraPlatform - - name: organization - mapping: tenant - reference: OgranizationGeneric - - name: role - mapping: device_role - reference: RoleGeneric - # - name: status - # mapping: status - # reference: StatusGeneric - - name: location - mapping: location - reference: LocationGeneric - - name: rack - mapping: rack - reference: InfraRack - - name: tags - mapping: tags - reference: BuiltinTag - - # Circuits (Provider, Provider Network, Circuits Types, Circuits) - - name: OgranizationGeneric - mapping: circuits.providers - identifiers: ["name"] - fields: - - name: name - mapping: name - - name: type - static: "Provider" - - name: InfraProviderNetwork - mapping: circuits.provider-networks - identifiers: ["name"] - fields: - - name: name - mapping: name - - name: vendor_id - mapping: service_id - - name: provider - mapping: provider.slug - - name: description - mapping: description - # - name: status - # mapping: status - # reference: StatusGeneric - - name: tags - mapping: tags - reference: BuiltinTag - - name: provider - mapping: provider - reference: OgranizationGeneric - - name: TemplateCircuitType - mapping: circuits.circuit-types - identifiers: ["name"] - fields: - - name: name - mapping: name - - name: description - mapping: description - - name: InfraCircuit - mapping: circuits.circuits - identifiers: ["circuit_id"] - fields: - - name: circuit_id - mapping: cid - - name: vendor_id - mapping: cid - - name: provider - mapping: provider - reference: OgranizationGeneric - - name: description - mapping: description - # - name: status - # mapping: status - # reference: StatusGeneric - - name: type - mapping: type - reference: TemplateCircuitType - # - name: tenant - # mapping: tenant - # reference: OgranizationGeneric - - name: tags - mapping: tags - reference: BuiltinTag - - # BGP Plugin (Autonomous System, BGP Session, BGP Peer Group) - - name: InfraAutonomousSystem - mapping: plugin.bgp.autonomous-systems - identifiers: ["name"] - fields: - - name: name - mapping: asn - - name: asn - mapping: asn - - name: description - mapping: description - - name: organization - mapping: provider - reference: OgranizationGeneric - -# IPAM (VRF, VLANs Groups, VLANs, Prefixes, IPs) - - name: InfraRouteTarget - mapping: ipam.route-targets - identifiers: ["name", "organization"] - fields: - - name: name - mapping: name - - name: description - mapping: description - - name: organization - mapping: tenant - reference: OgranizationGeneric - - name: InfraVRF - mapping: ipam.vrfs - identifiers: ["name"] - fields: - - name: name - mapping: name - - name: description - mapping: description - - name: organization - mapping: tenant - reference: OgranizationGeneric - - name: vrf_rd - mapping: rd - - name: import_rt - mapping: import_targets - reference: InfraRouteTarget - - name: export_rt - mapping: export_targets - reference: InfraRouteTarget - - name: CoreStandardGroup - mapping: ipam.vlan-groups - identifiers: ["name"] - fields: - - name: name - mapping: name - - name: description - mapping: description - # - name: location - # mapping: location - # reference: LocationGeneric - - name: InfraVLAN - mapping: ipam.vlans - identifiers: ["name", "vlan_id", "location", "organization"] - fields: - - name: name - mapping: name - - name: vlan_id - mapping: vid - - name: organization - mapping: tenant - reference: OgranizationGeneric - - name: description - mapping: description - - name: location - mapping: location - reference: LocationGeneric - - name: role - mapping: role - reference: RoleGeneric - # - name: status - # mapping: status - # reference: StatusGeneric - - name: vlan_group - mapping: group - reference: CoreStandardGroup - - name: InfraPrefix - mapping: ipam.prefixes - identifiers: ["prefix", "vrf", "organization"] - fields: - - name: prefix - mapping: prefix - - name: description - mapping: description - - name: organization - mapping: tenant - reference: OgranizationGeneric - - name: vrf - mapping: vrf - reference: InfraVRF - - name: role - mapping: role - reference: RoleGeneric - # - name: status - # mapping: status - # reference: StatusGeneric - - name: vlan - mapping: vlan - reference: InfraVLAN - - name: location - mapping: location - reference: LocationGeneric - - name: InfraIPAddress - mapping: ipam.ip-addresses - identifiers: ["address", "vrf"] - fields: - - name: address - mapping: address - - name: description - mapping: description - - name: organization - mapping: tenant - reference: OgranizationGeneric - - name: vrf - mapping: vrf - reference: InfraVRF - - name: role - mapping: role - reference: RoleGeneric - # - name: status - # mapping: status - # reference: StatusGeneric - - # Interfaces (interfaces, rear port, front port) - - name: InfraInterfaceL2L3 - identifiers: ["name", "device"] - mapping: dcim.interfaces - fields: - - name: name - mapping: name - - name: description - mapping: description - - name: interface_type - mapping: type.label - - name: l2_mode - mapping: mode.label - - name: mac_address - mapping: mac_address - - name: mgmt_only - mapping: mgmt_only - - name: untagged_vlan - mapping: untagged_vlan - reference: InfraVLAN - - name: tagged_vlan - mapping: tagged_vlans - reference: InfraVLAN - - name: device - mapping: device - reference: InfraDevice - # - name: status - # mapping: status - # reference: StatusGeneric - - name: tags - mapping: tags - reference: BuiltinTag - - name: InfraFrontPort - identifiers: ["name", "device"] - mapping: dcim.front-ports - fields: - - name: name - mapping: name - - name: description - mapping: description - - name: port_type - mapping: type.label - - name: rear_port - mapping: rear_port - reference: InfraRearPort - - name: device - mapping: device - reference: InfraDevice - # - name: status - # mapping: status - # reference: StatusGeneric - - name: InfraRearPort - identifiers: ["name", "device"] - mapping: dcim.rear-ports - fields: - - name: name - mapping: name - - name: description - mapping: description - - name: port_type - mapping: type.label - - name: device - mapping: device - reference: InfraDevice - # - name: status - # mapping: status - # reference: StatusGeneric diff --git a/sync/examples/nautobot-v1_to_infrahub/infrahub/sync_adapter.py b/sync/examples/nautobot-v1_to_infrahub/infrahub/sync_adapter.py deleted file mode 100644 index f3956cc2ec..0000000000 --- a/sync/examples/nautobot-v1_to_infrahub/infrahub/sync_adapter.py +++ /dev/null @@ -1,56 +0,0 @@ -from infrahub_sync.adapters.infrahub import InfrahubAdapter - -from .sync_models import ( - BuiltinTag, - CoreStandardGroup, - InfraAutonomousSystem, - InfraCircuit, - InfraDevice, - InfraFrontPort, - InfraInterfaceL2L3, - InfraIPAddress, - InfraPlatform, - InfraPrefix, - InfraProviderNetwork, - InfraRack, - InfraRearPort, - InfraRouteTarget, - InfraVLAN, - InfraVRF, - LocationGeneric, - RoleGeneric, - StatusGeneric, - TemplateCircuitType, - TemplateDeviceType, - TemplateLocationType, -) - - -# ------------------------------------------------------- -# AUTO-GENERATED FILE, DO NOT MODIFY -# This file has been generated with the command `infrahub-sync generate` -# All modifications will be lost the next time you reexecute this command -# ------------------------------------------------------- -class InfrahubSync(InfrahubAdapter): - CoreStandardGroup = CoreStandardGroup - BuiltinTag = BuiltinTag - InfraAutonomousSystem = InfraAutonomousSystem - InfraCircuit = InfraCircuit - InfraDevice = InfraDevice - InfraFrontPort = InfraFrontPort - InfraIPAddress = InfraIPAddress - InfraInterfaceL2L3 = InfraInterfaceL2L3 - InfraPlatform = InfraPlatform - InfraPrefix = InfraPrefix - InfraProviderNetwork = InfraProviderNetwork - InfraRack = InfraRack - InfraRearPort = InfraRearPort - InfraRouteTarget = InfraRouteTarget - InfraVLAN = InfraVLAN - InfraVRF = InfraVRF - LocationGeneric = LocationGeneric - RoleGeneric = RoleGeneric - StatusGeneric = StatusGeneric - TemplateCircuitType = TemplateCircuitType - TemplateDeviceType = TemplateDeviceType - TemplateLocationType = TemplateLocationType diff --git a/sync/examples/nautobot-v1_to_infrahub/infrahub/sync_models.py b/sync/examples/nautobot-v1_to_infrahub/infrahub/sync_models.py deleted file mode 100644 index 3e1a312ad0..0000000000 --- a/sync/examples/nautobot-v1_to_infrahub/infrahub/sync_models.py +++ /dev/null @@ -1,291 +0,0 @@ -from typing import Any, List, Optional - -from infrahub_sync.adapters.infrahub import InfrahubModel - -# ------------------------------------------------------- -# AUTO-GENERATED FILE, DO NOT MODIFY -# This file has been generated with the command `infrahub-sync generate` -# All modifications will be lost the next time you reexecute this command -# ------------------------------------------------------- - - -class CoreStandardGroup(InfrahubModel): - _modelname = "CoreStandardGroup" - _identifiers = ("name",) - _attributes = ("description",) - name: str - description: Optional[str] = None - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class BuiltinTag(InfrahubModel): - _modelname = "BuiltinTag" - _identifiers = ("name",) - _attributes = ("description",) - name: str - description: Optional[str] = None - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraAutonomousSystem(InfrahubModel): - _modelname = "InfraAutonomousSystem" - _identifiers = ("name",) - _attributes = ("organization", "description") - name: str - asn: int - description: Optional[str] = None - organization: str - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraCircuit(InfrahubModel): - _modelname = "InfraCircuit" - _identifiers = ("circuit_id",) - _attributes = ("provider", "type", "tags", "description", "vendor_id") - circuit_id: str - description: Optional[str] = None - vendor_id: Optional[str] = None - provider: str - type: str - tags: Optional[List[str]] = [] - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraDevice(InfrahubModel): - _modelname = "InfraDevice" - _identifiers = ("location", "organization", "name") - _attributes = ("model", "rack", "role", "tags", "platform", "serial_number", "asset_tag") - name: Optional[str] = None - serial_number: Optional[str] = None - asset_tag: Optional[str] = None - location: str - model: str - rack: Optional[str] = None - role: Optional[str] = None - tags: Optional[List[str]] = [] - platform: Optional[str] = None - organization: Optional[str] = None - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraFrontPort(InfrahubModel): - _modelname = "InfraFrontPort" - _identifiers = ("name", "device") - _attributes = ("rear_port", "description", "port_type") - name: str - description: Optional[str] = None - port_type: Optional[str] = None - rear_port: Optional[List[str]] = [] - device: str - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraIPAddress(InfrahubModel): - _modelname = "InfraIPAddress" - _identifiers = ("address", "vrf") - _attributes = ("organization", "role", "description") - address: str - description: Optional[str] = None - organization: Optional[str] = None - vrf: Optional[str] = None - role: Optional[str] = None - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraInterfaceL2L3(InfrahubModel): - _modelname = "InfraInterfaceL2L3" - _identifiers = ("name", "device") - _attributes = ("tagged_vlan", "tags", "l2_mode", "description", "mgmt_only", "mac_address", "interface_type") - l2_mode: Optional[str] = None - name: str - description: Optional[str] = None - mgmt_only: Optional[bool] = False - mac_address: Optional[str] = None - interface_type: Optional[str] = None - untagged_vlan: Optional[str] = None - tagged_vlan: Optional[List[str]] = [] - device: str - tags: Optional[List[str]] = [] - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraPlatform(InfrahubModel): - _modelname = "InfraPlatform" - _identifiers = ("name", "manufacturer") - _attributes = ("description", "napalm_driver") - name: str - description: Optional[str] = None - napalm_driver: Optional[str] = None - manufacturer: str - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraPrefix(InfrahubModel): - _modelname = "InfraPrefix" - _identifiers = ("prefix", "vrf", "organization") - _attributes = ("role", "vlan", "description") - prefix: str - description: Optional[str] = None - organization: Optional[str] = None - role: Optional[str] = None - vrf: Optional[str] = None - vlan: Optional[str] = None - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraProviderNetwork(InfrahubModel): - _modelname = "InfraProviderNetwork" - _identifiers = ("name",) - _attributes = ("provider", "tags", "description", "vendor_id") - name: str - description: Optional[str] = None - vendor_id: Optional[str] = None - provider: str - tags: Optional[List[str]] = [] - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraRack(InfrahubModel): - _modelname = "InfraRack" - _identifiers = ("name",) - _attributes = ("location", "role", "tags", "height", "facility_id", "serial_number", "asset_tag") - name: str - height: Optional[int] = None - facility_id: Optional[str] = None - serial_number: Optional[str] = None - asset_tag: Optional[str] = None - location: str - role: Optional[str] = None - tags: Optional[List[str]] = [] - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraRearPort(InfrahubModel): - _modelname = "InfraRearPort" - _identifiers = ("name", "device") - _attributes = ("description", "port_type") - name: str - description: Optional[str] = None - port_type: Optional[str] = None - device: str - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraRouteTarget(InfrahubModel): - _modelname = "InfraRouteTarget" - _identifiers = ("name", "organization") - _attributes = ("description",) - name: str - description: Optional[str] = None - organization: Optional[str] = None - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraVLAN(InfrahubModel): - _modelname = "InfraVLAN" - _identifiers = ("name", "vlan_id", "location", "organization") - _attributes = ("role", "vlan_group", "description") - name: str - description: Optional[str] = None - vlan_id: int - organization: Optional[str] = None - role: Optional[str] = None - vlan_group: Optional[str] = None - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraVRF(InfrahubModel): - _modelname = "InfraVRF" - _identifiers = ("name",) - _attributes = ("organization", "import_rt", "export_rt", "description", "vrf_rd") - name: str - description: Optional[str] = None - vrf_rd: Optional[str] = None - organization: Optional[str] = None - import_rt: Optional[List[str]] = [] - export_rt: Optional[List[str]] = [] - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class LocationGeneric(InfrahubModel): - _modelname = "LocationGeneric" - _identifiers = ("name",) - _attributes = ("organization", "location_type", "description", "type") - name: str - description: Optional[str] = None - type: str - organization: Optional[str] = None - location_type: Optional[str] = None - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class RoleGeneric(InfrahubModel): - _modelname = "RoleGeneric" - _identifiers = ("name",) - _attributes = ("description",) - name: str - description: Optional[str] = None - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class StatusGeneric(InfrahubModel): - _modelname = "StatusGeneric" - _identifiers = ("name",) - _attributes = ("label", "description") - name: str - label: Optional[str] = None - description: Optional[str] = None - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class TemplateCircuitType(InfrahubModel): - _modelname = "TemplateCircuitType" - _identifiers = ("name",) - _attributes = ("description",) - name: str - description: Optional[str] = None - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class TemplateDeviceType(InfrahubModel): - _modelname = "TemplateDeviceType" - _identifiers = ("name", "manufacturer") - _attributes = ("tags", "part_number", "height", "full_depth") - part_number: Optional[str] = None - height: Optional[int] = None - full_depth: Optional[bool] = None - name: str - manufacturer: str - tags: Optional[List[str]] = [] - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class TemplateLocationType(InfrahubModel): - _modelname = "TemplateLocationType" - _identifiers = ("name",) - _attributes = ("description",) - name: str - description: Optional[str] = None - local_id: Optional[str] = None - local_data: Optional[Any] = None diff --git a/sync/examples/nautobot-v1_to_infrahub/nautobot/sync_adapter.py b/sync/examples/nautobot-v1_to_infrahub/nautobot/sync_adapter.py deleted file mode 100644 index 0038d2c923..0000000000 --- a/sync/examples/nautobot-v1_to_infrahub/nautobot/sync_adapter.py +++ /dev/null @@ -1,56 +0,0 @@ -from infrahub_sync.adapters.nautobot import NautobotAdapter - -from .sync_models import ( - BuiltinTag, - CoreStandardGroup, - InfraAutonomousSystem, - InfraCircuit, - InfraDevice, - InfraFrontPort, - InfraInterfaceL2L3, - InfraIPAddress, - InfraPlatform, - InfraPrefix, - InfraProviderNetwork, - InfraRack, - InfraRearPort, - InfraRouteTarget, - InfraVLAN, - InfraVRF, - LocationGeneric, - RoleGeneric, - StatusGeneric, - TemplateCircuitType, - TemplateDeviceType, - TemplateLocationType, -) - - -# ------------------------------------------------------- -# AUTO-GENERATED FILE, DO NOT MODIFY -# This file has been generated with the command `infrahub-sync generate` -# All modifications will be lost the next time you reexecute this command -# ------------------------------------------------------- -class NautobotSync(NautobotAdapter): - CoreStandardGroup = CoreStandardGroup - BuiltinTag = BuiltinTag - InfraAutonomousSystem = InfraAutonomousSystem - InfraCircuit = InfraCircuit - InfraDevice = InfraDevice - InfraFrontPort = InfraFrontPort - InfraIPAddress = InfraIPAddress - InfraInterfaceL2L3 = InfraInterfaceL2L3 - InfraPlatform = InfraPlatform - InfraPrefix = InfraPrefix - InfraProviderNetwork = InfraProviderNetwork - InfraRack = InfraRack - InfraRearPort = InfraRearPort - InfraRouteTarget = InfraRouteTarget - InfraVLAN = InfraVLAN - InfraVRF = InfraVRF - LocationGeneric = LocationGeneric - RoleGeneric = RoleGeneric - StatusGeneric = StatusGeneric - TemplateCircuitType = TemplateCircuitType - TemplateDeviceType = TemplateDeviceType - TemplateLocationType = TemplateLocationType diff --git a/sync/examples/nautobot-v1_to_infrahub/nautobot/sync_models.py b/sync/examples/nautobot-v1_to_infrahub/nautobot/sync_models.py deleted file mode 100644 index e907698f7d..0000000000 --- a/sync/examples/nautobot-v1_to_infrahub/nautobot/sync_models.py +++ /dev/null @@ -1,291 +0,0 @@ -from typing import Any, List, Optional - -from infrahub_sync.adapters.nautobot import NautobotModel - -# ------------------------------------------------------- -# AUTO-GENERATED FILE, DO NOT MODIFY -# This file has been generated with the command `infrahub-sync generate` -# All modifications will be lost the next time you reexecute this command -# ------------------------------------------------------- - - -class CoreStandardGroup(NautobotModel): - _modelname = "CoreStandardGroup" - _identifiers = ("name",) - _attributes = ("description",) - name: str - description: Optional[str] = None - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class BuiltinTag(NautobotModel): - _modelname = "BuiltinTag" - _identifiers = ("name",) - _attributes = ("description",) - name: str - description: Optional[str] = None - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraAutonomousSystem(NautobotModel): - _modelname = "InfraAutonomousSystem" - _identifiers = ("name",) - _attributes = ("organization", "description") - name: str - asn: int - description: Optional[str] = None - organization: str - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraCircuit(NautobotModel): - _modelname = "InfraCircuit" - _identifiers = ("circuit_id",) - _attributes = ("provider", "type", "tags", "description", "vendor_id") - circuit_id: str - description: Optional[str] = None - vendor_id: Optional[str] = None - provider: str - type: str - tags: Optional[List[str]] = [] - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraDevice(NautobotModel): - _modelname = "InfraDevice" - _identifiers = ("location", "organization", "name") - _attributes = ("model", "rack", "role", "tags", "platform", "serial_number", "asset_tag") - name: Optional[str] = None - serial_number: Optional[str] = None - asset_tag: Optional[str] = None - location: str - model: str - rack: Optional[str] = None - role: Optional[str] = None - tags: Optional[List[str]] = [] - platform: Optional[str] = None - organization: Optional[str] = None - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraFrontPort(NautobotModel): - _modelname = "InfraFrontPort" - _identifiers = ("name", "device") - _attributes = ("rear_port", "description", "port_type") - name: str - description: Optional[str] = None - port_type: Optional[str] = None - rear_port: Optional[List[str]] = [] - device: str - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraIPAddress(NautobotModel): - _modelname = "InfraIPAddress" - _identifiers = ("address", "vrf") - _attributes = ("organization", "role", "description") - address: str - description: Optional[str] = None - organization: Optional[str] = None - vrf: Optional[str] = None - role: Optional[str] = None - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraInterfaceL2L3(NautobotModel): - _modelname = "InfraInterfaceL2L3" - _identifiers = ("name", "device") - _attributes = ("tagged_vlan", "tags", "l2_mode", "description", "mgmt_only", "mac_address", "interface_type") - l2_mode: Optional[str] = None - name: str - description: Optional[str] = None - mgmt_only: Optional[bool] = False - mac_address: Optional[str] = None - interface_type: Optional[str] = None - untagged_vlan: Optional[str] = None - tagged_vlan: Optional[List[str]] = [] - device: str - tags: Optional[List[str]] = [] - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraPlatform(NautobotModel): - _modelname = "InfraPlatform" - _identifiers = ("name", "manufacturer") - _attributes = ("description", "napalm_driver") - name: str - description: Optional[str] = None - napalm_driver: Optional[str] = None - manufacturer: str - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraPrefix(NautobotModel): - _modelname = "InfraPrefix" - _identifiers = ("prefix", "vrf", "organization") - _attributes = ("role", "vlan", "description") - prefix: str - description: Optional[str] = None - organization: Optional[str] = None - role: Optional[str] = None - vrf: Optional[str] = None - vlan: Optional[str] = None - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraProviderNetwork(NautobotModel): - _modelname = "InfraProviderNetwork" - _identifiers = ("name",) - _attributes = ("provider", "tags", "description", "vendor_id") - name: str - description: Optional[str] = None - vendor_id: Optional[str] = None - provider: str - tags: Optional[List[str]] = [] - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraRack(NautobotModel): - _modelname = "InfraRack" - _identifiers = ("name",) - _attributes = ("location", "role", "tags", "height", "facility_id", "serial_number", "asset_tag") - name: str - height: Optional[int] = None - facility_id: Optional[str] = None - serial_number: Optional[str] = None - asset_tag: Optional[str] = None - location: str - role: Optional[str] = None - tags: Optional[List[str]] = [] - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraRearPort(NautobotModel): - _modelname = "InfraRearPort" - _identifiers = ("name", "device") - _attributes = ("description", "port_type") - name: str - description: Optional[str] = None - port_type: Optional[str] = None - device: str - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraRouteTarget(NautobotModel): - _modelname = "InfraRouteTarget" - _identifiers = ("name", "organization") - _attributes = ("description",) - name: str - description: Optional[str] = None - organization: Optional[str] = None - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraVLAN(NautobotModel): - _modelname = "InfraVLAN" - _identifiers = ("name", "vlan_id", "location", "organization") - _attributes = ("role", "vlan_group", "description") - name: str - description: Optional[str] = None - vlan_id: int - organization: Optional[str] = None - role: Optional[str] = None - vlan_group: Optional[str] = None - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraVRF(NautobotModel): - _modelname = "InfraVRF" - _identifiers = ("name",) - _attributes = ("organization", "import_rt", "export_rt", "description", "vrf_rd") - name: str - description: Optional[str] = None - vrf_rd: Optional[str] = None - organization: Optional[str] = None - import_rt: Optional[List[str]] = [] - export_rt: Optional[List[str]] = [] - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class LocationGeneric(NautobotModel): - _modelname = "LocationGeneric" - _identifiers = ("name",) - _attributes = ("organization", "location_type", "description", "type") - name: str - description: Optional[str] = None - type: str - organization: Optional[str] = None - location_type: Optional[str] = None - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class RoleGeneric(NautobotModel): - _modelname = "RoleGeneric" - _identifiers = ("name",) - _attributes = ("description",) - name: str - description: Optional[str] = None - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class StatusGeneric(NautobotModel): - _modelname = "StatusGeneric" - _identifiers = ("name",) - _attributes = ("label", "description") - name: str - label: Optional[str] = None - description: Optional[str] = None - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class TemplateCircuitType(NautobotModel): - _modelname = "TemplateCircuitType" - _identifiers = ("name",) - _attributes = ("description",) - name: str - description: Optional[str] = None - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class TemplateDeviceType(NautobotModel): - _modelname = "TemplateDeviceType" - _identifiers = ("name", "manufacturer") - _attributes = ("tags", "part_number", "height", "full_depth") - part_number: Optional[str] = None - height: Optional[int] = None - full_depth: Optional[bool] = None - name: str - manufacturer: str - tags: Optional[List[str]] = [] - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class TemplateLocationType(NautobotModel): - _modelname = "TemplateLocationType" - _identifiers = ("name",) - _attributes = ("description",) - name: str - description: Optional[str] = None - local_id: Optional[str] = None - local_data: Optional[Any] = None diff --git a/sync/examples/nautobot-v2_to_infrahub/config.yml b/sync/examples/nautobot-v2_to_infrahub/config.yml deleted file mode 100644 index 529211e5ff..0000000000 --- a/sync/examples/nautobot-v2_to_infrahub/config.yml +++ /dev/null @@ -1,527 +0,0 @@ ---- -name: from-nautobot-v2 -source: - name: nautobot - settings: - url: "https://demo.nautobot.com" - token: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - -destination: - name: infrahub - settings: - url: "http://localhost:8000" - -# store: -# type: redis -# settings: -# host: localhost -# port: 6379 - -order: [ - "BuiltinTag", - "RoleGeneric", - "StatusGeneric", - "CoreStandardGroup", - "OrganizationGeneric", - "TemplateLocationType", - "LocationGeneric", - "InfraRack", - "TemplateDeviceType", - "InfraPlatform", - "InfraProviderNetwork", - "TemplateCircuitType", - "InfraCircuit", - "NautobotNamespace", - "InfraRouteTarget", - "InfraVLAN", - "InfraVRF", - # "InfraDevice", - "InfraPrefix", - # "InfraInterfaceL2L3", - "InfraRearPort", - "InfraFrontPort", - # "InfraIPAddress", -] - -schema_mapping: - # Tags - - name: BuiltinTag - mapping: extras.tags - identifiers: ["name"] - fields: - - name: name - mapping: name - - name: description - mapping: description - - # Roles (Device Role, Rack Role, IPAM Role, and IPAddressRoleChoices) - - name: RoleGeneric - mapping: extras.roles - identifiers: ["name"] - fields: - - name: name - mapping: name - - name: description - mapping: description - - name: label - mapping: display - - # Status (Device Role, Rack Role, IPAM Role, and IPAddressRoleChoices) - - name: StatusGeneric - mapping: extras.statuses - identifiers: ["name"] - fields: - - name: name - mapping: new_name - - name: description - mapping: description - - name: label - mapping: new_label - # Transform the "fake" Null Status from Nautobot v2 - transforms: - - field: new_name - expression: "{{ 'default' if name == 'NULL' else name }}" - - field: new_label - expression: "{{ 'Default' if display == 'NULL' else display }}" - - # Tenancy (tenants, tenant groups) - - name: CoreStandardGroup - mapping: tenancy.tenant-groups - identifiers: ["name"] - fields: - - name: name - mapping: name - - name: description - mapping: description - - name: OrganizationGeneric - mapping: tenancy.tenants - identifiers: ["name"] - fields: - - name: name - mapping: name - - name: description - mapping: description - - name: type - static: "Tenant" - - name: group - mapping: tenant_group - reference: CoreStandardGroup - - # Locations (Location Type, Locations) - - name: TemplateLocationType - mapping: dcim.location-types - identifiers: ["name"] - fields: - - name: name - mapping: name - - name: description - mapping: description - - name: LocationGeneric - mapping: dcim.locations - identifiers: ["name"] - fields: - - name: name - mapping: name - - name: description - mapping: description - - name: location_type - mapping: location_type - reference: TemplateLocationType - - name: status - mapping: status - reference: StatusGeneric - - name: tags - mapping: tags - reference: BuiltinTag - - # Racks - - name: InfraRack - mapping: dcim.racks - identifiers: ["name"] - fields: - - name: name - mapping: name - - name: location - mapping: location - reference: LocationGeneric - - name: height - mapping: u_height - - name: serial_number - mapping: serial - - name: asset_tag - mapping: asset_tag - - name: facility_id - mapping: facility_id - - name: role - mapping: role - reference: RoleGeneric - - name: tags - mapping: tags - reference: BuiltinTag - - # Device (manufacturer, device types, platform, devices) - - name: OrganizationGeneric - mapping: dcim.manufacturers - identifiers: ["name"] - fields: - - name: name - mapping: name - - name: description - mapping: description - - name: type - static: "Manufacturer" - - name: InfraPlatform - mapping: dcim.platforms - identifiers: ["name", "manufacturer"] - fields: - - name: name - mapping: name - - name: description - mapping: description - - name: napalm_driver - mapping: napalm_driver - - name: manufacturer - mapping: manufacturer - reference: OrganizationGeneric - - name: TemplateDeviceType - mapping: dcim.device-types - identifiers: ["name", "manufacturer"] - fields: - - name: name - mapping: model - - name: part_number - mapping: part_number - - name: full_depth - mapping: is_full_depth - - name: height - mapping: u_height - - name: tags - mapping: tags - reference: BuiltinTag - - name: manufacturer - mapping: manufacturer - reference: OrganizationGeneric - # -> Device validate_unique() - # https://github.com/nautobot/nautobot/blob/develop/nautobot/dcim/models/devices.py#L541C9-L541C24 - - name: InfraDevice - identifiers: ["location", "organization", "name"] - mapping: dcim.devices - fields: - - name: name - mapping: name - - name: serial_number - mapping: serial - - name: asset_tag - mapping: asset_tag - - name: model - mapping: device_type - reference: TemplateDeviceType - - name: platform - mapping: platform - reference: InfraPlatform - - name: organization - mapping: tenant - reference: OrganizationGeneric - - name: role - mapping: role - reference: RoleGeneric - - name: status - mapping: status - reference: StatusGeneric - - name: location - mapping: location - reference: LocationGeneric - - name: rack - mapping: rack - reference: InfraRack - - name: tags - mapping: tags - reference: BuiltinTag - - # Circuits (Provider, Provider Network, Circuits Types, Circuits) - - name: OrganizationGeneric - mapping: circuits.providers - identifiers: ["name"] - fields: - - name: name - mapping: name - - name: type - static: "Provider" - - name: InfraProviderNetwork - mapping: circuits.provider-networks - identifiers: ["name"] - fields: - - name: name - mapping: name - - name: vendor_id - mapping: service_id - - name: provider - mapping: provider.slug - - name: description - mapping: description - - name: status - mapping: status - reference: StatusGeneric - - name: tags - mapping: tags - reference: BuiltinTag - - name: provider - mapping: provider - reference: OrganizationGeneric - - name: TemplateCircuitType - mapping: circuits.circuit-types - identifiers: ["name"] - fields: - - name: name - mapping: name - - name: description - mapping: description - - name: InfraCircuit - mapping: circuits.circuits - identifiers: ["circuit_id"] - # Showcase of the filters on the Circuits, it will import the ones starting with a CID starting with ntt - filters: - - field: cid - operation: "contains" - value: "ntt" - transforms: - - field: circuit_id - expression: "{{ cid.upper() }}" - fields: - - name: circuit_id - mapping: cid - - name: vendor_id - mapping: cid - - name: provider - mapping: provider - reference: OrganizationGeneric - - name: description - mapping: description - - name: status - mapping: status - reference: StatusGeneric - - name: type - mapping: circuit_type - reference: TemplateCircuitType - # - name: tenant - # mapping: tenant - # reference: OrganizationGeneric - - name: tags - mapping: tags - reference: BuiltinTag - - # BGP Plugin (Autonomous System, BGP Session, BGP Peer Group) - - name: InfraAutonomousSystem - mapping: plugin.bgp.autonomous-systems - identifiers: ["name"] - fields: - - name: name - mapping: asn - - name: asn - mapping: asn - - name: description - mapping: description - - name: organization - mapping: provider - reference: OrganizationGeneric - -# IPAM (Namepsace, VRF, VLANs Groups, VLANs, Prefixes, IPs) - - name: NautobotNamespace - mapping: ipam.namespaces - identifiers: ["name"] - fields: - - name: name - mapping: name - - name: description - mapping: description - - name: InfraRouteTarget - mapping: ipam.route-targets - identifiers: ["name", "organization"] - fields: - - name: name - mapping: name - - name: description - mapping: description - - name: organization - mapping: tenant - reference: OrganizationGeneric - - name: InfraVRF - mapping: ipam.vrfs - identifiers: ["name", "ip_namespace"] - fields: - - name: name - mapping: name - - name: description - mapping: description - - name: organization - mapping: tenant - reference: OrganizationGeneric - - name: ip_namespace - mapping: namespace - reference: NautobotNamespace - - name: vrf_rd - mapping: rd - - name: import_rt - mapping: import_targets - reference: InfraRouteTarget - - name: export_rt - mapping: export_targets - reference: InfraRouteTarget - - name: CoreStandardGroup - mapping: ipam.vlan-groups - identifiers: ["name"] - fields: - - name: name - mapping: name - - name: description - mapping: description - # - name: location - # mapping: location - # reference: LocationGeneric - - name: InfraVLAN - mapping: ipam.vlans - identifiers: ["name"] - fields: - - name: name - mapping: name - - name: vlan_id - mapping: vid - - name: organization - mapping: tenant - reference: OrganizationGeneric - - name: description - mapping: description - - name: locations - mapping: locations - reference: LocationGeneric - - name: role - mapping: role - reference: RoleGeneric - - name: status - mapping: status - reference: StatusGeneric - - name: vlan_group - mapping: vlan_group - reference: CoreStandardGroup - - name: InfraPrefix - mapping: ipam.prefixes - identifiers: ["prefix", "ip_namespace"] - fields: - - name: prefix - mapping: prefix - - name: description - mapping: description - - name: organization - mapping: tenant - reference: OrganizationGeneric - - name: ip_namespace - mapping: namespace - reference: NautobotNamespace - - name: role - mapping: role - reference: RoleGeneric - - name: status - mapping: status - reference: StatusGeneric - - name: vlan - mapping: vlan - reference: InfraVLAN - - name: locations - mapping: locations - reference: LocationGeneric - - - name: InfraIPAddress - mapping: ipam.ip-addresses - identifiers: ["address", "ip_prefix"] - fields: - - name: address - mapping: address - - name: description - mapping: description - - name: organization - mapping: tenant - reference: OrganizationGeneric - - name: ip_prefix - mapping: parent - reference: InfraPrefix - - name: interfaces - mapping: interfaces - reference: InfraInterfaceL2L3 - - name: role - mapping: role - reference: RoleGeneric - - name: status - mapping: status - reference: StatusGeneric - - # Interfaces (interfaces, rear port, front port) - - name: InfraInterfaceL2L3 - identifiers: ["name", "device"] - mapping: dcim.interfaces - fields: - - name: name - mapping: name - - name: description - mapping: description - - name: interface_type - mapping: type.label - - name: l2_mode - mapping: mode.label - - name: mac_address - mapping: mac_address - # - name: ip_addresses - # mapping: ip_addresses - # reference: InfraIPAddress - - name: mgmt_only - mapping: mgmt_only - - name: untagged_vlan - mapping: untagged_vlan - reference: InfraVLAN - - name: tagged_vlan - mapping: tagged_vlans - reference: InfraVLAN - - name: device - mapping: device - reference: InfraDevice - - name: tags - mapping: tags - reference: BuiltinTag - - name: status - mapping: status - reference: StatusGeneric - - - name: InfraFrontPort - identifiers: ["name", "device"] - mapping: dcim.front-ports - fields: - - name: name - mapping: name - - name: description - mapping: description - - name: port_type - mapping: type.label - - name: rear_port - mapping: rear_port - reference: InfraRearPort - - name: device - mapping: device - reference: InfraDevice - - name: status - mapping: status - reference: StatusGeneric - - name: InfraRearPort - identifiers: ["name", "device"] - mapping: dcim.rear-ports - fields: - - name: name - mapping: name - - name: description - mapping: description - - name: port_type - mapping: type.label - - name: device - mapping: device - reference: InfraDevice - - name: status - mapping: status - reference: StatusGeneric diff --git a/sync/examples/nautobot-v2_to_infrahub/infrahub/sync_adapter.py b/sync/examples/nautobot-v2_to_infrahub/infrahub/sync_adapter.py deleted file mode 100644 index 262d18de92..0000000000 --- a/sync/examples/nautobot-v2_to_infrahub/infrahub/sync_adapter.py +++ /dev/null @@ -1,60 +0,0 @@ -from infrahub_sync.adapters.infrahub import InfrahubAdapter - -from .sync_models import ( - BuiltinTag, - CoreStandardGroup, - InfraAutonomousSystem, - InfraCircuit, - InfraDevice, - InfraFrontPort, - InfraInterfaceL2L3, - InfraIPAddress, - InfraPlatform, - InfraPrefix, - InfraProviderNetwork, - InfraRack, - InfraRearPort, - InfraRouteTarget, - InfraVLAN, - InfraVRF, - LocationGeneric, - NautobotNamespace, - OrganizationGeneric, - RoleGeneric, - StatusGeneric, - TemplateCircuitType, - TemplateDeviceType, - TemplateLocationType, -) - - -# ------------------------------------------------------- -# AUTO-GENERATED FILE, DO NOT MODIFY -# This file has been generated with the command `infrahub-sync generate` -# All modifications will be lost the next time you reexecute this command -# ------------------------------------------------------- -class InfrahubSync(InfrahubAdapter): - CoreStandardGroup = CoreStandardGroup - BuiltinTag = BuiltinTag - InfraAutonomousSystem = InfraAutonomousSystem - InfraCircuit = InfraCircuit - InfraDevice = InfraDevice - InfraFrontPort = InfraFrontPort - InfraIPAddress = InfraIPAddress - InfraInterfaceL2L3 = InfraInterfaceL2L3 - InfraPlatform = InfraPlatform - InfraPrefix = InfraPrefix - InfraProviderNetwork = InfraProviderNetwork - InfraRack = InfraRack - InfraRearPort = InfraRearPort - InfraRouteTarget = InfraRouteTarget - InfraVLAN = InfraVLAN - InfraVRF = InfraVRF - LocationGeneric = LocationGeneric - NautobotNamespace = NautobotNamespace - OrganizationGeneric = OrganizationGeneric - RoleGeneric = RoleGeneric - StatusGeneric = StatusGeneric - TemplateCircuitType = TemplateCircuitType - TemplateDeviceType = TemplateDeviceType - TemplateLocationType = TemplateLocationType diff --git a/sync/examples/nautobot-v2_to_infrahub/infrahub/sync_models.py b/sync/examples/nautobot-v2_to_infrahub/infrahub/sync_models.py deleted file mode 100644 index 794f1bac2e..0000000000 --- a/sync/examples/nautobot-v2_to_infrahub/infrahub/sync_models.py +++ /dev/null @@ -1,356 +0,0 @@ -from typing import Any, List, Optional - -from infrahub_sync.adapters.infrahub import InfrahubModel - - -# ------------------------------------------------------- -# AUTO-GENERATED FILE, DO NOT MODIFY -# This file has been generated with the command `infrahub-sync generate` -# All modifications will be lost the next time you reexecute this command -# ------------------------------------------------------- -class CoreStandardGroup(InfrahubModel): - _modelname = "CoreStandardGroup" - _identifiers = ("name",) - _attributes = ("description",) - name: str - description: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class BuiltinTag(InfrahubModel): - _modelname = "BuiltinTag" - _identifiers = ("name",) - _attributes = ("description",) - name: str - description: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraAutonomousSystem(InfrahubModel): - _modelname = "InfraAutonomousSystem" - _identifiers = ("name",) - _attributes = ("organization", "asn", "description") - name: str - asn: int - description: Optional[str] = None - organization: str - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraCircuit(InfrahubModel): - _modelname = "InfraCircuit" - _identifiers = ("circuit_id",) - _attributes = ("status", "provider", "type", "tags", "description", "vendor_id") - circuit_id: str - description: Optional[str] = None - vendor_id: Optional[str] = None - status: Optional[str] = None - provider: str - type: str - tags: Optional[List[str]] = [] - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraDevice(InfrahubModel): - _modelname = "InfraDevice" - _identifiers = ("location", "organization", "name") - _attributes = ("model", "rack", "status", "role", "tags", "platform", "serial_number", "asset_tag") - name: Optional[str] = None - serial_number: Optional[str] = None - asset_tag: Optional[str] = None - location: str - model: str - rack: Optional[str] = None - status: Optional[str] = None - role: Optional[str] = None - tags: Optional[List[str]] = [] - platform: Optional[str] = None - organization: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraFrontPort(InfrahubModel): - _modelname = "InfraFrontPort" - _identifiers = ("name", "device") - _attributes = ("rear_port", "description", "port_type") - name: str - description: Optional[str] = None - port_type: Optional[str] = None - rear_port: Optional[List[str]] = [] - device: str - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraIPAddress(InfrahubModel): - _modelname = "InfraIPAddress" - _identifiers = ("address", "ip_prefix") - _attributes = ("organization", "interfaces", "role", "description") - address: str - description: Optional[str] = None - organization: Optional[str] = None - interfaces: Optional[List[str]] = [] - role: Optional[str] = None - ip_prefix: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraInterfaceL2L3(InfrahubModel): - _modelname = "InfraInterfaceL2L3" - _identifiers = ("name", "device") - _attributes = ( - "tagged_vlan", - "status", - "tags", - "l2_mode", - "description", - "mgmt_only", - "mac_address", - "interface_type", - ) - l2_mode: Optional[str] = None - name: str - description: Optional[str] = None - mgmt_only: Optional[bool] = False - mac_address: Optional[str] = None - interface_type: Optional[str] = None - untagged_vlan: Optional[str] = None - tagged_vlan: Optional[List[str]] = [] - status: Optional[str] = None - device: str - tags: Optional[List[str]] = [] - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraPlatform(InfrahubModel): - _modelname = "InfraPlatform" - _identifiers = ("name", "manufacturer") - _attributes = ("description", "napalm_driver") - name: str - description: Optional[str] = None - napalm_driver: Optional[str] = None - manufacturer: str - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraPrefix(InfrahubModel): - _modelname = "InfraPrefix" - _identifiers = ("prefix", "ip_namespace") - _attributes = ("organization", "locations", "status", "role", "vlan", "description") - prefix: str - description: Optional[str] = None - organization: Optional[str] = None - locations: Optional[List[str]] = [] - status: Optional[str] = None - role: Optional[str] = None - vlan: Optional[str] = None - ip_namespace: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraProviderNetwork(InfrahubModel): - _modelname = "InfraProviderNetwork" - _identifiers = ("name",) - _attributes = ("status", "provider", "tags", "description", "vendor_id") - name: str - description: Optional[str] = None - vendor_id: Optional[str] = None - status: Optional[str] = None - provider: str - tags: Optional[List[str]] = [] - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraRack(InfrahubModel): - _modelname = "InfraRack" - _identifiers = ("name",) - _attributes = ("location", "role", "tags", "height", "facility_id", "serial_number", "asset_tag") - name: str - height: Optional[int] = None - facility_id: Optional[str] = None - serial_number: Optional[str] = None - asset_tag: Optional[str] = None - location: str - role: Optional[str] = None - tags: Optional[List[str]] = [] - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraRearPort(InfrahubModel): - _modelname = "InfraRearPort" - _identifiers = ("name", "device") - _attributes = ("description", "port_type") - name: str - description: Optional[str] = None - port_type: Optional[str] = None - device: str - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraRouteTarget(InfrahubModel): - _modelname = "InfraRouteTarget" - _identifiers = ("name", "organization") - _attributes = ("description",) - name: str - description: Optional[str] = None - organization: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraVLAN(InfrahubModel): - _modelname = "InfraVLAN" - _identifiers = ("name",) - _attributes = ("organization", "locations", "status", "role", "vlan_group", "description", "vlan_id") - name: str - description: Optional[str] = None - vlan_id: int - organization: Optional[str] = None - locations: Optional[List[str]] = [] - status: Optional[str] = None - role: Optional[str] = None - vlan_group: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraVRF(InfrahubModel): - _modelname = "InfraVRF" - _identifiers = ("name", "ip_namespace") - _attributes = ("organization", "import_rt", "export_rt", "description", "vrf_rd") - name: str - description: Optional[str] = None - vrf_rd: Optional[str] = None - organization: Optional[str] = None - ip_namespace: str - import_rt: Optional[List[str]] = [] - export_rt: Optional[List[str]] = [] - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class LocationGeneric(InfrahubModel): - _modelname = "LocationGeneric" - _identifiers = ("name",) - _attributes = ("tags", "location_type", "status", "description") - name: str - description: Optional[str] = None - tags: Optional[List[str]] = [] - location_type: Optional[str] = None - status: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class NautobotNamespace(InfrahubModel): - _modelname = "NautobotNamespace" - _identifiers = ("name",) - _attributes = ("description",) - name: str - description: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class OrganizationGeneric(InfrahubModel): - _modelname = "OrganizationGeneric" - _identifiers = ("name",) - _attributes = ("group", "description", "type") - name: str - description: Optional[str] = None - type: Optional[str] = None - group: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class RoleGeneric(InfrahubModel): - _modelname = "RoleGeneric" - _identifiers = ("name",) - _attributes = ("label", "description") - name: str - label: Optional[str] = None - description: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class StatusGeneric(InfrahubModel): - _modelname = "StatusGeneric" - _identifiers = ("name",) - _attributes = ("label", "description") - name: str - label: Optional[str] = None - description: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class TemplateCircuitType(InfrahubModel): - _modelname = "TemplateCircuitType" - _identifiers = ("name",) - _attributes = ("description",) - name: str - description: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class TemplateDeviceType(InfrahubModel): - _modelname = "TemplateDeviceType" - _identifiers = ("name", "manufacturer") - _attributes = ("tags", "part_number", "height", "full_depth") - part_number: Optional[str] = None - height: Optional[int] = None - full_depth: Optional[bool] = None - name: str - manufacturer: str - tags: Optional[List[str]] = [] - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class TemplateLocationType(InfrahubModel): - _modelname = "TemplateLocationType" - _identifiers = ("name",) - _attributes = ("description",) - name: str - description: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None diff --git a/sync/examples/nautobot-v2_to_infrahub/nautobot/sync_adapter.py b/sync/examples/nautobot-v2_to_infrahub/nautobot/sync_adapter.py deleted file mode 100644 index 51cc3ac57f..0000000000 --- a/sync/examples/nautobot-v2_to_infrahub/nautobot/sync_adapter.py +++ /dev/null @@ -1,60 +0,0 @@ -from infrahub_sync.adapters.nautobot import NautobotAdapter - -from .sync_models import ( - BuiltinTag, - CoreStandardGroup, - InfraAutonomousSystem, - InfraCircuit, - InfraDevice, - InfraFrontPort, - InfraInterfaceL2L3, - InfraIPAddress, - InfraPlatform, - InfraPrefix, - InfraProviderNetwork, - InfraRack, - InfraRearPort, - InfraRouteTarget, - InfraVLAN, - InfraVRF, - LocationGeneric, - NautobotNamespace, - OrganizationGeneric, - RoleGeneric, - StatusGeneric, - TemplateCircuitType, - TemplateDeviceType, - TemplateLocationType, -) - - -# ------------------------------------------------------- -# AUTO-GENERATED FILE, DO NOT MODIFY -# This file has been generated with the command `infrahub-sync generate` -# All modifications will be lost the next time you reexecute this command -# ------------------------------------------------------- -class NautobotSync(NautobotAdapter): - CoreStandardGroup = CoreStandardGroup - BuiltinTag = BuiltinTag - InfraAutonomousSystem = InfraAutonomousSystem - InfraCircuit = InfraCircuit - InfraDevice = InfraDevice - InfraFrontPort = InfraFrontPort - InfraIPAddress = InfraIPAddress - InfraInterfaceL2L3 = InfraInterfaceL2L3 - InfraPlatform = InfraPlatform - InfraPrefix = InfraPrefix - InfraProviderNetwork = InfraProviderNetwork - InfraRack = InfraRack - InfraRearPort = InfraRearPort - InfraRouteTarget = InfraRouteTarget - InfraVLAN = InfraVLAN - InfraVRF = InfraVRF - LocationGeneric = LocationGeneric - NautobotNamespace = NautobotNamespace - OrganizationGeneric = OrganizationGeneric - RoleGeneric = RoleGeneric - StatusGeneric = StatusGeneric - TemplateCircuitType = TemplateCircuitType - TemplateDeviceType = TemplateDeviceType - TemplateLocationType = TemplateLocationType diff --git a/sync/examples/nautobot-v2_to_infrahub/nautobot/sync_models.py b/sync/examples/nautobot-v2_to_infrahub/nautobot/sync_models.py deleted file mode 100644 index 3610f3cae3..0000000000 --- a/sync/examples/nautobot-v2_to_infrahub/nautobot/sync_models.py +++ /dev/null @@ -1,356 +0,0 @@ -from typing import Any, List, Optional - -from infrahub_sync.adapters.nautobot import NautobotModel - - -# ------------------------------------------------------- -# AUTO-GENERATED FILE, DO NOT MODIFY -# This file has been generated with the command `infrahub-sync generate` -# All modifications will be lost the next time you reexecute this command -# ------------------------------------------------------- -class CoreStandardGroup(NautobotModel): - _modelname = "CoreStandardGroup" - _identifiers = ("name",) - _attributes = ("description",) - name: str - description: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class BuiltinTag(NautobotModel): - _modelname = "BuiltinTag" - _identifiers = ("name",) - _attributes = ("description",) - name: str - description: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraAutonomousSystem(NautobotModel): - _modelname = "InfraAutonomousSystem" - _identifiers = ("name",) - _attributes = ("organization", "asn", "description") - name: str - asn: int - description: Optional[str] = None - organization: str - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraCircuit(NautobotModel): - _modelname = "InfraCircuit" - _identifiers = ("circuit_id",) - _attributes = ("status", "provider", "type", "tags", "description", "vendor_id") - circuit_id: str - description: Optional[str] = None - vendor_id: Optional[str] = None - status: Optional[str] = None - provider: str - type: str - tags: Optional[List[str]] = [] - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraDevice(NautobotModel): - _modelname = "InfraDevice" - _identifiers = ("location", "organization", "name") - _attributes = ("model", "rack", "status", "role", "tags", "platform", "serial_number", "asset_tag") - name: Optional[str] = None - serial_number: Optional[str] = None - asset_tag: Optional[str] = None - location: str - model: str - rack: Optional[str] = None - status: Optional[str] = None - role: Optional[str] = None - tags: Optional[List[str]] = [] - platform: Optional[str] = None - organization: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraFrontPort(NautobotModel): - _modelname = "InfraFrontPort" - _identifiers = ("name", "device") - _attributes = ("rear_port", "description", "port_type") - name: str - description: Optional[str] = None - port_type: Optional[str] = None - rear_port: Optional[List[str]] = [] - device: str - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraIPAddress(NautobotModel): - _modelname = "InfraIPAddress" - _identifiers = ("address", "ip_prefix") - _attributes = ("organization", "interfaces", "role", "description") - address: str - description: Optional[str] = None - organization: Optional[str] = None - interfaces: Optional[List[str]] = [] - role: Optional[str] = None - ip_prefix: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraInterfaceL2L3(NautobotModel): - _modelname = "InfraInterfaceL2L3" - _identifiers = ("name", "device") - _attributes = ( - "tagged_vlan", - "status", - "tags", - "l2_mode", - "description", - "mgmt_only", - "mac_address", - "interface_type", - ) - l2_mode: Optional[str] = None - name: str - description: Optional[str] = None - mgmt_only: Optional[bool] = False - mac_address: Optional[str] = None - interface_type: Optional[str] = None - untagged_vlan: Optional[str] = None - tagged_vlan: Optional[List[str]] = [] - status: Optional[str] = None - device: str - tags: Optional[List[str]] = [] - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraPlatform(NautobotModel): - _modelname = "InfraPlatform" - _identifiers = ("name", "manufacturer") - _attributes = ("description", "napalm_driver") - name: str - description: Optional[str] = None - napalm_driver: Optional[str] = None - manufacturer: str - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraPrefix(NautobotModel): - _modelname = "InfraPrefix" - _identifiers = ("prefix", "ip_namespace") - _attributes = ("organization", "locations", "status", "role", "vlan", "description") - prefix: str - description: Optional[str] = None - organization: Optional[str] = None - locations: Optional[List[str]] = [] - status: Optional[str] = None - role: Optional[str] = None - vlan: Optional[str] = None - ip_namespace: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraProviderNetwork(NautobotModel): - _modelname = "InfraProviderNetwork" - _identifiers = ("name",) - _attributes = ("status", "provider", "tags", "description", "vendor_id") - name: str - description: Optional[str] = None - vendor_id: Optional[str] = None - status: Optional[str] = None - provider: str - tags: Optional[List[str]] = [] - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraRack(NautobotModel): - _modelname = "InfraRack" - _identifiers = ("name",) - _attributes = ("location", "role", "tags", "height", "facility_id", "serial_number", "asset_tag") - name: str - height: Optional[int] = None - facility_id: Optional[str] = None - serial_number: Optional[str] = None - asset_tag: Optional[str] = None - location: str - role: Optional[str] = None - tags: Optional[List[str]] = [] - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraRearPort(NautobotModel): - _modelname = "InfraRearPort" - _identifiers = ("name", "device") - _attributes = ("description", "port_type") - name: str - description: Optional[str] = None - port_type: Optional[str] = None - device: str - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraRouteTarget(NautobotModel): - _modelname = "InfraRouteTarget" - _identifiers = ("name", "organization") - _attributes = ("description",) - name: str - description: Optional[str] = None - organization: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraVLAN(NautobotModel): - _modelname = "InfraVLAN" - _identifiers = ("name",) - _attributes = ("organization", "locations", "status", "role", "vlan_group", "description", "vlan_id") - name: str - description: Optional[str] = None - vlan_id: int - organization: Optional[str] = None - locations: Optional[List[str]] = [] - status: Optional[str] = None - role: Optional[str] = None - vlan_group: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraVRF(NautobotModel): - _modelname = "InfraVRF" - _identifiers = ("name", "ip_namespace") - _attributes = ("organization", "import_rt", "export_rt", "description", "vrf_rd") - name: str - description: Optional[str] = None - vrf_rd: Optional[str] = None - organization: Optional[str] = None - ip_namespace: str - import_rt: Optional[List[str]] = [] - export_rt: Optional[List[str]] = [] - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class LocationGeneric(NautobotModel): - _modelname = "LocationGeneric" - _identifiers = ("name",) - _attributes = ("tags", "location_type", "status", "description") - name: str - description: Optional[str] = None - tags: Optional[List[str]] = [] - location_type: Optional[str] = None - status: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class NautobotNamespace(NautobotModel): - _modelname = "NautobotNamespace" - _identifiers = ("name",) - _attributes = ("description",) - name: str - description: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class OrganizationGeneric(NautobotModel): - _modelname = "OrganizationGeneric" - _identifiers = ("name",) - _attributes = ("group", "description", "type") - name: str - description: Optional[str] = None - type: Optional[str] = None - group: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class RoleGeneric(NautobotModel): - _modelname = "RoleGeneric" - _identifiers = ("name",) - _attributes = ("label", "description") - name: str - label: Optional[str] = None - description: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class StatusGeneric(NautobotModel): - _modelname = "StatusGeneric" - _identifiers = ("name",) - _attributes = ("label", "description") - name: str - label: Optional[str] = None - description: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class TemplateCircuitType(NautobotModel): - _modelname = "TemplateCircuitType" - _identifiers = ("name",) - _attributes = ("description",) - name: str - description: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class TemplateDeviceType(NautobotModel): - _modelname = "TemplateDeviceType" - _identifiers = ("name", "manufacturer") - _attributes = ("tags", "part_number", "height", "full_depth") - part_number: Optional[str] = None - height: Optional[int] = None - full_depth: Optional[bool] = None - name: str - manufacturer: str - tags: Optional[List[str]] = [] - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class TemplateLocationType(NautobotModel): - _modelname = "TemplateLocationType" - _identifiers = ("name",) - _attributes = ("description",) - name: str - description: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None diff --git a/sync/examples/netbox_to_infrahub/config.yml b/sync/examples/netbox_to_infrahub/config.yml deleted file mode 100644 index ea99b56f43..0000000000 --- a/sync/examples/netbox_to_infrahub/config.yml +++ /dev/null @@ -1,466 +0,0 @@ ---- -name: from-netbox - -source: - name: netbox - settings: - url: "https://demo.netbox.dev" - token: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" - -destination: - name: infrahub - settings: - url: "http://localhost:8000" - -order: [ - "BuiltinTag", - "RoleGeneric", - "CoreStandardGroup", - "OrganizationGeneric", - "LocationGeneric", - "InfraRack", - "TemplateDeviceType", - "InfraProviderNetwork", - "TemplateCircuitType", - "InfraCircuit", - "InfraRouteTarget", - "InfraVRF", - "InfraDevice", - "InfraVLAN", - "InfraPrefix", - # "InfraIPAddress", - # "InfraInterfaceL2L3", -] - -schema_mapping: - # Tags - - name: BuiltinTag - mapping: extras.tags - identifiers: ["name"] - fields: - - name: name - mapping: name - - name: description - mapping: description - - # Roles (DCIM, IPAM, Devices) - - name: RoleGeneric - mapping: dcim.device_roles - identifiers: ["name"] - fields: - - name: name - mapping: name - - name: description - mapping: description - - name: RoleGeneric - mapping: dcim.rack_roles - identifiers: ["name"] - fields: - - name: name - mapping: name - - name: description - mapping: description - - name: RoleGeneric - mapping: ipam.roles - identifiers: ["name"] - fields: - - name: name - mapping: name - - name: description - mapping: description - - # Tenancy (tenants, tenant groups) - - name: CoreStandardGroup - mapping: tenancy.tenant-groups - identifiers: ["name"] - fields: - - name: name - mapping: name - - name: description - mapping: description - - name: OrganizationGeneric - mapping: tenancy.tenants - identifiers: ["name"] - fields: - - name: name - mapping: name - - name: description - mapping: description - - name: group - mapping: group - reference: CoreStandardGroup - - # Sites (Site Groups, Region, Sites, Location) - - name: CoreStandardGroup - mapping: dcim.site-groups - identifiers: ["name"] - fields: - - name: name - mapping: name - - name: description - mapping: description - - name: LocationGeneric - mapping: dcim.regions - fields: - - name: name - mapping: slug - - name: description - mapping: name - - name: type - static: "Region" - - name: tags - mapping: tags - reference: BuiltinTag - - name: LocationGeneric - mapping: dcim.sites - fields: - - name: name - mapping: slug - - name: description - mapping: name - - name: type - static: "Site" - - name: group - mapping: group - reference: CoreStandardGroup - - name: organization - mapping: tenant - reference: OrganizationGeneric - - name: tags - mapping: tags - reference: BuiltinTag - - name: LocationGeneric - mapping: dcim.locations - fields: - - name: name - mapping: slug - - name: description - mapping: name - - name: type - static: "Location" - - name: organization - mapping: tenant - reference: OrganizationGeneric - - name: tags - mapping: tags - reference: BuiltinTag - - # Racks - - name: InfraRack - mapping: dcim.racks - identifiers: ["name", "location"] - fields: - - name: name - mapping: name - - name: location - mapping: site - reference: LocationGeneric - - name: height - mapping: u_height - - name: serial_number - mapping: serial - - name: asset_tag - mapping: asset_tag - - name: facility_id - mapping: facility_id - - name: role - mapping: role - reference: RoleGeneric - - name: tags - mapping: tags - reference: BuiltinTag - - # Device (manufacturer, device types, devices, interfaces) - - name: OrganizationGeneric - mapping: dcim.manufacturers - identifiers: ["name"] - fields: - - name: name - mapping: name - - name: description - mapping: description - - name: TemplateDeviceType - mapping: dcim.device-types - identifiers: ["name", "manufacturer"] - fields: - - name: name - mapping: model - - name: part_number - mapping: part_number - - name: full_depth - mapping: is_full_depth - - name: height - mapping: integer_height - - name: tags - mapping: tags - reference: BuiltinTag - - name: manufacturer - mapping: manufacturer - reference: OrganizationGeneric - transforms: - - field: integer_height - expression: "{{ u_height|float|round(0, 'ceil') }}" - # -> The netbox constraint is `dcim_device_unique_name_site_tenant` - # Reusing device_name + site + Organization as identifiers - # /!\ Seem like Netbox allowed device to have the same name if there is a virtual-chassis - - name: InfraDevice - identifiers: ["location", "rack", "organization", "name"] - mapping: dcim.devices - fields: - - name: name - mapping: name - - name: serial_number - mapping: serial - - name: asset_tag - mapping: asset_tag - - name: description - mapping: description - - name: model - mapping: device_type - reference: TemplateDeviceType - - name: organization - mapping: tenant - reference: OrganizationGeneric - - name: role - mapping: role - reference: RoleGeneric - # - name: status - # mapping: status - # reference: StatusGeneric - - name: location - mapping: site - reference: LocationGeneric - - name: rack - mapping: rack - reference: InfraRack - - name: tags - mapping: tags - reference: BuiltinTag - # Showcase Filters - # It will import the ones with dmu01 in their name *AND* without pdu - filters: - - field: name - operation: "contains" - value: "dmi01" - - field: name - operation: "not contains" - value: "pdu" - # Showcase Transforms - transforms: - - field: name - expression: "{{ name.lower() }}" - - field: serial_number - expression: "{{ serial_number.lower() if serial_number else '' }}}" - - # Interfaces (interfaces, rear port, front port) - - name: InfraInterfaceL2L3 - identifiers: ["device", "name"] - mapping: dcim.interfaces - fields: - - name: name - mapping: name - - name: description - mapping: description - - name: interface_type - mapping: type.label - - name: l2_mode - mapping: mode.label - - name: mac_address - mapping: mac_address - # - name: ip_addresses - # mapping: ip_addresses - # reference: InfraIPAddress - - name: mgmt_only - mapping: mgmt_only - - name: untagged_vlan - mapping: untagged_vlan - reference: InfraVLAN - - name: tagged_vlan - mapping: tagged_vlans - reference: InfraVLAN - - name: device - mapping: device - reference: InfraDevice - - name: tags - mapping: tags - reference: BuiltinTag - # - name: status - # mapping: status - # reference: StatusGeneric - filters: - - field: device.name - operation: "contains" - value: "dmi01" - - # Circuits (Provider, Provider Network, Circuits Types, Circuits) - - name: OrganizationGeneric - mapping: circuits.providers - identifiers: ["name"] - fields: - - name: name - mapping: name - - name: description - mapping: description - - name: InfraProviderNetwork - mapping: circuits.provider-networks - identifiers: ["name"] - fields: - - name: name - mapping: name - - name: vendor_id - mapping: service_id - - name: provider - mapping: provider - reference: OrganizationGeneric - - name: description - mapping: description - # - name: status - # mapping: status - # reference: StatusGeneric - - name: tags - mapping: tags - reference: BuiltinTag - - name: provider - mapping: provider - reference: OrganizationGeneric - - name: TemplateCircuitType - mapping: circuits.circuit-types - identifiers: ["name"] - fields: - - name: name - mapping: name - - name: description - mapping: description - - name: tags - mapping: tags - reference: BuiltinTag - - name: InfraCircuit - mapping: circuits.circuits - identifiers: ["circuit_id"] - fields: - - name: circuit_id - mapping: cid - - name: vendor_id - mapping: cid - - name: provider - mapping: provider - reference: OrganizationGeneric - - name: description - mapping: description - # - name: status - # mapping: status - # reference: StatusGeneric - - name: type - mapping: type.name - - name: provider - mapping: provider - reference: OrganizationGeneric - - name: tags - mapping: tags - reference: BuiltinTag - -# IPAM (VRF, VLANs Groups, VLANs, Prefixes, IPs) - - name: InfraRouteTarget - mapping: ipam.route-targets - identifiers: ["name", "organization"] - fields: - - name: name - mapping: name - - name: description - mapping: description - - name: organization - mapping: tenant - reference: OrganizationGeneric - - name: InfraVRF - mapping: ipam.vrfs - identifiers: ["name"] - fields: - - name: name - mapping: name - - name: description - mapping: description - - name: organization - mapping: tenant - reference: OrganizationGeneric - - name: vrf_rd - mapping: rd - - name: import_rt - mapping: import_targets - reference: InfraRouteTarget - - name: export_rt - mapping: export_targets - reference: InfraRouteTarget - - name: CoreStandardGroup - mapping: ipam.vlan-groups - identifiers: ["name"] - fields: - - name: name - mapping: name - - name: description - mapping: description - - name: InfraVLAN - mapping: ipam.vlans - identifiers: ["name", "vlan_id", "location", "vlan_group"] - fields: - - name: name - mapping: name - - name: vlan_id - mapping: vid - - name: organization - mapping: tenant - reference: OrganizationGeneric - - name: description - mapping: description - - name: location - mapping: site - reference: LocationGeneric - - name: vlan_group - mapping: group - reference: CoreStandardGroup - - name: InfraPrefix - mapping: ipam.prefixes - identifiers: ["prefix", "vrf"] - fields: - - name: prefix - mapping: prefix - - name: description - mapping: description - - name: organization - mapping: tenant - reference: OrganizationGeneric - - name: role - mapping: role - reference: RoleGeneric - - name: vrf - mapping: vrf - reference: InfraVRF - - name: location - mapping: site - reference: LocationGeneric - # - name: status - # mapping: status - # reference: StatusGeneric - # - name: vlan - # mapping: vlan.name - # - name: location - # mapping: location.slug - - name: InfraIPAddress - mapping: ipam.ip-addresses - identifiers: ["address", "vrf"] - fields: - - name: address - mapping: address - - name: description - mapping: description - - name: organization - mapping: tenant - reference: OrganizationGeneric - # Not the same as ipam.role - # - name: role - # mapping: role.value - # - name: status - # mapping: status - # reference: StatusGeneric - - name: vrf - mapping: vrf - reference: InfraVRF diff --git a/sync/examples/netbox_to_infrahub/infrahub/sync_adapter.py b/sync/examples/netbox_to_infrahub/infrahub/sync_adapter.py deleted file mode 100644 index d15c7162c7..0000000000 --- a/sync/examples/netbox_to_infrahub/infrahub/sync_adapter.py +++ /dev/null @@ -1,46 +0,0 @@ -from infrahub_sync.adapters.infrahub import InfrahubAdapter - -from .sync_models import ( - BuiltinTag, - CoreStandardGroup, - InfraCircuit, - InfraDevice, - InfraInterfaceL2L3, - InfraIPAddress, - InfraPrefix, - InfraProviderNetwork, - InfraRack, - InfraRouteTarget, - InfraVLAN, - InfraVRF, - LocationGeneric, - OrganizationGeneric, - RoleGeneric, - TemplateCircuitType, - TemplateDeviceType, -) - - -# ------------------------------------------------------- -# AUTO-GENERATED FILE, DO NOT MODIFY -# This file has been generated with the command `infrahub-sync generate` -# All modifications will be lost the next time you reexecute this command -# ------------------------------------------------------- -class InfrahubSync(InfrahubAdapter): - CoreStandardGroup = CoreStandardGroup - BuiltinTag = BuiltinTag - InfraCircuit = InfraCircuit - InfraDevice = InfraDevice - InfraIPAddress = InfraIPAddress - InfraInterfaceL2L3 = InfraInterfaceL2L3 - InfraPrefix = InfraPrefix - InfraProviderNetwork = InfraProviderNetwork - InfraRack = InfraRack - InfraRouteTarget = InfraRouteTarget - InfraVLAN = InfraVLAN - InfraVRF = InfraVRF - LocationGeneric = LocationGeneric - OrganizationGeneric = OrganizationGeneric - RoleGeneric = RoleGeneric - TemplateCircuitType = TemplateCircuitType - TemplateDeviceType = TemplateDeviceType diff --git a/sync/examples/netbox_to_infrahub/infrahub/sync_models.py b/sync/examples/netbox_to_infrahub/infrahub/sync_models.py deleted file mode 100644 index 40d3811411..0000000000 --- a/sync/examples/netbox_to_infrahub/infrahub/sync_models.py +++ /dev/null @@ -1,249 +0,0 @@ -from typing import Any, List, Optional - -from infrahub_sync.adapters.infrahub import InfrahubModel - - -# ------------------------------------------------------- -# AUTO-GENERATED FILE, DO NOT MODIFY -# This file has been generated with the command `infrahub-sync generate` -# All modifications will be lost the next time you reexecute this command -# ------------------------------------------------------- -class CoreStandardGroup(InfrahubModel): - _modelname = "CoreStandardGroup" - _identifiers = ("name",) - _attributes = ("description",) - name: str - description: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class BuiltinTag(InfrahubModel): - _modelname = "BuiltinTag" - _identifiers = ("name",) - _attributes = ("description",) - name: str - description: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraCircuit(InfrahubModel): - _modelname = "InfraCircuit" - _identifiers = ("circuit_id",) - _attributes = ("provider", "type", "tags", "description", "vendor_id") - circuit_id: str - description: Optional[str] = None - vendor_id: Optional[str] = None - provider: str - type: str - tags: Optional[List[str]] = [] - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraDevice(InfrahubModel): - _modelname = "InfraDevice" - _identifiers = ("location", "rack", "organization", "name") - _attributes = ("model", "role", "tags", "description", "serial_number", "asset_tag") - name: Optional[str] = None - description: Optional[str] = None - serial_number: Optional[str] = None - asset_tag: Optional[str] = None - location: str - model: str - rack: Optional[str] = None - role: Optional[str] = None - tags: Optional[List[str]] = [] - organization: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraIPAddress(InfrahubModel): - _modelname = "InfraIPAddress" - _identifiers = ("address", "vrf") - _attributes = ("organization", "description") - address: str - description: Optional[str] = None - organization: Optional[str] = None - vrf: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraInterfaceL2L3(InfrahubModel): - _modelname = "InfraInterfaceL2L3" - _identifiers = ("device", "name") - _attributes = ("tagged_vlan", "tags", "l2_mode", "description", "mgmt_only", "mac_address", "interface_type") - l2_mode: Optional[str] = None - name: str - description: Optional[str] = None - mgmt_only: Optional[bool] = False - mac_address: Optional[str] = None - interface_type: Optional[str] = None - untagged_vlan: Optional[str] = None - tagged_vlan: Optional[List[str]] = [] - device: str - tags: Optional[List[str]] = [] - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraPrefix(InfrahubModel): - _modelname = "InfraPrefix" - _identifiers = ("prefix", "vrf") - _attributes = ("organization", "location", "role", "description") - prefix: str - description: Optional[str] = None - organization: Optional[str] = None - location: Optional[str] = None - role: Optional[str] = None - vrf: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraProviderNetwork(InfrahubModel): - _modelname = "InfraProviderNetwork" - _identifiers = ("name",) - _attributes = ("provider", "tags", "description", "vendor_id") - name: str - description: Optional[str] = None - vendor_id: Optional[str] = None - provider: str - tags: Optional[List[str]] = [] - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraRack(InfrahubModel): - _modelname = "InfraRack" - _identifiers = ("name", "location") - _attributes = ("role", "tags", "height", "facility_id", "serial_number", "asset_tag") - name: str - height: Optional[int] = None - facility_id: Optional[str] = None - serial_number: Optional[str] = None - asset_tag: Optional[str] = None - location: str - role: Optional[str] = None - tags: Optional[List[str]] = [] - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraRouteTarget(InfrahubModel): - _modelname = "InfraRouteTarget" - _identifiers = ("name", "organization") - _attributes = ("description",) - name: str - description: Optional[str] = None - organization: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraVLAN(InfrahubModel): - _modelname = "InfraVLAN" - _identifiers = ("name", "vlan_id", "location", "vlan_group") - _attributes = ("organization", "description") - name: str - description: Optional[str] = None - vlan_id: int - organization: Optional[str] = None - location: Optional[str] = None - vlan_group: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraVRF(InfrahubModel): - _modelname = "InfraVRF" - _identifiers = ("name",) - _attributes = ("organization", "import_rt", "export_rt", "description", "vrf_rd") - name: str - description: Optional[str] = None - vrf_rd: Optional[str] = None - organization: Optional[str] = None - import_rt: Optional[List[str]] = [] - export_rt: Optional[List[str]] = [] - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class LocationGeneric(InfrahubModel): - _modelname = "LocationGeneric" - _identifiers = ("name",) - _attributes = ("organization", "tags", "group", "description", "type") - name: str - description: Optional[str] = None - type: str - organization: Optional[str] = None - tags: Optional[List[str]] = [] - group: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class OrganizationGeneric(InfrahubModel): - _modelname = "OrganizationGeneric" - _identifiers = ("name",) - _attributes = ("group", "description") - name: str - description: Optional[str] = None - group: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class RoleGeneric(InfrahubModel): - _modelname = "RoleGeneric" - _identifiers = ("name",) - _attributes = ("description",) - name: str - description: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class TemplateCircuitType(InfrahubModel): - _modelname = "TemplateCircuitType" - _identifiers = ("name",) - _attributes = ("tags", "description") - name: str - description: Optional[str] = None - tags: Optional[List[str]] = [] - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class TemplateDeviceType(InfrahubModel): - _modelname = "TemplateDeviceType" - _identifiers = ("name", "manufacturer") - _attributes = ("tags", "part_number", "height", "full_depth") - part_number: Optional[str] = None - height: Optional[int] = None - full_depth: Optional[bool] = None - name: str - manufacturer: str - tags: Optional[List[str]] = [] - - local_id: Optional[str] = None - local_data: Optional[Any] = None diff --git a/sync/examples/netbox_to_infrahub/netbox/sync_adapter.py b/sync/examples/netbox_to_infrahub/netbox/sync_adapter.py deleted file mode 100644 index fb564be65c..0000000000 --- a/sync/examples/netbox_to_infrahub/netbox/sync_adapter.py +++ /dev/null @@ -1,46 +0,0 @@ -from infrahub_sync.adapters.netbox import NetboxAdapter - -from .sync_models import ( - BuiltinTag, - CoreStandardGroup, - InfraCircuit, - InfraDevice, - InfraInterfaceL2L3, - InfraIPAddress, - InfraPrefix, - InfraProviderNetwork, - InfraRack, - InfraRouteTarget, - InfraVLAN, - InfraVRF, - LocationGeneric, - OrganizationGeneric, - RoleGeneric, - TemplateCircuitType, - TemplateDeviceType, -) - - -# ------------------------------------------------------- -# AUTO-GENERATED FILE, DO NOT MODIFY -# This file has been generated with the command `infrahub-sync generate` -# All modifications will be lost the next time you reexecute this command -# ------------------------------------------------------- -class NetboxSync(NetboxAdapter): - CoreStandardGroup = CoreStandardGroup - BuiltinTag = BuiltinTag - InfraCircuit = InfraCircuit - InfraDevice = InfraDevice - InfraIPAddress = InfraIPAddress - InfraInterfaceL2L3 = InfraInterfaceL2L3 - InfraPrefix = InfraPrefix - InfraProviderNetwork = InfraProviderNetwork - InfraRack = InfraRack - InfraRouteTarget = InfraRouteTarget - InfraVLAN = InfraVLAN - InfraVRF = InfraVRF - LocationGeneric = LocationGeneric - OrganizationGeneric = OrganizationGeneric - RoleGeneric = RoleGeneric - TemplateCircuitType = TemplateCircuitType - TemplateDeviceType = TemplateDeviceType diff --git a/sync/examples/netbox_to_infrahub/netbox/sync_models.py b/sync/examples/netbox_to_infrahub/netbox/sync_models.py deleted file mode 100644 index f8d5929629..0000000000 --- a/sync/examples/netbox_to_infrahub/netbox/sync_models.py +++ /dev/null @@ -1,249 +0,0 @@ -from typing import Any, List, Optional - -from infrahub_sync.adapters.netbox import NetboxModel - - -# ------------------------------------------------------- -# AUTO-GENERATED FILE, DO NOT MODIFY -# This file has been generated with the command `infrahub-sync generate` -# All modifications will be lost the next time you reexecute this command -# ------------------------------------------------------- -class CoreStandardGroup(NetboxModel): - _modelname = "CoreStandardGroup" - _identifiers = ("name",) - _attributes = ("description",) - name: str - description: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class BuiltinTag(NetboxModel): - _modelname = "BuiltinTag" - _identifiers = ("name",) - _attributes = ("description",) - name: str - description: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraCircuit(NetboxModel): - _modelname = "InfraCircuit" - _identifiers = ("circuit_id",) - _attributes = ("provider", "type", "tags", "description", "vendor_id") - circuit_id: str - description: Optional[str] = None - vendor_id: Optional[str] = None - provider: str - type: str - tags: Optional[List[str]] = [] - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraDevice(NetboxModel): - _modelname = "InfraDevice" - _identifiers = ("location", "rack", "organization", "name") - _attributes = ("model", "role", "tags", "description", "serial_number", "asset_tag") - name: Optional[str] = None - description: Optional[str] = None - serial_number: Optional[str] = None - asset_tag: Optional[str] = None - location: str - model: str - rack: Optional[str] = None - role: Optional[str] = None - tags: Optional[List[str]] = [] - organization: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraIPAddress(NetboxModel): - _modelname = "InfraIPAddress" - _identifiers = ("address", "vrf") - _attributes = ("organization", "description") - address: str - description: Optional[str] = None - organization: Optional[str] = None - vrf: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraInterfaceL2L3(NetboxModel): - _modelname = "InfraInterfaceL2L3" - _identifiers = ("device", "name") - _attributes = ("tagged_vlan", "tags", "l2_mode", "description", "mgmt_only", "mac_address", "interface_type") - l2_mode: Optional[str] = None - name: str - description: Optional[str] = None - mgmt_only: Optional[bool] = False - mac_address: Optional[str] = None - interface_type: Optional[str] = None - untagged_vlan: Optional[str] = None - tagged_vlan: Optional[List[str]] = [] - device: str - tags: Optional[List[str]] = [] - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraPrefix(NetboxModel): - _modelname = "InfraPrefix" - _identifiers = ("prefix", "vrf") - _attributes = ("organization", "location", "role", "description") - prefix: str - description: Optional[str] = None - organization: Optional[str] = None - location: Optional[str] = None - role: Optional[str] = None - vrf: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraProviderNetwork(NetboxModel): - _modelname = "InfraProviderNetwork" - _identifiers = ("name",) - _attributes = ("provider", "tags", "description", "vendor_id") - name: str - description: Optional[str] = None - vendor_id: Optional[str] = None - provider: str - tags: Optional[List[str]] = [] - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraRack(NetboxModel): - _modelname = "InfraRack" - _identifiers = ("name", "location") - _attributes = ("role", "tags", "height", "facility_id", "serial_number", "asset_tag") - name: str - height: Optional[int] = None - facility_id: Optional[str] = None - serial_number: Optional[str] = None - asset_tag: Optional[str] = None - location: str - role: Optional[str] = None - tags: Optional[List[str]] = [] - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraRouteTarget(NetboxModel): - _modelname = "InfraRouteTarget" - _identifiers = ("name", "organization") - _attributes = ("description",) - name: str - description: Optional[str] = None - organization: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraVLAN(NetboxModel): - _modelname = "InfraVLAN" - _identifiers = ("name", "vlan_id", "location", "vlan_group") - _attributes = ("organization", "description") - name: str - description: Optional[str] = None - vlan_id: int - organization: Optional[str] = None - location: Optional[str] = None - vlan_group: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraVRF(NetboxModel): - _modelname = "InfraVRF" - _identifiers = ("name",) - _attributes = ("organization", "import_rt", "export_rt", "description", "vrf_rd") - name: str - description: Optional[str] = None - vrf_rd: Optional[str] = None - organization: Optional[str] = None - import_rt: Optional[List[str]] = [] - export_rt: Optional[List[str]] = [] - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class LocationGeneric(NetboxModel): - _modelname = "LocationGeneric" - _identifiers = ("name",) - _attributes = ("organization", "tags", "group", "description", "type") - name: str - description: Optional[str] = None - type: str - organization: Optional[str] = None - tags: Optional[List[str]] = [] - group: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class OrganizationGeneric(NetboxModel): - _modelname = "OrganizationGeneric" - _identifiers = ("name",) - _attributes = ("group", "description") - name: str - description: Optional[str] = None - group: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class RoleGeneric(NetboxModel): - _modelname = "RoleGeneric" - _identifiers = ("name",) - _attributes = ("description",) - name: str - description: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class TemplateCircuitType(NetboxModel): - _modelname = "TemplateCircuitType" - _identifiers = ("name",) - _attributes = ("tags", "description") - name: str - description: Optional[str] = None - tags: Optional[List[str]] = [] - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class TemplateDeviceType(NetboxModel): - _modelname = "TemplateDeviceType" - _identifiers = ("name", "manufacturer") - _attributes = ("tags", "part_number", "height", "full_depth") - part_number: Optional[str] = None - height: Optional[int] = None - full_depth: Optional[bool] = None - name: str - manufacturer: str - tags: Optional[List[str]] = [] - - local_id: Optional[str] = None - local_data: Optional[Any] = None diff --git a/sync/examples/observium_to_infrahub/config.yml b/sync/examples/observium_to_infrahub/config.yml deleted file mode 100644 index 5bb8d75643..0000000000 --- a/sync/examples/observium_to_infrahub/config.yml +++ /dev/null @@ -1,90 +0,0 @@ ---- -name: from-observium -source: - name: observium - settings: - url: "OBSERVIUM_URL" - # api_endpoint: "api/v0" - # auth_method: "basic" - username: "OBSERVIUM_USERNAME" - password: "OBSERVIUM_PASSWORD" - -destination: - name: infrahub - settings: - url: "http://localhost:8000" - -order: [ - "CoreStandardGroup", - "IpamIPAddress", - "InfraDevice", -] - -schema_mapping: - - name: CoreStandardGroup - mapping: groups - fields: - - name: name - mapping: group_name - - name: description - mapping: group_descr - - # Primary Device IP - - name: IpamIPAddress - mapping: devices - identifiers: ["address"] - fields: - - name: address - mapping: ip - - name: description - # We are using the field created via the transforms - mapping: new_description - filters: - - field: hostname - operation: regex - value: "^pe-[0-9]{3}$" - - field: ip - operation: is_ip_within - value: "10.0.0.0/8" - transforms: - - field: new_description - expression: "{{ hostname | upper | replace('.', '-') }}" - - - name: InfraDevice - mapping: devices - identifiers: ["name"] - fields: - - name: name - mapping: hostname - - name: description - mapping: description - - name: description - mapping: upper_serial - # /!\ Platform need to exist beforehand ! - - name: platform - mapping: os - reference: InfraPlatform - # /!\ Manufacturer need to exist beforehand ! - - name: manufacturer - mapping: vendor - reference: OrganizationManufacturer - - name: type - mapping: unknown_hardware - - name: primary_address - mapping: ip - reference: IpamIPAddress - filters: - - field: device_id - operation: ">" - value: 100 - - field: device_id - operation: "<=" - value: 200 - - field: hostname - operation: regex - value: "^pe-[0-9]{3}$" - transforms: - - field: upper_serial - expression: "{{ serial.upper() if serial else '' }}" - - field: unknown_hardware - expression: "{{ hardware if hardware else 'unknown' }}" diff --git a/sync/examples/observium_to_infrahub/infrahub/sync_adapter.py b/sync/examples/observium_to_infrahub/infrahub/sync_adapter.py deleted file mode 100644 index f9b8639335..0000000000 --- a/sync/examples/observium_to_infrahub/infrahub/sync_adapter.py +++ /dev/null @@ -1,18 +0,0 @@ -from infrahub_sync.adapters.infrahub import InfrahubAdapter - -from .sync_models import ( - CoreStandardGroup, - InfraDevice, - IpamIPAddress, -) - - -# ------------------------------------------------------- -# AUTO-GENERATED FILE, DO NOT MODIFY -# This file has been generated with the command `infrahub-sync generate` -# All modifications will be lost the next time you reexecute this command -# ------------------------------------------------------- -class InfrahubSync(InfrahubAdapter): - CoreStandardGroup = CoreStandardGroup - InfraDevice = InfraDevice - IpamIPAddress = IpamIPAddress diff --git a/sync/examples/observium_to_infrahub/infrahub/sync_models.py b/sync/examples/observium_to_infrahub/infrahub/sync_models.py deleted file mode 100644 index 4b0ff12b25..0000000000 --- a/sync/examples/observium_to_infrahub/infrahub/sync_models.py +++ /dev/null @@ -1,44 +0,0 @@ -from typing import Any, Optional - -from infrahub_sync.adapters.infrahub import InfrahubModel - - -# ------------------------------------------------------- -# AUTO-GENERATED FILE, DO NOT MODIFY -# This file has been generated with the command `infrahub-sync generate` -# All modifications will be lost the next time you reexecute this command -# ------------------------------------------------------- -class CoreStandardGroup(InfrahubModel): - _modelname = "CoreStandardGroup" - _identifiers = ("name",) - _attributes = ("description",) - name: str - description: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraDevice(InfrahubModel): - _modelname = "InfraDevice" - _identifiers = ("name",) - _attributes = ("primary_address", "platform", "description", "type") - name: str - description: Optional[str] = None - type: str - primary_address: Optional[str] = None - platform: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class IpamIPAddress(InfrahubModel): - _modelname = "IpamIPAddress" - _identifiers = ("address",) - _attributes = ("description",) - address: str - description: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None diff --git a/sync/examples/observium_to_infrahub/observium/sync_adapter.py b/sync/examples/observium_to_infrahub/observium/sync_adapter.py deleted file mode 100644 index 9edf6bf911..0000000000 --- a/sync/examples/observium_to_infrahub/observium/sync_adapter.py +++ /dev/null @@ -1,18 +0,0 @@ -from infrahub_sync.adapters.observium import ObserviumAdapter - -from .sync_models import ( - CoreStandardGroup, - InfraDevice, - IpamIPAddress, -) - - -# ------------------------------------------------------- -# AUTO-GENERATED FILE, DO NOT MODIFY -# This file has been generated with the command `infrahub-sync generate` -# All modifications will be lost the next time you reexecute this command -# ------------------------------------------------------- -class ObserviumSync(ObserviumAdapter): - CoreStandardGroup = CoreStandardGroup - InfraDevice = InfraDevice - IpamIPAddress = IpamIPAddress diff --git a/sync/examples/observium_to_infrahub/observium/sync_models.py b/sync/examples/observium_to_infrahub/observium/sync_models.py deleted file mode 100644 index 10cee57928..0000000000 --- a/sync/examples/observium_to_infrahub/observium/sync_models.py +++ /dev/null @@ -1,44 +0,0 @@ -from typing import Any, Optional - -from infrahub_sync.adapters.observium import ObserviumModel - - -# ------------------------------------------------------- -# AUTO-GENERATED FILE, DO NOT MODIFY -# This file has been generated with the command `infrahub-sync generate` -# All modifications will be lost the next time you reexecute this command -# ------------------------------------------------------- -class CoreStandardGroup(ObserviumModel): - _modelname = "CoreStandardGroup" - _identifiers = ("name",) - _attributes = ("description",) - name: str - description: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraDevice(ObserviumModel): - _modelname = "InfraDevice" - _identifiers = ("name",) - _attributes = ("primary_address", "platform", "description", "type") - name: str - description: Optional[str] = None - type: str - primary_address: Optional[str] = None - platform: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class IpamIPAddress(ObserviumModel): - _modelname = "IpamIPAddress" - _identifiers = ("address",) - _attributes = ("description",) - address: str - description: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None diff --git a/sync/examples/peering-manager_to_infrahub/config.yml b/sync/examples/peering-manager_to_infrahub/config.yml deleted file mode 100644 index f74ef36089..0000000000 --- a/sync/examples/peering-manager_to_infrahub/config.yml +++ /dev/null @@ -1,190 +0,0 @@ ---- -name: from-peering-manager -source: - name: peeringmanager - settings: - url: "https://demo.peering-manager.net" - # api_endpoint: "api" - # auth_method: "token" - token: "13bf6338aed52d172e33750d39717fff5a5f5d18" - -destination: - name: infrahub - settings: - url: "http://localhost:8000" - -order: [ - "InfraAutonomousSystem", - "InfraBGPCommunity", - "InfraBGPRoutingPolicy", - "InfraBGPPeerGroup", - "InfraIXP", - "IpamIPAddress", - "InfraIXPConnection", -] - -schema_mapping: - - name: InfraAutonomousSystem - mapping: peering/autonomous-systems - identifiers: ["asn"] - fields: - - name: name - mapping: name - - name: description - mapping: display - - name: asn - mapping: asn - - name: irr_as_set - mapping: irr_as_set - - name: ipv4_max_prefixes - mapping: ipv4_max_prefixes - - name: ipv6_max_prefixes - mapping: ipv6_max_prefixes - - name: affiliated - mapping: affiliated - - - name: InfraBGPCommunity - mapping: peering/communities - identifiers: ["name"] - fields: - - name: name - mapping: slug - - name: label - mapping: name - - name: description - mapping: description - - name: value - mapping: value - - name: community_type - mapping: type - - - - name: InfraBGPRoutingPolicy - mapping: peering/routing-policies - identifiers: ["name"] - fields: - - name: name - mapping: slug - - name: label - mapping: name - - name: description - mapping: description - - name: policy_type - mapping: type - - name: weight - mapping: weight - - name: address_family - mapping: address_family - - name: bgp_communities - mapping: communities - reference: InfraBGPCommunity - - - name: InfraBGPPeerGroup - mapping: peering/bgp-groups - identifiers: ["name"] - fields: - - name: name - mapping: name - - name: description - mapping: description - - name: status - mapping: status.value - - name: import_policies - mapping: import_routing_policies - reference: InfraBGPRoutingPolicy - - name: export_policies - mapping: export_routing_policies - reference: InfraBGPRoutingPolicy - - name: bgp_communities - mapping: communities - reference: InfraBGPCommunity - - - name: InfraIXP - mapping: peering/internet-exchanges - identifiers: ["name"] - fields: - - name: name - mapping: name - - name: description - mapping: description - - name: status - mapping: status.value - - name: import_policies - mapping: import_routing_policies - reference: InfraBGPRoutingPolicy - - name: export_policies - mapping: export_routing_policies - reference: InfraBGPRoutingPolicy - - name: bgp_communities - mapping: communities - reference: InfraBGPCommunity - # Showcase Filters - filters: - - field: name - operation: "is_not_empty" - - field: status.value - operation: contains - value: "enabled" - - field: name - operation: contains - value: "S.H.I.E.L.D" - # Showcase Transforms - transforms: - - field: description - expression: "{{ name.upper() }}[{{ status.value.lower() }}]" - - # Create the IPv4 and IPv6 from the InfraIXPConnection - # /!\ 'address' is mandatory on Infrahub but it can be empty in PeeringManager - - name: IpamIPAddress - mapping: net/connections - identifiers: ["address"] - fields: - - name: address - mapping: ipv6_address - - name: description - mapping: name - filters: - - field: ipv6_address - operation: "is_not_empty" - - name: IpamIPAddress - mapping: net/connections - identifiers: ["address"] - fields: - - name: address - mapping: ipv4_address - - name: description - mapping: name - filters: - - field: ipv4_address - operation: "is_not_empty" - - - name: InfraIXPConnection - mapping: net/connections - identifiers: ["name"] - fields: - - name: name - mapping: name - - name: peeringdb_netixlan - mapping: peeringdb_netixlan.id - - name: description - mapping: description - - name: status - mapping: status.value - - name: vlan - mapping: vlan - - name: ipv6_address - mapping: ipv6_address - reference: IpamIPAddress - - name: ipv4_address - mapping: ipv4_address - reference: IpamIPAddress - - name: internet_exchange_point - mapping: internet_exchange_point - reference: InfraIXP - # Showcase Filters - # As we filter InfraIXP on the name S.H.I.E.L.D, we need to have the same filter here - # to avoid importing IXP Connection without IXP - filters: - - field: internet_exchange_point.name - operation: contains - value: "S.H.I.E.L.D" diff --git a/sync/examples/peering-manager_to_infrahub/infrahub/sync_adapter.py b/sync/examples/peering-manager_to_infrahub/infrahub/sync_adapter.py deleted file mode 100644 index e31f3f83d1..0000000000 --- a/sync/examples/peering-manager_to_infrahub/infrahub/sync_adapter.py +++ /dev/null @@ -1,26 +0,0 @@ -from infrahub_sync.adapters.infrahub import InfrahubAdapter - -from .sync_models import ( - InfraAutonomousSystem, - InfraBGPCommunity, - InfraBGPPeerGroup, - InfraBGPRoutingPolicy, - InfraIXP, - InfraIXPConnection, - IpamIPAddress, -) - - -# ------------------------------------------------------- -# AUTO-GENERATED FILE, DO NOT MODIFY -# This file has been generated with the command `infrahub-sync generate` -# All modifications will be lost the next time you reexecute this command -# ------------------------------------------------------- -class InfrahubSync(InfrahubAdapter): - InfraAutonomousSystem = InfraAutonomousSystem - InfraBGPPeerGroup = InfraBGPPeerGroup - IpamIPAddress = IpamIPAddress - InfraBGPRoutingPolicy = InfraBGPRoutingPolicy - InfraBGPCommunity = InfraBGPCommunity - InfraIXP = InfraIXP - InfraIXPConnection = InfraIXPConnection diff --git a/sync/examples/peering-manager_to_infrahub/infrahub/sync_models.py b/sync/examples/peering-manager_to_infrahub/infrahub/sync_models.py deleted file mode 100644 index 7e87b8a69a..0000000000 --- a/sync/examples/peering-manager_to_infrahub/infrahub/sync_models.py +++ /dev/null @@ -1,120 +0,0 @@ -from typing import Any, List, Optional - -from infrahub_sync.adapters.infrahub import InfrahubModel - - -# ------------------------------------------------------- -# AUTO-GENERATED FILE, DO NOT MODIFY -# This file has been generated with the command `infrahub-sync generate` -# All modifications will be lost the next time you reexecute this command -# ------------------------------------------------------- -class InfraAutonomousSystem(InfrahubModel): - _modelname = "InfraAutonomousSystem" - _identifiers = ("asn",) - _attributes = ("name", "description", "irr_as_set", "ipv4_max_prefixes", "ipv6_max_prefixes", "affiliated") - name: str - asn: int - description: Optional[str] = None - irr_as_set: Optional[str] = None - ipv4_max_prefixes: Optional[int] = None - ipv6_max_prefixes: Optional[int] = None - affiliated: Optional[bool] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraBGPPeerGroup(InfrahubModel): - _modelname = "InfraBGPPeerGroup" - _identifiers = ("name",) - _attributes = ("import_policies", "export_policies", "bgp_communities", "description", "status") - name: str - description: Optional[str] = None - status: Optional[str] = None - import_policies: Optional[List[str]] = [] - export_policies: Optional[List[str]] = [] - bgp_communities: Optional[List[str]] = [] - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class IpamIPAddress(InfrahubModel): - _modelname = "IpamIPAddress" - _identifiers = ("address",) - _attributes = ("description",) - address: str - description: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraBGPRoutingPolicy(InfrahubModel): - _modelname = "InfraBGPRoutingPolicy" - _identifiers = ("name",) - _attributes = ("bgp_communities", "address_family", "label", "description", "policy_type", "weight") - address_family: int - label: Optional[str] = None - description: Optional[str] = None - name: str - policy_type: str - weight: Optional[int] = 1000 - bgp_communities: Optional[List[str]] = [] - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraBGPCommunity(InfrahubModel): - _modelname = "InfraBGPCommunity" - _identifiers = ("name",) - _attributes = ("description", "value", "label", "community_type") - description: Optional[str] = None - name: str - value: str - label: Optional[str] = None - community_type: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraIXP(InfrahubModel): - _modelname = "InfraIXP" - _identifiers = ("name",) - _attributes = ("export_policies", "bgp_communities", "import_policies", "description", "status") - description: Optional[str] = None - name: str - status: Optional[str] = "enabled" - export_policies: Optional[List[str]] = [] - bgp_communities: Optional[List[str]] = [] - import_policies: Optional[List[str]] = [] - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraIXPConnection(InfrahubModel): - _modelname = "InfraIXPConnection" - _identifiers = ("name",) - _attributes = ( - "ipv4_address", - "internet_exchange_point", - "ipv6_address", - "status", - "vlan", - "description", - "peeringdb_netixlan", - ) - status: Optional[str] = "enabled" - vlan: Optional[int] = None - name: str - description: Optional[str] = None - peeringdb_netixlan: Optional[int] = None - ipv4_address: Optional[str] = None - internet_exchange_point: str - ipv6_address: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None diff --git a/sync/examples/peering-manager_to_infrahub/peeringmanager/sync_adapter.py b/sync/examples/peering-manager_to_infrahub/peeringmanager/sync_adapter.py deleted file mode 100644 index 0717be8c98..0000000000 --- a/sync/examples/peering-manager_to_infrahub/peeringmanager/sync_adapter.py +++ /dev/null @@ -1,26 +0,0 @@ -from infrahub_sync.adapters.peeringmanager import PeeringmanagerAdapter - -from .sync_models import ( - InfraAutonomousSystem, - InfraBGPCommunity, - InfraBGPPeerGroup, - InfraBGPRoutingPolicy, - InfraIXP, - InfraIXPConnection, - IpamIPAddress, -) - - -# ------------------------------------------------------- -# AUTO-GENERATED FILE, DO NOT MODIFY -# This file has been generated with the command `infrahub-sync generate` -# All modifications will be lost the next time you reexecute this command -# ------------------------------------------------------- -class PeeringmanagerSync(PeeringmanagerAdapter): - InfraAutonomousSystem = InfraAutonomousSystem - InfraBGPPeerGroup = InfraBGPPeerGroup - IpamIPAddress = IpamIPAddress - InfraBGPRoutingPolicy = InfraBGPRoutingPolicy - InfraBGPCommunity = InfraBGPCommunity - InfraIXP = InfraIXP - InfraIXPConnection = InfraIXPConnection diff --git a/sync/examples/peering-manager_to_infrahub/peeringmanager/sync_models.py b/sync/examples/peering-manager_to_infrahub/peeringmanager/sync_models.py deleted file mode 100644 index 42e59d24ca..0000000000 --- a/sync/examples/peering-manager_to_infrahub/peeringmanager/sync_models.py +++ /dev/null @@ -1,120 +0,0 @@ -from typing import Any, List, Optional - -from infrahub_sync.adapters.peeringmanager import PeeringmanagerModel - - -# ------------------------------------------------------- -# AUTO-GENERATED FILE, DO NOT MODIFY -# This file has been generated with the command `infrahub-sync generate` -# All modifications will be lost the next time you reexecute this command -# ------------------------------------------------------- -class InfraAutonomousSystem(PeeringmanagerModel): - _modelname = "InfraAutonomousSystem" - _identifiers = ("asn",) - _attributes = ("name", "description", "irr_as_set", "ipv4_max_prefixes", "ipv6_max_prefixes", "affiliated") - name: str - asn: int - description: Optional[str] = None - irr_as_set: Optional[str] = None - ipv4_max_prefixes: Optional[int] = None - ipv6_max_prefixes: Optional[int] = None - affiliated: Optional[bool] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraBGPPeerGroup(PeeringmanagerModel): - _modelname = "InfraBGPPeerGroup" - _identifiers = ("name",) - _attributes = ("import_policies", "export_policies", "bgp_communities", "description", "status") - name: str - description: Optional[str] = None - status: Optional[str] = None - import_policies: Optional[List[str]] = [] - export_policies: Optional[List[str]] = [] - bgp_communities: Optional[List[str]] = [] - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class IpamIPAddress(PeeringmanagerModel): - _modelname = "IpamIPAddress" - _identifiers = ("address",) - _attributes = ("description",) - address: str - description: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraBGPRoutingPolicy(PeeringmanagerModel): - _modelname = "InfraBGPRoutingPolicy" - _identifiers = ("name",) - _attributes = ("bgp_communities", "address_family", "label", "description", "policy_type", "weight") - address_family: int - label: Optional[str] = None - description: Optional[str] = None - name: str - policy_type: str - weight: Optional[int] = 1000 - bgp_communities: Optional[List[str]] = [] - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraBGPCommunity(PeeringmanagerModel): - _modelname = "InfraBGPCommunity" - _identifiers = ("name",) - _attributes = ("description", "value", "label", "community_type") - description: Optional[str] = None - name: str - value: str - label: Optional[str] = None - community_type: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraIXP(PeeringmanagerModel): - _modelname = "InfraIXP" - _identifiers = ("name",) - _attributes = ("export_policies", "bgp_communities", "import_policies", "description", "status") - description: Optional[str] = None - name: str - status: Optional[str] = "enabled" - export_policies: Optional[List[str]] = [] - bgp_communities: Optional[List[str]] = [] - import_policies: Optional[List[str]] = [] - - local_id: Optional[str] = None - local_data: Optional[Any] = None - - -class InfraIXPConnection(PeeringmanagerModel): - _modelname = "InfraIXPConnection" - _identifiers = ("name",) - _attributes = ( - "ipv4_address", - "internet_exchange_point", - "ipv6_address", - "status", - "vlan", - "description", - "peeringdb_netixlan", - ) - status: Optional[str] = "enabled" - vlan: Optional[int] = None - name: str - description: Optional[str] = None - peeringdb_netixlan: Optional[int] = None - ipv4_address: Optional[str] = None - internet_exchange_point: str - ipv6_address: Optional[str] = None - - local_id: Optional[str] = None - local_data: Optional[Any] = None diff --git a/sync/infrahub-sync/infrahub_sync/__init__.py b/sync/infrahub-sync/infrahub_sync/__init__.py deleted file mode 100644 index e7198517b8..0000000000 --- a/sync/infrahub-sync/infrahub_sync/__init__.py +++ /dev/null @@ -1,202 +0,0 @@ -import operator -import re -from typing import Any, List, Optional, Union - -import pydantic -from jinja2 import Template -from netutils.ip import is_ip_within as netutils_is_ip_within - -from infrahub_sync.adapters.utils import get_value - - -class SchemaMappingFilter(pydantic.BaseModel): - field: str - operation: str - value: Optional[Any] = None - - -class SchemaMappingTransform(pydantic.BaseModel): - field: str - expression: str - - -class SchemaMappingField(pydantic.BaseModel): - name: str - mapping: Optional[str] = pydantic.Field(default=None) - static: Optional[Any] = pydantic.Field(default=None) - reference: Optional[str] = pydantic.Field(default=None) - - -class SchemaMappingModel(pydantic.BaseModel): - name: str - mapping: str - identifiers: Optional[List[str]] = pydantic.Field(default=None) - filters: Optional[List[SchemaMappingFilter]] = pydantic.Field(default=None) - transforms: Optional[List[SchemaMappingTransform]] = pydantic.Field(default=None) - fields: List[SchemaMappingField] = [] - - -class SyncAdapter(pydantic.BaseModel): - name: str - settings: Optional[dict[str, Any]] = {} - - -class SyncStore(pydantic.BaseModel): - type: str - settings: Optional[dict[str, Any]] = {} - - -class SyncConfig(pydantic.BaseModel): - name: str - store: Optional[SyncStore] = [] - source: SyncAdapter - destination: SyncAdapter - order: List[str] = pydantic.Field(default_factory=list) - schema_mapping: List[SchemaMappingModel] = [] - - -class SyncInstance(SyncConfig): - directory: str - - -def is_ip_within_filter(ip: str, ip_compare: Union[str, List[str]]) -> bool: - """Check if an IP address is within a given subnet.""" - return netutils_is_ip_within(ip=ip, ip_compare=ip_compare) - - -def convert_to_int(value: Any) -> int: - try: - return int(value) - except (ValueError, TypeError) as exc: - raise ValueError(f"Cannot convert '{value}' to int") from exc - - -FILTERS_OPERATIONS = { - "==": operator.eq, - "!=": operator.ne, - ">": lambda field, value: operator.gt(convert_to_int(field), convert_to_int(value)), - "<": lambda field, value: operator.lt(convert_to_int(field), convert_to_int(value)), - ">=": lambda field, value: operator.ge(convert_to_int(field), convert_to_int(value)), - "<=": lambda field, value: operator.le(convert_to_int(field), convert_to_int(value)), - "in": lambda field, value: value and field in value, - "not in": lambda field, value: field not in value, - "contains": lambda field, value: field and value in field, - "not contains": lambda field, value: field and value not in field, - "is_empty": lambda field: field is None or not field, - "is_not_empty": lambda field: field is not None and field, - "regex": lambda field, pattern: re.match(pattern, field) is not None, - # Netutils - "is_ip_within": lambda field, value: is_ip_within_filter(ip=field, ip_compare=value), -} - - -class DiffSyncMixin: - def load(self): - """Load all the models, one by one based on the order defined in top_level.""" - for item in self.top_level: - if hasattr(self, f"load_{item}"): - print(f"Loading {item}") - method = getattr(self, f"load_{item}") - method() - else: - print(f"Loading {item}") - self.model_loader(model_name=item, model=getattr(self, item)) - - def model_loader(self, model_name: str, model): - raise NotImplementedError - - -class DiffSyncModelMixin: - @classmethod - def apply_filter(cls, field_value: Any, operation: str, value: Any) -> bool: - """Apply a specified operation to a field value.""" - operation_func = FILTERS_OPERATIONS.get(operation) - if operation_func is None: - raise ValueError(f"Unsupported operation: {operation}") - - # Handle is_empty and is_not_empty which do not use the value argument - if operation in {"is_empty", "is_not_empty"}: - return operation_func(field_value) - - return operation_func(field_value, value) - - @classmethod - def apply_filters(cls, item: dict[str, Any], filters: List[SchemaMappingFilter]) -> bool: - """Apply filters to an item and return True if it passes all filters.""" - for filter_obj in filters: - # Use dot notation to access attributes - field_value = get_value(obj=item, name=filter_obj.field) - if not cls.apply_filter(field_value=field_value, operation=filter_obj.operation, value=filter_obj.value): - return False - return True - - @classmethod - def apply_transform(cls, item: dict[str, Any], transform_expr: str, field: str) -> None: - """Apply a transformation expression using Jinja2 to a specified field in the item.""" - try: - # Create a Jinja2 template from the transformation expression - template = Template(transform_expr) - - # Render the template using the item's context - transformed_value = template.render(**item) - - # Assign the result back to the item - item[field] = transformed_value - except Exception as exc: - raise ValueError(f"Failed to transform '{field}' with '{transform_expr}': {exc}") from exc - - @classmethod - def apply_transforms(cls, item: dict[str, Any], transforms: List[SchemaMappingTransform]) -> dict[str, Any]: - """Apply a list of structured transformations to an item.""" - for transform_obj in transforms: - field = transform_obj.field - expr = transform_obj.expression - cls.apply_transform(item=item, transform_expr=expr, field=field) - return item - - @classmethod - def filter_records(cls, records: list[dict], schema_mapping: SchemaMappingModel) -> list[dict]: - """ - Apply filters to the records based on the schema mapping configuration. - """ - filters = schema_mapping.filters or [] - if not filters: - return records - filtered_records = [] - for record in records: - if cls.apply_filters(item=record, filters=filters): - filtered_records.append(record) - return filtered_records - - @classmethod - def transform_records(cls, records: list[dict], schema_mapping: SchemaMappingModel) -> list[dict]: - """ - Apply transformations to the records based on the schema mapping configuration. - """ - transforms = schema_mapping.transforms or [] - if not transforms: - return records - transformed_records = [] - for record in records: - transformed_record = cls.apply_transforms(item=record, transforms=transforms) - transformed_records.append(transformed_record) - return transformed_records - - @classmethod - def get_resource_name(cls, schema_mapping: List[SchemaMappingModel]) -> str: - """Get the resource name from the schema mapping.""" - for element in schema_mapping: - if element.name == cls.__name__: - return element.mapping - raise ValueError(f"Resource name not found for class {cls.__name__}") - - @classmethod - def is_list(cls, name): - field = cls.__fields__.get(name) - if not field: - raise ValueError(f"Unable to find the field {name} under {cls}") - - if isinstance(field.default, list): - return True - - return False diff --git a/sync/infrahub-sync/infrahub_sync/adapters/infrahub.py b/sync/infrahub-sync/infrahub_sync/adapters/infrahub.py deleted file mode 100644 index 647c212aed..0000000000 --- a/sync/infrahub-sync/infrahub_sync/adapters/infrahub.py +++ /dev/null @@ -1,247 +0,0 @@ -import copy -import os -from typing import Any, Mapping - -from infrahub_sdk import ( - Config, - InfrahubClientSync, - InfrahubNodeSync, - NodeSchema, - NodeStoreSync, -) -from infrahub_sdk.exceptions import NodeNotFoundError -from infrahub_sdk.utils import compare_lists - -from diffsync import Adapter, DiffSyncModel -from infrahub_sync import ( - DiffSyncMixin, - DiffSyncModelMixin, - SyncAdapter, - SyncConfig, -) -from infrahub_sync.generator import has_field - - -def update_node(node: InfrahubNodeSync, attrs: dict): - for attr_name, attr_value in attrs.items(): - if attr_name in node._schema.attribute_names: - attr = getattr(node, attr_name) - attr.value = attr_value - - if attr_name in node._schema.relationship_names: - for rel_schema in node._schema.relationships: - if attr_name == rel_schema.name and rel_schema.cardinality == "one": - if attr_value: - if rel_schema.kind != "Generic": - peer = node._client.store.get( - key=attr_value, kind=rel_schema.peer, raise_when_missing=False - ) - else: - peer = node._client.store.get(key=attr_value, raise_when_missing=False) - if not peer: - print(f"Unable to find {rel_schema.peer} [{attr_value}] in the Store - Ignored") - continue - setattr(node, attr_name, peer) - else: - # TODO: Do we want to delete old relationship here ? - pass - - if attr_name == rel_schema.name and rel_schema.cardinality == "many": - attr = getattr(node, attr_name) - existing_peer_ids = attr.peer_ids - new_peer_ids = [ - node._client.store.get(key=value, kind=rel_schema.peer).id for value in list(attr_value) - ] - _, existing_only, new_only = compare_lists(existing_peer_ids, new_peer_ids) # noqa: F841 - - for existing_id in existing_only: - attr.remove(existing_id) - - for new_id in new_only: - attr.add(new_id) - - return node - - -def diffsync_to_infrahub(ids: Mapping[Any, Any], attrs: Mapping[Any, Any], store: NodeStoreSync, schema: NodeSchema): - data = copy.deepcopy(dict(ids)) - data.update(dict(attrs)) - - for key in list(data.keys()): - if key in schema.relationship_names: - for rel_schema in schema.relationships: - if key == rel_schema.name and rel_schema.cardinality == "one": - if data[key] is None: - del data[key] - continue - if rel_schema.kind != "Generic": - peer = store.get(key=data[key], kind=rel_schema.peer, raise_when_missing=False) - else: - peer = store.get(key=data[key], raise_when_missing=False) - if not peer: - print(f"Unable to find {rel_schema.peer} [{data[key]}] in the Store - Ignored") - continue - - data[key] = peer.id - if key == rel_schema.name and rel_schema.cardinality == "many": - if data[key] is None: - del data[key] - continue - new_values = [store.get(key=value, kind=rel_schema.peer).id for value in list(data[key])] - data[key] = new_values - - return data - - -class InfrahubAdapter(DiffSyncMixin, Adapter): - type = "Infrahub" - - def __init__(self, *args, target: str, adapter: SyncAdapter, config: SyncConfig, branch: str = None, **kwargs): - super().__init__(*args, **kwargs) - self.target = target - self.config = config - - settings = adapter.settings or {} - url = os.environ.get("INFRAHUB_ADDRESS") or os.environ.get("INFRAHUB_URL") or settings.get("url") - token = os.environ.get("INFRAHUB_API_TOKEN") or settings.get("token") - - if not url or not token: - raise ValueError("Both url and token must be specified!") - - if branch: - sdk_config = Config(timeout=60, default_branch=branch, api_token=token) - else: - sdk_config = Config(timeout=60, api_token=token) - - self.client = InfrahubClientSync(address=url, config=sdk_config) - - # We need to identify with an account until we have some auth in place - remote_account = config.source.name - try: - self.account = self.client.get(kind="CoreAccount", name__value=remote_account) - except NodeNotFoundError: - self.account = None - - def model_loader(self, model_name: str, model: "InfrahubModel"): - """ - Load and process models using schema mapping filters and transformations. - - This method retrieves data from Infrahub, applies filters and transformations - as specified in the schema mapping, and loads the processed data into the adapter. - """ - element = next((el for el in self.config.schema_mapping if el.name == model_name), None) - if element: - # Retrieve all nodes corresponding to model_name (list of InfrahubNodeSync) - nodes = self.client.all(kind=model_name, populate_store=True) - - # Transform the list of InfrahubNodeSync into a list of (node, dict) tuples - node_dict_pairs = [(node, self.infrahub_node_to_diffsync(node=node)) for node in nodes] - total = len(node_dict_pairs) - - # Extract the list of dicts for filtering and transforming - list_obj = [pair[1] for pair in node_dict_pairs] - - if self.config.source.name.title() == self.type.title(): - # Filter records - filtered_objs = model.filter_records(records=list_obj, schema_mapping=element) - print(f"{self.type}: Loading {len(filtered_objs)}/{total} {model_name}") - # Transform records - transformed_objs = model.transform_records(records=filtered_objs, schema_mapping=element) - else: - print(f"{self.type}: Loading all {total} {model_name}") - transformed_objs = list_obj - - # Create model instances after filtering and transforming - for transformed_obj in transformed_objs: - original_node = next(node for node, obj in node_dict_pairs if obj == transformed_obj) - item = model(**transformed_obj) - unique_id = item.get_unique_id() - self.client.store.set(key=unique_id, node=original_node) - self.update_or_add_model_instance(item) - - def infrahub_node_to_diffsync(self, node: InfrahubNodeSync) -> dict: - """Convert an InfrahubNode into a dict that will be used to create a DiffSyncModel.""" - data: dict[str, Any] = {"local_id": str(node.id)} - - for attr_name in node._schema.attribute_names: - if has_field(config=self.config, name=node._schema.kind, field=attr_name): - attr = getattr(node, attr_name) - # Is it the right place to do it or are we missing some de-serialize ? - # got a ValidationError from pydantic while trying to get the model(**data) - # for IPHost and IPInterface - if attr.value and not isinstance(attr.value, str): - data[attr_name] = str(attr.value) - else: - data[attr_name] = attr.value - - for rel_schema in node._schema.relationships: - if not has_field(config=self.config, name=node._schema.kind, field=rel_schema.name): - continue - if rel_schema.cardinality == "one": - rel = getattr(node, rel_schema.name) - if not rel.id: - continue - if rel_schema.kind != "Generic": - peer_node = self.client.store.get(key=rel.id, kind=rel_schema.peer, raise_when_missing=False) - else: - peer_node = self.client.store.get(key=rel.id, raise_when_missing=False) - if not peer_node: - # I am not sure if we should end up here "normaly" - print(f"Debug Unable to find {rel_schema.peer} [{rel.id}] in the Store - Pulling from Infrahub") - peer_node = self.client.get(id=rel.id, kind=rel_schema.peer, populate_store=True) - if not peer_node: - print(f"Unable to find {rel_schema.peer} [{rel.id}]") - continue - - peer_data = self.infrahub_node_to_diffsync(node=peer_node) - peer_kind = f"{peer_node._schema.namespace}{peer_node._schema.name}" - peer_model = getattr(self, peer_kind) - peer_item = peer_model(**peer_data) - - data[rel_schema.name] = peer_item.get_unique_id() - - elif rel_schema.cardinality == "many": - values = [] - rel_manager = getattr(node, rel_schema.name) - for peer in rel_manager: - peer_node = self.client.store.get(key=peer.id, kind=rel_schema.peer) - peer_data = self.infrahub_node_to_diffsync(node=peer_node) - peer_model = getattr(self, rel_schema.peer) - peer_item = peer_model(**peer_data) - - values.append(peer_item.get_unique_id()) - - data[rel_schema.name] = values - - return data - - -class InfrahubModel(DiffSyncModelMixin, DiffSyncModel): - @classmethod - def create( - cls, - adapter: Adapter, - ids: Mapping[Any, Any], - attrs: Mapping[Any, Any], - ): - schema = adapter.client.schema.get(kind=cls.__name__) - data = diffsync_to_infrahub(ids=ids, attrs=attrs, schema=schema, store=adapter.client.store) - unique_id = cls(**ids, **attrs).get_unique_id() - source_id = None - if adapter.account: - source_id = adapter.account.id - create_data = adapter.client.schema.generate_payload_create( - schema=schema, data=data, source=source_id, is_protected=True - ) - node = adapter.client.create(kind=cls.__name__, data=create_data) - node.save(allow_upsert=True) - adapter.client.store.set(key=unique_id, node=node) - - return super().create(adapter=adapter, ids=ids, attrs=attrs) - - def update(self, attrs): - node = self.adapter.client.get(id=self.local_id, kind=self.__class__.__name__) - node = update_node(node=node, attrs=attrs) - node.save(allow_upsert=True) - - return super().update(attrs) diff --git a/sync/infrahub-sync/infrahub_sync/adapters/ipfabricsync.py b/sync/infrahub-sync/infrahub_sync/adapters/ipfabricsync.py deleted file mode 100644 index 507d3671f5..0000000000 --- a/sync/infrahub-sync/infrahub_sync/adapters/ipfabricsync.py +++ /dev/null @@ -1,146 +0,0 @@ -from __future__ import annotations - -from typing import Any, Mapping - -try: - from ipfabric import IPFClient -except ImportError as e: - print(e) - -from diffsync import Adapter, DiffSyncModel -from infrahub_sync import ( - DiffSyncMixin, - DiffSyncModelMixin, - SchemaMappingModel, - SyncAdapter, - SyncConfig, -) - -ipf_filters = { - "tables/inventory/summary/platforms": {"and": [{"platform": ["empty", False]}]}, - "tables/inventory/summary/models": {"and": [{"model": ["empty", False]}]}, - "tables/inventory/pn": {"and": [{"name": ["empty", False]}]}, -} - - -class IpfabricsyncAdapter(DiffSyncMixin, Adapter): - type = "IPFabric" - - def __init__(self, *args, target: str, adapter: SyncAdapter, config: SyncConfig, **kwargs): - super().__init__(*args, **kwargs) - - self.target = target - self.client = self._create_ipfabric_client(adapter) - self.config = config - - def _create_ipfabric_client(self, adapter: SyncAdapter): - settings = adapter.settings or {} - - base_url = settings.get("base_url") or None - auth = settings.get("auth") or None - - if not base_url or not auth: - raise ValueError("Both url and auth must be specified!") - - return IPFClient(**settings) - - def build_mapping(self, reference, obj): - # Get object class and model name from the store - object_class, modelname = self.store._get_object_class_and_model(model=reference) - - # Find the schema element matching the model name - schema_element = next((element for element in self.config.schema_mapping if element.name == modelname), None) - - # Collect all relevant field mappings for identifiers - new_identifiers = [] - - # Convert schema_element.fields to a dictionary for fast lookup - field_dict = {field.name: field.mapping for field in schema_element.fields} - - # Loop through object_class._identifiers to find corresponding field mappings - for identifier in object_class._identifiers: - if identifier in field_dict: - new_identifiers.append(field_dict[identifier]) - - # Construct the unique identifier, using a fallback if a key isn't found - unique_id = "__".join(str(obj.get(key, "")) for key in new_identifiers) - return unique_id - - def model_loader(self, model_name, model): - """ - Load and process models using schema mapping filters and transformations. - - This method retrieves data from IP Fabric, and loads the processed data into the adapter. - """ - for element in self.config.schema_mapping: - if not element.name == model_name: - continue - - table = self.client.fetch_all(element.mapping, filters=ipf_filters.get(element.mapping)) - print(f"{self.type}: Loading {len(table)} from `{element.mapping}`") - - # TODO Filter records - # TODO Transform records - for obj in table: - data = self.ipfabric_dict_to_diffsync(obj=obj, mapping=element, model=model) - item = model(**data) - self.update_or_add_model_instance(item) - - def ipfabric_dict_to_diffsync(self, obj: dict, mapping: SchemaMappingModel, model: IpfabricsyncModel) -> dict: # pylint: disable=too-many-branches - data: dict[str, Any] = {"local_id": str(obj["id"])} - - for field in mapping.fields: # pylint: disable=too-many-nested-blocks - field_is_list = model.is_list(name=field.name) - - if field.static: - data[field.name] = field.static - elif not field_is_list and field.mapping and not field.reference: - value = obj.get(field.mapping) - if value is not None: - # TODO: Be able to do this in the infrahub-sync mapping file - if field.name == "speed": - data[field.name] = value / 1000 - else: - data[field.name] = value - elif field_is_list and field.mapping and not field.reference: - raise NotImplementedError( - "it's not supported yet to have an attribute of type list with a simple mapping" - ) - - elif field.mapping and field.reference: - all_nodes_for_reference = self.store.get_all(model=field.reference) - - nodes = [item for item in all_nodes_for_reference] # noqa: C416 - if not nodes and all_nodes_for_reference: - raise IndexError( - f"Unable to get '{field.mapping}' with '{field.reference}' reference from store." - f" The available models are {self.store.get_all_model_names()}" - ) - if not field_is_list: - if node := obj[field.mapping]: - matching_nodes = [] - node_id = self.build_mapping(reference=field.reference, obj=obj) - matching_nodes = [item for item in nodes if str(item) == node_id] - if len(matching_nodes) == 0: - data[field.name] = None - else: - node = matching_nodes[0] - data[field.name] = node.get_unique_id() - - return data - - -class IpfabricsyncModel(DiffSyncModelMixin, DiffSyncModel): - @classmethod - def create( - cls, - adapter: Adapter, - ids: Mapping[Any, Any], - attrs: Mapping[Any, Any], - ): - # TODO - return super().create(adapter=adapter, ids=ids, attrs=attrs) - - def update(self, attrs): - # TODO - return super().update(attrs) diff --git a/sync/infrahub-sync/infrahub_sync/adapters/librenms.py b/sync/infrahub-sync/infrahub_sync/adapters/librenms.py deleted file mode 100644 index ea249f7479..0000000000 --- a/sync/infrahub-sync/infrahub_sync/adapters/librenms.py +++ /dev/null @@ -1,158 +0,0 @@ -from __future__ import annotations - -import os -from typing import Any, Mapping - -from diffsync import Adapter, DiffSyncModel -from infrahub_sync import ( - DiffSyncMixin, - DiffSyncModelMixin, - SchemaMappingModel, - SyncAdapter, - SyncConfig, -) - -from .rest_api_client import RestApiClient -from .utils import derive_identifier_key, get_value - - -class LibrenmsAdapter(DiffSyncMixin, Adapter): - type = "LibreNMS" - - def __init__(self, *args, target: str, adapter: SyncAdapter, config: SyncConfig, **kwargs): - super().__init__(*args, **kwargs) - - self.target = target - self.client = self._create_rest_client(adapter) - self.config = config - - def _create_rest_client(self, adapter: SyncAdapter) -> RestApiClient: - settings = adapter.settings or {} - url = os.environ.get("LIBRENMS_ADDRESS") or os.environ.get("LIBRENMS_URL") or settings.get("url") - api_endpoint = settings.get("api_endpoint", "/api/v0") - auth_method = settings.get("auth_method", "x-auth-token") - api_token = os.environ.get("LIBRENMS_TOKEN") or settings.get("token") - timeout = settings.get("timeout", 30) - - if not url: - raise ValueError("url must be specified!") - - if auth_method != "x-auth-token" or not api_token: - raise ValueError("Token-based authentication requires a valid API token!") - - full_base_url = f"{url.rstrip('/')}/{api_endpoint.strip('/')}" - return RestApiClient(base_url=full_base_url, auth_method=auth_method, api_token=api_token, timeout=timeout) - - def model_loader(self, model_name: str, model: LibrenmsModel): - """ - Load and process models using schema mapping filters and transformations. - - This method retrieves data from Librenms, applies filters and transformations - as specified in the schema mapping, and loads the processed data into the adapter. - """ - for element in self.config.schema_mapping: - if not element.name == model_name: - continue - - # Use the resource endpoint from the schema mapping - resource_name = element.mapping - response_key = resource_name.split("/")[-1] - - try: - # Fetch data from the specified resource endpoint - response_data = self.client.get(resource_name) - objs = response_data.get(response_key, []) - except Exception as exc: - raise ValueError(f"Error fetching data from REST API: {str(exc)}") from exc - - total = len(objs) - if self.config.source.name.title() == self.type.title(): - # Filter records - filtered_objs = model.filter_records(records=objs, schema_mapping=element) - print(f"{self.type}: Loading {len(filtered_objs)}/{total} {resource_name}") - # Transform records - transformed_objs = model.transform_records(records=filtered_objs, schema_mapping=element) - else: - print(f"{self.type}: Loading all {total} {resource_name}") - transformed_objs = objs - - # Create model instances after filtering and transforming - for obj in transformed_objs: - data = self.obj_to_diffsync(obj=obj, mapping=element, model=model) - item = model(**data) - self.add(item) - - def obj_to_diffsync(self, obj: dict[str, Any], mapping: SchemaMappingModel, model: LibrenmsModel) -> dict: # noqa: C901 - obj_id = derive_identifier_key(obj=obj) - data: dict[str, Any] = {"local_id": str(obj_id)} - - for field in mapping.fields: # pylint: disable=too-many-nested-blocks - field_is_list = model.is_list(name=field.name) - - if field.static: - data[field.name] = field.static - elif not field_is_list and field.mapping and not field.reference: - value = get_value(obj, field.mapping) - if value is not None: - data[field.name] = value - elif field_is_list and field.mapping and not field.reference: - raise NotImplementedError( - "it's not supported yet to have an attribute of type list with a simple mapping" - ) - - elif field.mapping and field.reference: - all_nodes_for_reference = self.store.get_all(model=field.reference) - nodes = [item for item in all_nodes_for_reference] # noqa: C416 - if not nodes and all_nodes_for_reference: - raise IndexError( - f"Unable to get '{field.mapping}' with '{field.reference}' reference from store." - f" The available models are {self.store.get_all_model_names()}" - ) - if not field_is_list: - if node := get_value(obj, field.mapping): - if isinstance(node, dict): - matching_nodes = [] - node_id = node.get("id", None) - matching_nodes = [item for item in nodes if item.local_id == str(node_id)] - if len(matching_nodes) == 0: - raise IndexError(f"Unable to locate the node {model} {node_id}") - node = matching_nodes[0] - data[field.name] = node.get_unique_id() - else: - # Some link are referencing the node identifier directly without the id (i.e location in device) - data[field.name] = node - - else: - data[field.name] = [] - for node in get_value(obj, field.mapping): - if not node: - continue - node_id = node.get("id", None) - if not node_id: - if isinstance(node, tuple): - node_id = node[1] if node[0] == "id" else None - if not node_id: - continue - matching_nodes = [item for item in nodes if item.local_id == str(node_id)] - if len(matching_nodes) == 0: - raise IndexError(f"Unable to locate the node {field.reference} {node_id}") - data[field.name].append(matching_nodes[0].get_unique_id()) - data[field.name] = sorted(data[field.name]) - - return data - - -class LibrenmsModel(DiffSyncModelMixin, DiffSyncModel): - @classmethod - def create( - cls, - adapter: Adapter, - ids: Mapping[Any, Any], - attrs: Mapping[Any, Any], - ): - # TODO - return super().create(adapter=adapter, ids=ids, attrs=attrs) - - def update(self, attrs): - # TODO - return super().update(attrs) diff --git a/sync/infrahub-sync/infrahub_sync/adapters/nautobot.py b/sync/infrahub-sync/infrahub_sync/adapters/nautobot.py deleted file mode 100644 index 3a59b95847..0000000000 --- a/sync/infrahub-sync/infrahub_sync/adapters/nautobot.py +++ /dev/null @@ -1,159 +0,0 @@ -from __future__ import annotations - -# pylint: disable=R0801 -import os -from typing import Any, Mapping - -import pynautobot - -from diffsync import Adapter, DiffSyncModel -from infrahub_sync import ( - DiffSyncMixin, - DiffSyncModelMixin, - SchemaMappingModel, - SyncAdapter, - SyncConfig, -) - -from .utils import get_value - - -class NautobotAdapter(DiffSyncMixin, Adapter): - type = "Nautobot" - - def __init__(self, *args, target: str, adapter: SyncAdapter, config: SyncConfig, **kwargs): - super().__init__(*args, **kwargs) - - self.target = target - self.client = self._create_nautobot_client(adapter) - self.config = config - - def _create_nautobot_client(self, adapter: SyncAdapter): - settings = adapter.settings or {} - url = os.environ.get("NAUTOBOT_ADDRESS") or os.environ.get("NAUTOBOT_URL") or settings.get("url") - token = os.environ.get("NAUTOBOT_TOKEN") or settings.get("token") - - if not url or not token: - raise ValueError("Both url and token must be specified!") - - return pynautobot.api(url, token=token, threading=True, max_workers=5, retries=3) - - def model_loader(self, model_name: str, model: NautobotModel): - """ - Load and process models using schema mapping filters and transformations. - - This method retrieves data from Nautobot, applies filters and transformations - as specified in the schema mapping, and loads the processed data into the adapter. - """ - # Retrieve schema mapping for this model - for element in self.config.schema_mapping: - if not element.name == model_name: - continue - - # Use the resource endpoint from the schema mapping - app_name, resource_name = element.mapping.split(".") - nautobot_app = getattr(self.client, app_name) - nautobot_model = getattr(nautobot_app, resource_name) - - # Retrieve all objects (RecordSet) - nodes = nautobot_model.all() - - # Transform the RecordSet into a list of Dict - list_obj = [] - for node in nodes: - list_obj.append(dict(node)) - - total = len(list_obj) - if self.config.source.name.title() == self.type.title(): - # Filter records - filtered_objs = model.filter_records(records=list_obj, schema_mapping=element) - print(f"{self.type}: Loading {len(filtered_objs)}/{total} {resource_name}") - # Transform records - transformed_objs = model.transform_records(records=filtered_objs, schema_mapping=element) - else: - print(f"{self.type}: Loading all {total} {resource_name}") - transformed_objs = list_obj - - # Create model instances after filtering and transforming - for obj in transformed_objs: - data = self.nautobot_obj_to_diffsync(obj=obj, mapping=element, model=model) - item = model(**data) - self.add(item) - - def nautobot_obj_to_diffsync(self, obj: dict[str, Any], mapping: SchemaMappingModel, model: NautobotModel) -> dict: # noqa: C901 - obj_id = obj.get("id", None) - data: dict[str, Any] = {"local_id": str(obj_id)} - - for field in mapping.fields: # pylint: disable=too-many-nested-blocks - field_is_list = model.is_list(name=field.name) - - if field.static: - data[field.name] = field.static - elif not field_is_list and field.mapping and not field.reference: - value = get_value(obj, field.mapping) - if value is not None: - data[field.name] = value - elif field_is_list and field.mapping and not field.reference: - raise NotImplementedError( - "it's not supported yet to have an attribute of type list with a simple mapping" - ) - - elif field.mapping and field.reference: - all_nodes_for_reference = self.store.get_all(model=field.reference) - nodes = [item for item in all_nodes_for_reference] # noqa: C416 - if not nodes and all_nodes_for_reference: - raise IndexError( - f"Unable to get '{field.mapping}' with '{field.reference}' reference from store." - f" The available models are {self.store.get_all_model_names()}" - ) - if not field_is_list: - if node := get_value(obj, field.mapping): - matching_nodes = [] - node_id = node.get("id", None) - if node_id: - matching_nodes = [item for item in nodes if item.local_id == str(node_id)] - if len(matching_nodes) == 0: - # If the peer is a Node we are filtering, we could end up not finding it - # raise IndexError(f"Unable to locate the node {field.name} {node_id}") - print(f"Unable to locate the node {field.name} {node_id}") - continue - node = matching_nodes[0] - data[field.name] = node.get_unique_id() - - else: - data[field.name] = [] - for node in get_value(obj, field.mapping): - if not node: - continue - node_id = node.get("id", None) - if not node_id: - if isinstance(node, tuple): - node_id = node[1] if node[0] == "id" else None - if not node_id: - continue - matching_nodes = [item for item in nodes if item.local_id == str(node_id)] - if len(matching_nodes) == 0: - # If the peer is a Node we are filtering, we could end up not finding it - # raise IndexError(f"Unable to locate the node {field.name} {node_id}") - print(f"Unable to locate the node {field.name} {node_id}") - continue - data[field.name].append(matching_nodes[0].get_unique_id()) - data[field.name] = sorted(data[field.name]) - - return data - - -class NautobotModel(DiffSyncModelMixin, DiffSyncModel): - @classmethod - def create( - cls, - adapter: Adapter, - ids: Mapping[Any, Any], - attrs: Mapping[Any, Any], - ): - # TODO - return super().create(adapter=adapter, ids=ids, attrs=attrs) - - def update(self, attrs): - # TODO - return super().update(attrs) diff --git a/sync/infrahub-sync/infrahub_sync/adapters/netbox.py b/sync/infrahub-sync/infrahub_sync/adapters/netbox.py deleted file mode 100644 index 7cc975fd76..0000000000 --- a/sync/infrahub-sync/infrahub_sync/adapters/netbox.py +++ /dev/null @@ -1,152 +0,0 @@ -from __future__ import annotations - -# pylint: disable=R0801 -import os -from typing import Any, Mapping - -import pynetbox - -from diffsync import Adapter, DiffSyncModel -from infrahub_sync import ( - DiffSyncMixin, - DiffSyncModelMixin, - SchemaMappingModel, - SyncAdapter, - SyncConfig, -) - -from .utils import get_value - - -class NetboxAdapter(DiffSyncMixin, Adapter): - type = "Netbox" - - def __init__(self, *args, target: str, adapter: SyncAdapter, config: SyncConfig, **kwargs): - super().__init__(*args, **kwargs) - - self.target = target - self.client = self._create_netbox_client(adapter) - self.config = config - - def _create_netbox_client(self, adapter: SyncAdapter): - settings = adapter.settings or {} - url = os.environ.get("NETBOX_ADDRESS") or os.environ.get("NETBOX_URL") or settings.get("url") - token = os.environ.get("NETBOX_TOKEN") or settings.get("token") - - if not url or not token: - raise ValueError("Both url and token must be specified!") - - return pynetbox.api(url, token=token) - - def model_loader(self, model_name: str, model: NetboxModel): - """ - Load and process models using schema mapping filters and transformations. - - This method retrieves data from Netbox, applies filters and transformations - as specified in the schema mapping, and loads the processed data into the adapter. - """ - for element in self.config.schema_mapping: - if not element.name == model_name: - continue - - # Use the resource endpoint from the schema mapping - app_name, resource_name = element.mapping.split(".") - netbox_app = getattr(self.client, app_name) - netbox_model = getattr(netbox_app, resource_name) - - # Retrieve all objects (RecordSet) - nodes = netbox_model.all() - - # Transform the RecordSet into a list of Dict - list_obj = [] - for node in nodes: - list_obj.append(dict(node)) - - total = len(list_obj) - if self.config.source.name.title() == self.type.title(): - # Filter records - filtered_objs = model.filter_records(records=list_obj, schema_mapping=element) - print(f"{self.type}: Loading {len(filtered_objs)}/{total} {resource_name}") - # Transform records - transformed_objs = model.transform_records(records=filtered_objs, schema_mapping=element) - else: - print(f"{self.type}: Loading all {total} {resource_name}") - transformed_objs = list_obj - - # Create model instances after filtering and transforming - for obj in transformed_objs: - data = self.netbox_obj_to_diffsync(obj=obj, mapping=element, model=model) - item = model(**data) - self.add(item) - - def netbox_obj_to_diffsync(self, obj: dict[str, Any], mapping: SchemaMappingModel, model: NetboxModel) -> dict: # noqa: C901 - obj_id = obj.get("id", None) - data: dict[str, Any] = {"local_id": str(obj_id)} - - for field in mapping.fields: # pylint: disable=too-many-nested-blocks - field_is_list = model.is_list(name=field.name) - - if field.static: - data[field.name] = field.static - elif not field_is_list and field.mapping and not field.reference: - value = get_value(obj, field.mapping) - if value is not None: - data[field.name] = value - elif field_is_list and field.mapping and not field.reference: - raise NotImplementedError( - "It's not supported yet to have an attribute of type list with a simple mapping" - ) - elif field.mapping and field.reference: - all_nodes_for_reference = self.store.get_all(model=field.reference) - nodes = [item for item in all_nodes_for_reference] - if not nodes and all_nodes_for_reference: - raise IndexError( - f"Unable to get '{field.mapping}' with '{field.reference}' reference from store." - f" The available models are {self.store.get_all_model_names()}" - ) - if not field_is_list: - if node := get_value(obj, field.mapping): - if isinstance(node, dict): - matching_nodes = [] - node_id = node.get("id", None) - matching_nodes = [item for item in nodes if item.local_id == str(node_id)] - if len(matching_nodes) == 0: - raise IndexError(f"Unable to locate the node {field.name} {node_id}") - node = matching_nodes[0] - data[field.name] = node.get_unique_id() - else: - data[field.name] = node - else: - data[field.name] = [] - for node in get_value(obj, field.mapping): - if not node: - continue - node_id = node.get("id", None) - if not node_id: - if isinstance(node, tuple): - node_id = node[1] if node[0] == "id" else None - if not node_id: - continue - matching_nodes = [item for item in nodes if item.local_id == str(node_id)] - if len(matching_nodes) == 0: - raise IndexError(f"Unable to locate the node {field.reference} {node_id}") - data[field.name].append(matching_nodes[0].get_unique_id()) - data[field.name] = sorted(data[field.name]) - - return data - - -class NetboxModel(DiffSyncModelMixin, DiffSyncModel): - @classmethod - def create( - cls, - adapter: Adapter, - ids: Mapping[Any, Any], - attrs: Mapping[Any, Any], - ): - # TODO - return super().create(adapter=adapter, ids=ids, attrs=attrs) - - def update(self, attrs): - # TODO - return super().update(attrs) diff --git a/sync/infrahub-sync/infrahub_sync/adapters/observium.py b/sync/infrahub-sync/infrahub_sync/adapters/observium.py deleted file mode 100644 index 09cdc4149a..0000000000 --- a/sync/infrahub-sync/infrahub_sync/adapters/observium.py +++ /dev/null @@ -1,166 +0,0 @@ -from __future__ import annotations - -import os -from typing import Any, Mapping - -from diffsync import Adapter, DiffSyncModel -from infrahub_sync import ( - DiffSyncMixin, - DiffSyncModelMixin, - SchemaMappingModel, - SyncAdapter, - SyncConfig, -) - -from .rest_api_client import RestApiClient -from .utils import derive_identifier_key, get_value - - -class ObserviumAdapter(DiffSyncMixin, Adapter): - type = "Observium" - - def __init__(self, *args, target: str, adapter: SyncAdapter, config: SyncConfig, **kwargs): - super().__init__(*args, **kwargs) - - self.target = target - self.client = self._create_rest_client(adapter) - self.config = config - - def _create_rest_client(self, adapter: SyncAdapter) -> RestApiClient: - settings = adapter.settings or {} - url = os.environ.get("OBSERVIUM_ADDRESS") or os.environ.get("OBSERVIUM_URL") or settings.get("url") - api_endpoint = settings.get("api_endpoint", "/api/v0") - auth_method = settings.get("auth_method", "basic") - api_token = os.environ.get("OBSERVIUM_TOKEN") or settings.get("token") - username = os.environ.get("OBSERVIUM_USERNAME") or settings.get("username") - password = os.environ.get("OBSERVIUM_PASSWORD") or settings.get("password") - timeout = settings.get("timeout") - - if not url: - raise ValueError("url must be specified!") - - base_url = f"{url.rstrip('/')}/{api_endpoint.strip('/')}" - return RestApiClient( - base_url=base_url, - auth_method=auth_method, - api_token=api_token, - username=username, - password=password, - timeout=timeout, - ) - - def model_loader(self, model_name: str, model: ObserviumModel): - """ - Load and process models using schema mapping filters and transformations. - - This method retrieves data from Observium, applies filters and transformations - as specified in the schema mapping, and loads the processed data into the adapter. - """ - for element in self.config.schema_mapping: - if not element.name == model_name: - continue - - # Use the resource endpoint from the schema mapping - resource_name = element.mapping - - try: - # Fetch data from the specified resource endpoint - response_data = self.client.get(resource_name) - objs = response_data.get(resource_name, {}) - except Exception as exc: - raise ValueError(f"Error fetching data from REST API: {str(exc)}") from exc - - if isinstance(objs, dict): - objs = list(objs.values()) - - total = len(objs) - if self.config.source.name.title() == self.type.title(): - # Filter records - filtered_objs = model.filter_records(records=objs, schema_mapping=element) - print(f"{self.type}: Loading {len(filtered_objs)}/{total} {resource_name}") - # Transform records - transformed_objs = model.transform_records(records=filtered_objs, schema_mapping=element) - else: - print(f"{self.type}: Loading all {total} {resource_name}") - transformed_objs = objs - - # Create model instances after filtering and transforming - for obj in transformed_objs: - data = self.obj_to_diffsync(obj=obj, mapping=element, model=model) - item = model(**data) - self.add(item) - - def obj_to_diffsync(self, obj: dict[str, Any], mapping: SchemaMappingModel, model: ObserviumModel) -> dict: # noqa: C901 - obj_id = derive_identifier_key(obj=obj) - data: dict[str, Any] = {"local_id": str(obj_id)} - - for field in mapping.fields: # pylint: disable=too-many-nested-blocks - field_is_list = model.is_list(name=field.name) - - if field.static: - data[field.name] = field.static - elif not field_is_list and field.mapping and not field.reference: - value = get_value(obj, field.mapping) - if value is not None: - data[field.name] = value - elif field_is_list and field.mapping and not field.reference: - raise NotImplementedError( - "it's not supported yet to have an attribute of type list with a simple mapping" - ) - - elif field.mapping and field.reference: - all_nodes_for_reference = self.store.get_all(model=field.reference) - nodes = [item for item in all_nodes_for_reference] # noqa: C416 - if not nodes and all_nodes_for_reference: - raise IndexError( - f"Unable to get '{field.mapping}' with '{field.reference}' reference from store." - f" The available models are {self.store.get_all_model_names()}" - ) - if not field_is_list: - if node := get_value(obj, field.mapping): - if isinstance(node, dict): - matching_nodes = [] - node_id = node.get("id", None) - matching_nodes = [item for item in nodes if item.local_id == str(node_id)] - if len(matching_nodes) == 0: - raise IndexError(f"Unable to locate the node {model} {node_id}") - node = matching_nodes[0] - data[field.name] = node.get_unique_id() - else: - # Some link are referencing the node identifier directly without the id (i.e location in device) - data[field.name] = node - - else: - data[field.name] = [] - for node in get_value(obj, field.mapping): - if not node: - continue - node_id = getattr(node, "id", None) - if not node_id: - if isinstance(node, tuple): - node_id = node[1] if node[0] == "id" else None - if not node_id: - continue - matching_nodes = [item for item in nodes if item.local_id == str(node_id)] - if len(matching_nodes) == 0: - raise IndexError(f"Unable to locate the node {field.reference} {node_id}") - data[field.name].append(matching_nodes[0].get_unique_id()) - data[field.name] = sorted(data[field.name]) - - return data - - -class ObserviumModel(DiffSyncModelMixin, DiffSyncModel): - @classmethod - def create( - cls, - adapter: Adapter, - ids: Mapping[Any, Any], - attrs: Mapping[Any, Any], - ): - # TODO - return super().create(adapter=adapter, ids=ids, attrs=attrs) - - def update(self, attrs): - # TODO - return super().update(attrs) diff --git a/sync/infrahub-sync/infrahub_sync/adapters/peeringmanager.py b/sync/infrahub-sync/infrahub_sync/adapters/peeringmanager.py deleted file mode 100644 index 7310a563ae..0000000000 --- a/sync/infrahub-sync/infrahub_sync/adapters/peeringmanager.py +++ /dev/null @@ -1,205 +0,0 @@ -from __future__ import annotations - -import os -from typing import Any, Mapping - -import requests - -from diffsync import Adapter, DiffSyncModel -from infrahub_sync import ( - DiffSyncMixin, - DiffSyncModelMixin, - SchemaMappingModel, - SyncAdapter, - SyncConfig, -) - -from .rest_api_client import RestApiClient -from .utils import derive_identifier_key, get_value - - -class PeeringmanagerAdapter(DiffSyncMixin, Adapter): - type = "Peeringmanager" - - def __init__(self, *args, target: str, adapter: SyncAdapter, config: SyncConfig, **kwargs): - super().__init__(*args, **kwargs) - - self.target = target - self.client = self._create_rest_client(adapter) - self.config = config - - def _create_rest_client(self, adapter: SyncAdapter) -> RestApiClient: - settings = adapter.settings or {} - url = os.environ.get("PEERING_MANAGER_ADDRESS") or os.environ.get("PEERING_MANAGER_URL") or settings.get("url") - api_endpoint = settings.get("api_endpoint", "/api") # Default endpoint, change if necessary - auth_method = settings.get("auth_method", "token") - api_token = os.environ.get("PEERING_MANAGER_TOKEN") or settings.get("token") - timeout = settings.get("timeout", 30) - - if not url: - raise ValueError("url must be specified!") - - if auth_method != "token" or not api_token: - raise ValueError("Token-based authentication requires a valid API token!") - - full_base_url = f"{url.rstrip('/')}/{api_endpoint.strip('/')}" - return RestApiClient(base_url=full_base_url, auth_method=auth_method, api_token=api_token, timeout=timeout) - - def model_loader(self, model_name: str, model: PeeringmanagerModel): - """ - Load and process models using schema mapping filters and transformations. - - This method retrieves data from Peering Manager, applies filters and transformations - as specified in the schema mapping, and loads the processed data into the adapter. - """ - # Retrieve schema mapping for this model - for element in self.config.schema_mapping: - if not element.name == model_name: - continue - - # Use the resource endpoint from the schema mapping - resource_name = element.mapping - - try: - # Retrieve all objects - response_data = self.client.get(resource_name) - objs = response_data.get("results", []) - except Exception as exc: - raise ValueError(f"Error fetching data from REST API: {str(exc)}") from exc - - total = len(objs) - if self.config.source.name.title() == self.type.title(): - # Filter records - filtered_objs = model.filter_records(records=objs, schema_mapping=element) - print(f"{self.type}: Loading {len(filtered_objs)}/{total} {resource_name}") - # Transform records - transformed_objs = model.transform_records(records=filtered_objs, schema_mapping=element) - else: - print(f"{self.type}: Loading all {total} {resource_name}") - transformed_objs = objs - - # Create model instances after filtering and transforming - for obj in transformed_objs: - data = self.obj_to_diffsync(obj=obj, mapping=element, model=model) - item = model(**data) - self.add(item) - - def obj_to_diffsync(self, obj: dict[str, Any], mapping: SchemaMappingModel, model: PeeringmanagerModel) -> dict: # noqa: C901 - obj_id = derive_identifier_key(obj=obj) - data: dict[str, Any] = {"local_id": str(obj_id)} - - for field in mapping.fields: - field_is_list = model.is_list(name=field.name) - - if field.static: - data[field.name] = field.static - elif not field_is_list and field.mapping and not field.reference: - value = get_value(obj, field.mapping) - if value is not None: - data[field.name] = value - elif field_is_list and field.mapping and not field.reference: - raise NotImplementedError( - "It's not supported yet to have an attribute of type list with a simple mapping" - ) - elif field.mapping and field.reference: - all_nodes_for_reference = self.store.get_all(model=field.reference) - nodes = [item for item in all_nodes_for_reference] - if not nodes and all_nodes_for_reference: - raise IndexError( - f"Unable to get '{field.mapping}' with '{field.reference}' reference from store." - f" The available models are {self.store.get_all_model_names()}" - ) - if not field_is_list: - if node := get_value(obj, field.mapping): - if isinstance(node, dict): - matching_nodes = [] - node_id = node.get("id", None) - matching_nodes = [item for item in nodes if item.local_id == str(node_id)] - if len(matching_nodes) == 0: - raise IndexError(f"Unable to locate the node {field.name} {node_id}") - node = matching_nodes[0] - data[field.name] = node.get_unique_id() - else: - data[field.name] = node - else: - data[field.name] = [] - for node in get_value(obj, field.mapping): - if not node: - continue - node_id = node.get("id", None) - if not node_id: - if isinstance(node, tuple): - node_id = node[1] if node[0] == "id" else None - if not node_id: - continue - matching_nodes = [item for item in nodes if item.local_id == str(node_id)] - if len(matching_nodes) == 0: - raise IndexError(f"Unable to locate the node {field.reference} {node_id}") - data[field.name].append(matching_nodes[0].get_unique_id()) - data[field.name] = sorted(data[field.name]) - - return data - - -class PeeringmanagerModel(DiffSyncModelMixin, DiffSyncModel): - @classmethod - def create( - cls, - adapter: Adapter, - ids: Mapping[Any, Any], - attrs: Mapping[Any, Any], - ): - # TODO - return super().create(adapter=adapter, ids=ids, attrs=attrs) - - def update(self, attrs: dict): - """ - Update an object in the Peering Manager system with new attributes. - - This method maps the given attributes to the corresponding target fields - based on the schema mapping configuration, and sends an update request - to the API endpoint of the object. - """ - # Determine the resource name using the schema mapping - resource_name = self.__class__.get_resource_name(schema_mapping=self.adapter.config.schema_mapping) - - # Determine the unique identifier for the API request - unique_identifier = self.local_id if hasattr(self, "local_id") else self.get_unique_id() - endpoint = f"{resource_name}/{unique_identifier}/" - - # Map incoming attributes to the target attributes based on schema mapping - mapped_attrs: dict[str, Any] = {} - for field in self.adapter.config.schema_mapping: - if field.name == self.__class__.get_type(): - for field_mapping in field.fields: - # Map source field name to target field name - if field_mapping.name in attrs: - target_field_name = field_mapping.mapping - value = attrs[field_mapping.name] - - # Check if the field is a relationship - if field_mapping.reference: - all_nodes_for_reference = self.adapter.store.get_all(model=field_mapping.reference) - - if isinstance(value, list): - # For lists, filter nodes to match the unique IDs in the attribute value - filtered_nodes = [ - node for node in all_nodes_for_reference if node.get_unique_id() in value - ] - mapped_attrs[target_field_name] = [node.local_id for node in filtered_nodes] - else: - # For single references, find the matching node - filtered_node = next( - (node for node in all_nodes_for_reference if node.get_unique_id() == value), None - ) - if filtered_node: - mapped_attrs[target_field_name] = filtered_node.local_id - else: - mapped_attrs[target_field_name] = value - - # Attempt to send the update request to the API - try: - self.adapter.client.patch(endpoint, data=mapped_attrs) - return super().update(attrs) - except (requests.exceptions.HTTPError, ConnectionError) as exc: - raise ValueError(f"Error during update: {str(exc)}") from exc diff --git a/sync/infrahub-sync/infrahub_sync/adapters/rest_api_client.py b/sync/infrahub-sync/infrahub_sync/adapters/rest_api_client.py deleted file mode 100644 index 31afc60c60..0000000000 --- a/sync/infrahub-sync/infrahub_sync/adapters/rest_api_client.py +++ /dev/null @@ -1,90 +0,0 @@ -from typing import Any, Optional - -import requests - - -class RestApiClient: - def __init__( - self, - base_url: str, - auth_method: str, - api_token: Optional[str] = None, - username: Optional[str] = None, - password: Optional[str] = None, - timeout: Optional[int] = 30, - ): - self.base_url = base_url.rstrip("/") - self.headers = { - "Content-Type": "application/json", - "Accept": "application/json", - } - - # Determine authentication method, some are use by more than one API. - # Example : - # -> Peering Manager - if auth_method == "token" and api_token: - self.headers["Authorization"] = f"Token {api_token}" - # -> LibreNMS - elif auth_method == "x-auth-token" and api_token: - self.headers["X-Auth-Token"] = api_token - # -> Peering DB - elif auth_method == "api-key" and api_token: - self.headers["Authorization"] = f"Api-Key {api_token}" - # -> RIPE API - elif auth_method == "key" and api_token: - self.headers["Authorization"] = f"Key {api_token}" - # -> Observium - elif auth_method == "basic" and username and password: - self.auth = (username, password) - else: - raise ValueError("Invalid authentication configuration!") - - self.timeout = timeout - - def request( - self, method: str, endpoint: str, params: Optional[dict[str, Any]] = None, data: Optional[dict[str, Any]] = None - ) -> Any: - """Make a request to the REST API.""" - url = f"{self.base_url}/{endpoint.lstrip('/')}" - - try: - if hasattr(self, "auth"): - response = requests.request( - method=method, - url=url, - headers=self.headers, - params=params, - json=data, - auth=self.auth, - timeout=self.timeout, - ) - else: - response = requests.request( - method=method, url=url, headers=self.headers, params=params, json=data, timeout=self.timeout - ) - - response.raise_for_status() # Raise an HTTPError for bad responses - - try: - return response.json() - except requests.exceptions.JSONDecodeError as exc: - print("Response content is not valid JSON:", response.text) # Print the response content - raise ValueError("Response content is not valid JSON.") from exc - - except requests.exceptions.RequestException as exc: - raise ConnectionError(f"API request failed: {str(exc)}") from exc - - def get(self, endpoint: str, params: Optional[dict[str, Any]] = None) -> Any: - return self.request("GET", endpoint, params=params) - - def post(self, endpoint: str, data: Optional[dict[str, Any]] = None) -> Any: - return self.request("POST", endpoint, data=data) - - def patch(self, endpoint: str, data: Optional[dict[str, Any]] = None) -> Any: - return self.request("PATCH", endpoint, data=data) - - def put(self, endpoint: str, data: Optional[dict[str, Any]] = None) -> Any: - return self.request("PUT", endpoint, data=data) - - def delete(self, endpoint: str) -> Any: - return self.request("DELETE", endpoint) diff --git a/sync/infrahub-sync/infrahub_sync/adapters/utils.py b/sync/infrahub-sync/infrahub_sync/adapters/utils.py deleted file mode 100644 index eb4f8f515e..0000000000 --- a/sync/infrahub-sync/infrahub_sync/adapters/utils.py +++ /dev/null @@ -1,38 +0,0 @@ -from typing import Any, Optional - - -def get_value(obj, name: str): - """Query a value in dot notation recursively""" - if "." not in name: - # Check if the object is a dictionary and use appropriate method to access the attribute. - if isinstance(obj, dict): - return obj.get(name) - return getattr(obj, name, None) - - first_name, remaining_part = name.split(".", maxsplit=1) - - # Check if the object is a dictionary and use appropriate method to access the attribute. - if isinstance(obj, dict): - sub_obj = obj.get(first_name) - else: - sub_obj = getattr(obj, first_name, None) - - if not sub_obj: - return None - return get_value(obj=sub_obj, name=remaining_part) - - -def derive_identifier_key(obj: dict[str, Any]) -> Optional[str]: - """Try to get obj.id, and if it doesn't exist, try to get a key ending with _id""" - obj_id = obj.get("id", None) - if obj_id is None: - for key, value in obj.items(): - if key.endswith("_id"): - if value: - obj_id = value - break - - # If we still didn't find any id, raise ValueError - if obj_id is None: - raise ValueError("No suitable identifier key found in object") - return obj_id diff --git a/sync/infrahub-sync/infrahub_sync/cli.py b/sync/infrahub-sync/infrahub_sync/cli.py deleted file mode 100644 index 00121b6018..0000000000 --- a/sync/infrahub-sync/infrahub_sync/cli.py +++ /dev/null @@ -1,149 +0,0 @@ -import logging -from timeit import default_timer as timer - -import typer -from infrahub_sdk import InfrahubClientSync -from infrahub_sdk.exceptions import ServerNotResponsiveError -from rich.console import Console - -from infrahub_sync.utils import ( - find_missing_schema_model, - get_all_sync, - get_instance, - get_potenda_from_instance, - render_adapter, -) - -app = typer.Typer() -console = Console() - -logging.basicConfig(level=logging.WARNING) - - -def print_error_and_abort(message: str): - console.print(f"Error: {message}", style="bold red") - raise typer.Abort() - - -@app.command(name="list") -def list_projects( - directory: str = typer.Option(None, help="Base directory to search for sync configurations"), -): - """List all available SYNC projects.""" - for item in get_all_sync(directory=directory): - console.print(f"{item.name} | {item.source.name} >> {item.destination.name} | {item.directory}") - - -@app.command(name="diff") -def diff_cmd( - name: str = typer.Option(default=None, help="Name of the sync to use"), - config_file: str = typer.Option(default=None, help="File path to the sync configuration YAML file"), - directory: str = typer.Option(None, help="Base directory to search for sync configurations"), - branch: str = typer.Option(default=None, help="Branch to use for the diff."), - show_progress: bool = typer.Option(default=True, help="Show a progress bar during diff"), -): - """Calculate and print the differences between the source and the destination systems for a given project.""" - if sum([bool(name), bool(config_file)]) != 1: - print_error_and_abort("Please specify exactly one of 'name' or 'config-file'.") - - sync_instance = get_instance(name=name, config_file=config_file, directory=directory) - if not sync_instance: - print_error_and_abort("Failed to load sync instance.") - - try: - ptd = get_potenda_from_instance(sync_instance=sync_instance, branch=branch, show_progress=show_progress) - except ValueError as exc: - print_error_and_abort(f"Failed to initialize the Sync Instance: {exc}") - try: - ptd.source_load() - ptd.destination_load() - except ValueError as exc: - print_error_and_abort(exc) - - mydiff = ptd.diff() - - print(mydiff.str()) - - -@app.command(name="sync") -def sync_cmd( - name: str = typer.Option(default=None, help="Name of the sync to use"), - config_file: str = typer.Option(default=None, help="File path to the sync configuration YAML file"), - directory: str = typer.Option(None, help="Base directory to search for sync configurations"), - branch: str = typer.Option(default=None, help="Branch to use for the sync."), - diff: bool = typer.Option( - default=True, help="Print the differences between the source and the destination before syncing" - ), - show_progress: bool = typer.Option(default=True, help="Show a progress bar during syncing"), -): - """Synchronize the data between source and the destination systems for a given project or configuration file.""" - if sum([bool(name), bool(config_file)]) != 1: - print_error_and_abort("Please specify exactly one of 'name' or 'config-file'.") - - sync_instance = get_instance(name=name, config_file=config_file, directory=directory) - if not sync_instance: - print_error_and_abort("Failed to load sync instance.") - - try: - ptd = get_potenda_from_instance(sync_instance=sync_instance, branch=branch, show_progress=show_progress) - except ValueError as exc: - print_error_and_abort(f"Failed to initialize the Sync Instance: {exc}") - try: - ptd.source_load() - ptd.destination_load() - except ValueError as exc: - print_error_and_abort(exc) - - mydiff = ptd.diff() - - if mydiff.has_diffs(): - if diff: - print(mydiff.str()) - start_synctime = timer() - ptd.sync(diff=mydiff) - end_synctime = timer() - console.print(f"Sync: Completed in {end_synctime - start_synctime} sec") - else: - console.print("No difference found. Nothing to sync") - - -@app.command(name="generate") -def generate( - name: str = typer.Option(default=None, help="Name of the sync to use"), - config_file: str = typer.Option(default=None, help="File path to the sync configuration YAML file"), - directory: str = typer.Option(None, help="Base directory to search for sync configurations"), -): - """Generate all the python files for a given sync based on the configuration.""" - - if sum([bool(name), bool(config_file)]) != 1: - print_error_and_abort("Please specify exactly one of 'name' or 'config_file'.") - - sync_instance = get_instance(name=name, config_file=config_file, directory=directory) - if not sync_instance: - print_error_and_abort(f"Unable to find the sync {name}. Use the list command to see the sync available") - - # TODO - # - Do not use the env variable token here if token is present in settings - # - Do not use `main` if the branch is indicated in the file - infrahub_address = None - if sync_instance.destination.name == "infrahub": - if sync_instance.destination.settings and isinstance(sync_instance.source.settings, dict): - infrahub_address = sync_instance.destination.settings["url"] - elif sync_instance.source.name == "infrahub": - if sync_instance.source.settings and isinstance(sync_instance.source.settings, dict): - infrahub_address = sync_instance.source.settings["url"] - - client = InfrahubClientSync(address=infrahub_address) - - try: - schema = client.schema.all() - except ServerNotResponsiveError as exc: - print_error_and_abort(str(exc)) - - missing_schema_models = find_missing_schema_model(sync_instance=sync_instance, schema=schema) - if missing_schema_models: - print_error_and_abort(f"One or more model model are not present in the Schema - {missing_schema_models}") - - rendered_files = render_adapter(sync_instance=sync_instance, schema=schema) - for template, output_path in rendered_files: - console.print(f"Rendered template {template} to {output_path}") diff --git a/sync/infrahub-sync/infrahub_sync/generator/__init__.py b/sync/infrahub-sync/infrahub_sync/generator/__init__.py deleted file mode 100644 index e661a31d3e..0000000000 --- a/sync/infrahub-sync/infrahub_sync/generator/__init__.py +++ /dev/null @@ -1,173 +0,0 @@ -from pathlib import Path -from typing import Any, List, Optional, Union - -import jinja2 -from infrahub_sdk import ( - AttributeSchema, - NodeSchema, - RelationshipKind, - RelationshipSchema, -) - -from infrahub_sync import SyncConfig - -ATTRIBUTE_KIND_MAP = { - "Text": "str", - "String": "str", - "TextArea": "str", - "DateTime": "str", - "HashedPassword": "str", - "Number": "int", - "Integer": "int", - "Boolean": "bool", -} - - -def list_to_set(items: List[str]) -> str: - """Convert a list in a string representation of a Set.""" - if not items: - return "()" - - response = '"' + '", "'.join(items) + '"' - if len(items) == 1: - response += "," - - return "(" + response + ")" - - -def list_to_str(items: List[str]) -> str: - """Convert a list into a string separated with comma""" - return ", ".join(items) - - -def has_node(config: SyncConfig, name: str) -> bool: - for item in config.schema_mapping: - if item.name == name: - return True - return False - - -def has_field(config: SyncConfig, name: str, field: str) -> bool: - for item in config.schema_mapping: - if item.name == name: - for subitem in item.fields: - if subitem.name == field: - return True - return False - - -def get_identifiers(node: NodeSchema, config: SyncConfig) -> Optional[List[str]]: - """Return the identifiers that should be used by DiffSync.""" - - config_identifiers = [ - item.identifiers for item in config.schema_mapping if item.name == node.kind and item.identifiers - ] - - if config_identifiers: - return config_identifiers[0] - - identifiers = [ - attr.name for attr in node.attributes if attr.unique and has_field(config, name=node.kind, field=attr.name) - ] - - if not identifiers: - return None - - return identifiers - - -def get_attributes(node: NodeSchema, config: SyncConfig) -> Optional[List[str]]: - """Return the attributes that should be used by DiffSync.""" - attrs_attributes = [attr.name for attr in node.attributes if has_field(config, name=node.kind, field=attr.name)] - rels_identifiers = [ - rel.name - for rel in node.relationships - if rel.kind != RelationshipKind.COMPONENT and has_field(config, name=node.kind, field=rel.name) - ] - - identifiers = get_identifiers(node=node, config=config) - if not identifiers: - return None - - attributes = [item for item in rels_identifiers + attrs_attributes if item not in identifiers] - - if not attributes: - return None - - return attributes - - -def get_children(node: NodeSchema, config: SyncConfig) -> Optional[str]: - # rel.peer.lower() might now work in all cases we should have a better function to convert that - children = { - rel.peer.lower(): rel.name - for rel in node.relationships - if rel.cardinality == "many" - and rel.kind == RelationshipKind.COMPONENT - and has_field(config, name=node.kind, field=rel.name) - } - - if not children: - return None - - children_list = [f'"{key}": "{value}"' for key, value in children.items()] - return "{" + ", ".join(children_list) + "}" - - -def get_kind(item: Union[RelationshipSchema, AttributeSchema]) -> str: - kind = "str" - if isinstance(item, AttributeSchema): - kind = ATTRIBUTE_KIND_MAP.get(item.kind, "str") - if item.optional: - kind = f"Optional[{kind}]" - if item.default_value is not None: - # Format the default value based on its type - if isinstance(item.default_value, str): - kind += f' = "{item.default_value}"' - elif isinstance(item.default_value, (int, float, bool)): - kind += f" = {item.default_value}" - else: - kind += f" = {repr(item.default_value)}" - else: - kind += " = None" - - elif isinstance(item, RelationshipSchema) and item.cardinality == "one": - if item.optional: - kind = f"Optional[{kind}] = None" - - elif isinstance(item, RelationshipSchema) and item.cardinality == "many": - kind = "List[str]" - if item.optional: - kind = f"Optional[{kind}]" - kind += " = []" - - return kind - - -def has_children(node: NodeSchema, config: SyncConfig) -> bool: - if get_children(config=config, node=node): - return True - return False - - -def render_template(template_file: Path, output_dir: Path, output_file: Path, context: dict[str, Any]): - template_loader = jinja2.PackageLoader("infrahub_sync", "generator/templates") - template_env = jinja2.Environment( - loader=template_loader, - ) - # Add custom filters to Jinja2 - template_env.filters["get_identifiers"] = get_identifiers - template_env.filters["get_attributes"] = get_attributes - template_env.filters["get_children"] = get_children - template_env.filters["list_to_set"] = list_to_set - template_env.filters["list_to_str"] = list_to_str - template_env.filters["has_node"] = has_node - template_env.filters["has_field"] = has_field - template_env.filters["has_children"] = has_children - template_env.filters["get_kind"] = get_kind - - template = template_env.get_template(str(template_file)) - - rendered_tpl = template.render(**context) # type: ignore[arg-type] - output_filename = output_dir / output_file - output_filename.write_text(rendered_tpl, encoding="utf-8") diff --git a/sync/infrahub-sync/infrahub_sync/generator/templates/diffsync_adapter.j2 b/sync/infrahub-sync/infrahub_sync/generator/templates/diffsync_adapter.j2 deleted file mode 100644 index 2c9bbb7430..0000000000 --- a/sync/infrahub-sync/infrahub_sync/generator/templates/diffsync_adapter.j2 +++ /dev/null @@ -1,20 +0,0 @@ -from infrahub_sync.adapters.{{ adapter.name }} import {{ adapter.name.title() }}Adapter - -from .sync_models import ( -{% for nodekind, node in schema.items()|sort() %} -{%- if node | get_identifiers(config) %} {{ nodekind }}, -{% endif -%} -{% endfor -%} -) - - -# ------------------------------------------------------- -# AUTO-GENERATED FILE, DO NOT MODIFY -# This file has been generated with the command `infrahub-sync generate` -# All modifications will be lost the next time you reexecute this command -# ------------------------------------------------------- -class {{ adapter.name.title() }}Sync({{ adapter.name.title() }}Adapter): -{% for nodekind, node in schema.items() -%} -{%- if node | get_identifiers(config) %} {{ node.kind }} = {{ nodekind }} -{% endif -%} -{% endfor %} diff --git a/sync/infrahub-sync/infrahub_sync/generator/templates/diffsync_models.j2 b/sync/infrahub-sync/infrahub_sync/generator/templates/diffsync_models.j2 deleted file mode 100644 index a200a187b3..0000000000 --- a/sync/infrahub-sync/infrahub_sync/generator/templates/diffsync_models.j2 +++ /dev/null @@ -1,31 +0,0 @@ -from typing import Any, List, Optional - -from infrahub_sync.adapters.{{ adapter.name }} import {{ adapter.name.title() }}Model - -# ------------------------------------------------------- -# AUTO-GENERATED FILE, DO NOT MODIFY -# This file has been generated with the command `infrahub-sync generate` -# All modifications will be lost the next time you reexecute this command -# ------------------------------------------------------- -{%- for nodekind, node in schema.items() -%} -{%- if node | get_identifiers(config) and config | has_node(node.kind) %} -class {{ nodekind }}({{ adapter.name.title() }}Model): - _modelname = "{{ node.kind }}" - _identifiers = {{ node | get_identifiers(config) | list_to_set }} - _attributes = {{ node | get_attributes(config) | list_to_set }} - -{%- for attr in node.attributes -%} -{%- if config | has_field(node.kind, attr.name) %} - {{ attr.name }}: {{ attr | get_kind }} -{%- endif -%} -{%- endfor -%} -{%- for rel in node.relationships -%} -{%- if config | has_field(node.kind, rel.name) %} - {{ rel.name }}: {{ rel | get_kind }} -{%- endif -%} -{%- endfor %} - - local_id: Optional[str] = None - local_data: Optional[Any] = None -{% endif %} -{%- endfor %} diff --git a/sync/infrahub-sync/infrahub_sync/utils.py b/sync/infrahub-sync/infrahub_sync/utils.py deleted file mode 100644 index 05cc0ebecc..0000000000 --- a/sync/infrahub-sync/infrahub_sync/utils.py +++ /dev/null @@ -1,188 +0,0 @@ -import importlib -import sys -from pathlib import Path -from typing import List, MutableMapping, Optional, Tuple, Union - -import yaml -from infrahub_sdk.schema import GenericSchema, NodeSchema - -from diffsync.store.local import LocalStore -from diffsync.store.redis import RedisStore -from infrahub_sync import SyncAdapter, SyncConfig, SyncInstance -from infrahub_sync.generator import render_template -from potenda import Potenda - - -def find_missing_schema_model( - sync_instance: SyncInstance, schema: MutableMapping[str, Union[NodeSchema, GenericSchema]] -) -> List[str]: - missing_schema_models = [] - for item in sync_instance.schema_mapping: - match_found = any(item.name == node.kind for node in schema.values()) - - if not match_found: - missing_schema_models.append(item.name) - - return missing_schema_models - - -def render_adapter( - sync_instance: SyncInstance, schema: MutableMapping[str, Union[NodeSchema, GenericSchema]] -) -> List[Tuple[str, str]]: - files_to_render = ( - ("diffsync_models.j2", "sync_models.py"), - ("diffsync_adapter.j2", "sync_adapter.py"), - ) - rendered_files = [] - for adapter in [sync_instance.source, sync_instance.destination]: - output_dir_path = Path(sync_instance.directory, adapter.name) - if not output_dir_path.is_dir(): - output_dir_path.mkdir(exist_ok=True) - - init_file_path = output_dir_path / "__init__.py" - if not init_file_path.exists(): - init_file_path.touch() - - for item in files_to_render: - render_template( - template_file=item[0], - output_dir=output_dir_path, - output_file=item[1], - context={"schema": schema, "adapter": adapter, "config": sync_instance}, - ) - output_file_path = output_dir_path / item[1] - rendered_files.append((item[0], output_file_path)) - - return rendered_files - - -def import_adapter(sync_instance: SyncInstance, adapter: SyncAdapter): - directory = Path(sync_instance.directory) - sys.path.insert(0, str(directory)) - adapter_file_path = directory / f"{adapter.name}" / "sync_adapter.py" - - try: - adapter_name = f"{adapter.name.title()}Sync" - spec = importlib.util.spec_from_file_location(f"{adapter.name}.adapter", str(adapter_file_path)) - adapter_module = importlib.util.module_from_spec(spec) - sys.modules[f"{adapter.name}.adapter"] = adapter_module - spec.loader.exec_module(adapter_module) - - adapter_class = getattr(adapter_module, adapter_name, None) - if adapter_class is None: - raise AttributeError(f"{adapter_name} not found in adapter.py") - except (FileNotFoundError, AttributeError) as exc: - raise ImportError(f"{adapter_name}: {str(exc)}") from exc - return adapter_class - - -def get_all_sync(directory: Optional[str] = None) -> List[SyncInstance]: - results = [] - search_directory = Path(directory) if directory else Path(__file__).parent - config_files = search_directory.glob("**/config.yml") - - for config_file in config_files: - with config_file.open("r") as file: - directory_name = str(config_file.parent) - config_data = yaml.safe_load(file) - SyncConfig(**config_data) - results.append(SyncInstance(**config_data, directory=directory_name)) - - return results - - -def get_instance( - name: Optional[str] = None, config_file: Optional[str] = "config.yml", directory: Optional[str] = None -) -> Optional[SyncInstance]: - if name: - all_sync_instances = get_all_sync(directory=directory) - for item in all_sync_instances: - if item.name == name: - return item - return None - - config_file_path = None - try: - if Path(config_file).is_absolute() or directory is None: - config_file_path = Path(config_file) - elif directory: - config_file_path = Path(directory, config_file) - except TypeError: - # TODO Log or raise an Error/Warning - return None - - if config_file_path: - directory_path = config_file_path.parent - if config_file_path.is_file(): - with config_file_path.open("r", encoding="UTF-8") as file: - config_data = yaml.safe_load(file) - return SyncInstance(**config_data, directory=str(directory_path)) - - return None - - -def get_potenda_from_instance( - sync_instance: SyncInstance, branch: Optional[str] = None, show_progress: Optional[bool] = True -) -> Potenda: - source = import_adapter(sync_instance=sync_instance, adapter=sync_instance.source) - destination = import_adapter(sync_instance=sync_instance, adapter=sync_instance.destination) - - source_store = LocalStore() - destination_store = LocalStore() - - if sync_instance.store: - if sync_instance.store.type == "redis": - if sync_instance.store.settings and isinstance(sync_instance.store.settings, dict): - redis_settings = sync_instance.store.settings - source_store = RedisStore(**redis_settings, name=sync_instance.source.name) - destination_store = RedisStore(**redis_settings, name=sync_instance.destination.name) - else: - source_store = RedisStore(name=sync_instance.source.name) - destination_store = RedisStore(name=sync_instance.destination.name) - - try: - if sync_instance.source.name == "infrahub" and branch: - src = source( - config=sync_instance, - target="source", - adapter=sync_instance.source, - branch=branch, - internal_storage_engine=source_store, - ) - else: - src = source( - config=sync_instance, - target="source", - adapter=sync_instance.source, - internal_storage_engine=source_store, - ) - except ValueError as exc: - raise ValueError(f"{sync_instance.source.name.title()}Adapter - {exc}") from exc - try: - if sync_instance.destination.name == "infrahub" and branch: - dst = destination( - config=sync_instance, - target="destination", - adapter=sync_instance.destination, - branch=branch, - internal_storage_engine=destination_store, - ) - else: - dst = destination( - config=sync_instance, - target="destination", - adapter=sync_instance.destination, - internal_storage_engine=destination_store, - ) - except ValueError as exc: - raise ValueError(f"{sync_instance.destination.name.title()}Adapter - {exc}") from exc - - ptd = Potenda( - destination=dst, - source=src, - config=sync_instance, - top_level=sync_instance.order, - show_progress=show_progress, - ) - - return ptd diff --git a/sync/infrahub-sync/tests/__init__.py b/sync/infrahub-sync/tests/__init__.py deleted file mode 100644 index 9c48bcf96d..0000000000 --- a/sync/infrahub-sync/tests/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -import builtins - -from rich import print as rprint - -builtins.rprint = rprint # type: ignore diff --git a/sync/poetry.lock b/sync/poetry.lock deleted file mode 100644 index af9b623798..0000000000 --- a/sync/poetry.lock +++ /dev/null @@ -1,2085 +0,0 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. - -[[package]] -name = "annotated-types" -version = "0.7.0" -description = "Reusable constraint types to use with typing.Annotated" -optional = false -python-versions = ">=3.8" -files = [ - {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, - {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, -] - -[[package]] -name = "anyio" -version = "4.4.0" -description = "High level compatibility layer for multiple asynchronous event loop implementations" -optional = false -python-versions = ">=3.8" -files = [ - {file = "anyio-4.4.0-py3-none-any.whl", hash = "sha256:c1b2d8f46a8a812513012e1107cb0e68c17159a7a594208005a57dc776e1bdc7"}, - {file = "anyio-4.4.0.tar.gz", hash = "sha256:5aadc6a1bbb7cdb0bede386cac5e2940f5e2ff3aa20277e991cf028e0585ce94"}, -] - -[package.dependencies] -exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} -idna = ">=2.8" -sniffio = ">=1.1" -typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""} - -[package.extras] -doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] -test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] -trio = ["trio (>=0.23)"] - -[[package]] -name = "astroid" -version = "3.2.2" -description = "An abstract syntax tree for Python with inference support." -optional = false -python-versions = ">=3.8.0" -files = [ - {file = "astroid-3.2.2-py3-none-any.whl", hash = "sha256:e8a0083b4bb28fcffb6207a3bfc9e5d0a68be951dd7e336d5dcf639c682388c0"}, - {file = "astroid-3.2.2.tar.gz", hash = "sha256:8ead48e31b92b2e217b6c9733a21afafe479d52d6e164dd25fb1a770c7c3cf94"}, -] - -[package.dependencies] -typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.11\""} - -[[package]] -name = "asttokens" -version = "2.4.1" -description = "Annotate AST trees with source code positions" -optional = false -python-versions = "*" -files = [ - {file = "asttokens-2.4.1-py2.py3-none-any.whl", hash = "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24"}, - {file = "asttokens-2.4.1.tar.gz", hash = "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0"}, -] - -[package.dependencies] -six = ">=1.12.0" - -[package.extras] -astroid = ["astroid (>=1,<2)", "astroid (>=2,<4)"] -test = ["astroid (>=1,<2)", "astroid (>=2,<4)", "pytest"] - -[[package]] -name = "async-timeout" -version = "4.0.3" -description = "Timeout context manager for asyncio programs" -optional = false -python-versions = ">=3.7" -files = [ - {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}, - {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, -] - -[[package]] -name = "certifi" -version = "2024.7.4" -description = "Python package for providing Mozilla's CA Bundle." -optional = false -python-versions = ">=3.6" -files = [ - {file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"}, - {file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"}, -] - -[[package]] -name = "cfgv" -version = "3.4.0" -description = "Validate configuration and produce human readable error messages." -optional = false -python-versions = ">=3.8" -files = [ - {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, - {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, -] - -[[package]] -name = "charset-normalizer" -version = "3.3.2" -description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -optional = false -python-versions = ">=3.7.0" -files = [ - {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, - {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, -] - -[[package]] -name = "click" -version = "8.1.7" -description = "Composable command line interface toolkit" -optional = false -python-versions = ">=3.7" -files = [ - {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, - {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] - -[[package]] -name = "coverage" -version = "7.5.3" -description = "Code coverage measurement for Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "coverage-7.5.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a6519d917abb15e12380406d721e37613e2a67d166f9fb7e5a8ce0375744cd45"}, - {file = "coverage-7.5.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aea7da970f1feccf48be7335f8b2ca64baf9b589d79e05b9397a06696ce1a1ec"}, - {file = "coverage-7.5.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:923b7b1c717bd0f0f92d862d1ff51d9b2b55dbbd133e05680204465f454bb286"}, - {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62bda40da1e68898186f274f832ef3e759ce929da9a9fd9fcf265956de269dbc"}, - {file = "coverage-7.5.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8b7339180d00de83e930358223c617cc343dd08e1aa5ec7b06c3a121aec4e1d"}, - {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:25a5caf742c6195e08002d3b6c2dd6947e50efc5fc2c2205f61ecb47592d2d83"}, - {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:05ac5f60faa0c704c0f7e6a5cbfd6f02101ed05e0aee4d2822637a9e672c998d"}, - {file = "coverage-7.5.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:239a4e75e09c2b12ea478d28815acf83334d32e722e7433471fbf641c606344c"}, - {file = "coverage-7.5.3-cp310-cp310-win32.whl", hash = "sha256:a5812840d1d00eafae6585aba38021f90a705a25b8216ec7f66aebe5b619fb84"}, - {file = "coverage-7.5.3-cp310-cp310-win_amd64.whl", hash = "sha256:33ca90a0eb29225f195e30684ba4a6db05dbef03c2ccd50b9077714c48153cac"}, - {file = "coverage-7.5.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f81bc26d609bf0fbc622c7122ba6307993c83c795d2d6f6f6fd8c000a770d974"}, - {file = "coverage-7.5.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7cec2af81f9e7569280822be68bd57e51b86d42e59ea30d10ebdbb22d2cb7232"}, - {file = "coverage-7.5.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55f689f846661e3f26efa535071775d0483388a1ccfab899df72924805e9e7cd"}, - {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50084d3516aa263791198913a17354bd1dc627d3c1639209640b9cac3fef5807"}, - {file = "coverage-7.5.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:341dd8f61c26337c37988345ca5c8ccabeff33093a26953a1ac72e7d0103c4fb"}, - {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ab0b028165eea880af12f66086694768f2c3139b2c31ad5e032c8edbafca6ffc"}, - {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:5bc5a8c87714b0c67cfeb4c7caa82b2d71e8864d1a46aa990b5588fa953673b8"}, - {file = "coverage-7.5.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:38a3b98dae8a7c9057bd91fbf3415c05e700a5114c5f1b5b0ea5f8f429ba6614"}, - {file = "coverage-7.5.3-cp311-cp311-win32.whl", hash = "sha256:fcf7d1d6f5da887ca04302db8e0e0cf56ce9a5e05f202720e49b3e8157ddb9a9"}, - {file = "coverage-7.5.3-cp311-cp311-win_amd64.whl", hash = "sha256:8c836309931839cca658a78a888dab9676b5c988d0dd34ca247f5f3e679f4e7a"}, - {file = "coverage-7.5.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:296a7d9bbc598e8744c00f7a6cecf1da9b30ae9ad51c566291ff1314e6cbbed8"}, - {file = "coverage-7.5.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:34d6d21d8795a97b14d503dcaf74226ae51eb1f2bd41015d3ef332a24d0a17b3"}, - {file = "coverage-7.5.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e317953bb4c074c06c798a11dbdd2cf9979dbcaa8ccc0fa4701d80042d4ebf1"}, - {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:705f3d7c2b098c40f5b81790a5fedb274113373d4d1a69e65f8b68b0cc26f6db"}, - {file = "coverage-7.5.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1196e13c45e327d6cd0b6e471530a1882f1017eb83c6229fc613cd1a11b53cd"}, - {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:015eddc5ccd5364dcb902eaecf9515636806fa1e0d5bef5769d06d0f31b54523"}, - {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:fd27d8b49e574e50caa65196d908f80e4dff64d7e592d0c59788b45aad7e8b35"}, - {file = "coverage-7.5.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:33fc65740267222fc02975c061eb7167185fef4cc8f2770267ee8bf7d6a42f84"}, - {file = "coverage-7.5.3-cp312-cp312-win32.whl", hash = "sha256:7b2a19e13dfb5c8e145c7a6ea959485ee8e2204699903c88c7d25283584bfc08"}, - {file = "coverage-7.5.3-cp312-cp312-win_amd64.whl", hash = "sha256:0bbddc54bbacfc09b3edaec644d4ac90c08ee8ed4844b0f86227dcda2d428fcb"}, - {file = "coverage-7.5.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f78300789a708ac1f17e134593f577407d52d0417305435b134805c4fb135adb"}, - {file = "coverage-7.5.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b368e1aee1b9b75757942d44d7598dcd22a9dbb126affcbba82d15917f0cc155"}, - {file = "coverage-7.5.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f836c174c3a7f639bded48ec913f348c4761cbf49de4a20a956d3431a7c9cb24"}, - {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:244f509f126dc71369393ce5fea17c0592c40ee44e607b6d855e9c4ac57aac98"}, - {file = "coverage-7.5.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4c2872b3c91f9baa836147ca33650dc5c172e9273c808c3c3199c75490e709d"}, - {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dd4b3355b01273a56b20c219e74e7549e14370b31a4ffe42706a8cda91f19f6d"}, - {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:f542287b1489c7a860d43a7d8883e27ca62ab84ca53c965d11dac1d3a1fab7ce"}, - {file = "coverage-7.5.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:75e3f4e86804023e991096b29e147e635f5e2568f77883a1e6eed74512659ab0"}, - {file = "coverage-7.5.3-cp38-cp38-win32.whl", hash = "sha256:c59d2ad092dc0551d9f79d9d44d005c945ba95832a6798f98f9216ede3d5f485"}, - {file = "coverage-7.5.3-cp38-cp38-win_amd64.whl", hash = "sha256:fa21a04112c59ad54f69d80e376f7f9d0f5f9123ab87ecd18fbb9ec3a2beed56"}, - {file = "coverage-7.5.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f5102a92855d518b0996eb197772f5ac2a527c0ec617124ad5242a3af5e25f85"}, - {file = "coverage-7.5.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d1da0a2e3b37b745a2b2a678a4c796462cf753aebf94edcc87dcc6b8641eae31"}, - {file = "coverage-7.5.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8383a6c8cefba1b7cecc0149415046b6fc38836295bc4c84e820872eb5478b3d"}, - {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9aad68c3f2566dfae84bf46295a79e79d904e1c21ccfc66de88cd446f8686341"}, - {file = "coverage-7.5.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e079c9ec772fedbade9d7ebc36202a1d9ef7291bc9b3a024ca395c4d52853d7"}, - {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bde997cac85fcac227b27d4fb2c7608a2c5f6558469b0eb704c5726ae49e1c52"}, - {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:990fb20b32990b2ce2c5f974c3e738c9358b2735bc05075d50a6f36721b8f303"}, - {file = "coverage-7.5.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3d5a67f0da401e105753d474369ab034c7bae51a4c31c77d94030d59e41df5bd"}, - {file = "coverage-7.5.3-cp39-cp39-win32.whl", hash = "sha256:e08c470c2eb01977d221fd87495b44867a56d4d594f43739a8028f8646a51e0d"}, - {file = "coverage-7.5.3-cp39-cp39-win_amd64.whl", hash = "sha256:1d2a830ade66d3563bb61d1e3c77c8def97b30ed91e166c67d0632c018f380f0"}, - {file = "coverage-7.5.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:3538d8fb1ee9bdd2e2692b3b18c22bb1c19ffbefd06880f5ac496e42d7bb3884"}, - {file = "coverage-7.5.3.tar.gz", hash = "sha256:04aefca5190d1dc7a53a4c1a5a7f8568811306d7a8ee231c42fb69215571944f"}, -] - -[package.dependencies] -tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} - -[package.extras] -toml = ["tomli"] - -[[package]] -name = "decorator" -version = "5.1.1" -description = "Decorators for Humans" -optional = false -python-versions = ">=3.5" -files = [ - {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"}, - {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, -] - -[[package]] -name = "diffsync" -version = "2.0.0" -description = "Library to easily sync/diff/update 2 different data sources" -optional = false -python-versions = ">=3.8,<4.0" -files = [ - {file = "diffsync-2.0.0-py3-none-any.whl", hash = "sha256:59f864a115abc5b0aa3b9db0d44deff59c81cd5469e5894326c27e29511e3aab"}, - {file = "diffsync-2.0.0.tar.gz", hash = "sha256:712bc85a24f49ef6075344dc3a16c85e27b1416154c46fd5de7acf72e8321a9b"}, -] - -[package.dependencies] -colorama = ">=0.4.3,<0.5.0" -packaging = ">=21.3,<24.0" -pydantic = ">=2.0.0,<3.0.0" -redis = {version = ">=4.3,<5.0", optional = true, markers = "extra == \"redis\""} -structlog = ">=20.1.0,<23.0.0" -typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} - -[package.extras] -redis = ["redis (>=4.3,<5.0)"] - -[[package]] -name = "dill" -version = "0.3.8" -description = "serialize all of Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, - {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, -] - -[package.extras] -graph = ["objgraph (>=1.7.2)"] -profile = ["gprof2dot (>=2022.7.29)"] - -[[package]] -name = "distlib" -version = "0.3.8" -description = "Distribution utilities" -optional = false -python-versions = "*" -files = [ - {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"}, - {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, -] - -[[package]] -name = "exceptiongroup" -version = "1.2.1" -description = "Backport of PEP 654 (exception groups)" -optional = false -python-versions = ">=3.7" -files = [ - {file = "exceptiongroup-1.2.1-py3-none-any.whl", hash = "sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad"}, - {file = "exceptiongroup-1.2.1.tar.gz", hash = "sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16"}, -] - -[package.extras] -test = ["pytest (>=6)"] - -[[package]] -name = "execnet" -version = "2.1.1" -description = "execnet: rapid multi-Python deployment" -optional = false -python-versions = ">=3.8" -files = [ - {file = "execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc"}, - {file = "execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3"}, -] - -[package.extras] -testing = ["hatch", "pre-commit", "pytest", "tox"] - -[[package]] -name = "executing" -version = "2.0.1" -description = "Get the currently executing AST node of a frame, and other information" -optional = false -python-versions = ">=3.5" -files = [ - {file = "executing-2.0.1-py2.py3-none-any.whl", hash = "sha256:eac49ca94516ccc753f9fb5ce82603156e590b27525a8bc32cce8ae302eb61bc"}, - {file = "executing-2.0.1.tar.gz", hash = "sha256:35afe2ce3affba8ee97f2d69927fa823b08b472b7b994e36a52a964b93d16147"}, -] - -[package.extras] -tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"] - -[[package]] -name = "filelock" -version = "3.15.1" -description = "A platform independent file lock." -optional = false -python-versions = ">=3.8" -files = [ - {file = "filelock-3.15.1-py3-none-any.whl", hash = "sha256:71b3102950e91dfc1bb4209b64be4dc8854f40e5f534428d8684f953ac847fac"}, - {file = "filelock-3.15.1.tar.gz", hash = "sha256:58a2549afdf9e02e10720eaa4d4470f56386d7a6f72edd7d0596337af8ed7ad8"}, -] - -[package.extras] -docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-asyncio (>=0.21)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] -typing = ["typing-extensions (>=4.8)"] - -[[package]] -name = "gitdb" -version = "4.0.11" -description = "Git Object Database" -optional = false -python-versions = ">=3.7" -files = [ - {file = "gitdb-4.0.11-py3-none-any.whl", hash = "sha256:81a3407ddd2ee8df444cbacea00e2d038e40150acfa3001696fe0dcf1d3adfa4"}, - {file = "gitdb-4.0.11.tar.gz", hash = "sha256:bf5421126136d6d0af55bc1e7c1af1c397a34f5b7bd79e776cd3e89785c2b04b"}, -] - -[package.dependencies] -smmap = ">=3.0.1,<6" - -[[package]] -name = "gitpython" -version = "3.1.43" -description = "GitPython is a Python library used to interact with Git repositories" -optional = false -python-versions = ">=3.7" -files = [ - {file = "GitPython-3.1.43-py3-none-any.whl", hash = "sha256:eec7ec56b92aad751f9912a73404bc02ba212a23adb2c7098ee668417051a1ff"}, - {file = "GitPython-3.1.43.tar.gz", hash = "sha256:35f314a9f878467f5453cc1fee295c3e18e52f1b99f10f6cf5b1682e968a9e7c"}, -] - -[package.dependencies] -gitdb = ">=4.0.1,<5" - -[package.extras] -doc = ["sphinx (==4.3.2)", "sphinx-autodoc-typehints", "sphinx-rtd-theme", "sphinxcontrib-applehelp (>=1.0.2,<=1.0.4)", "sphinxcontrib-devhelp (==1.0.2)", "sphinxcontrib-htmlhelp (>=2.0.0,<=2.0.1)", "sphinxcontrib-qthelp (==1.0.3)", "sphinxcontrib-serializinghtml (==1.1.5)"] -test = ["coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock", "mypy", "pre-commit", "pytest (>=7.3.1)", "pytest-cov", "pytest-instafail", "pytest-mock", "pytest-sugar", "typing-extensions"] - -[[package]] -name = "graphql-core" -version = "3.2.3" -description = "GraphQL implementation for Python, a port of GraphQL.js, the JavaScript reference implementation for GraphQL." -optional = false -python-versions = ">=3.6,<4" -files = [ - {file = "graphql-core-3.2.3.tar.gz", hash = "sha256:06d2aad0ac723e35b1cb47885d3e5c45e956a53bc1b209a9fc5369007fe46676"}, - {file = "graphql_core-3.2.3-py3-none-any.whl", hash = "sha256:5766780452bd5ec8ba133f8bf287dc92713e3868ddd83aee4faab9fc3e303dc3"}, -] - -[[package]] -name = "h11" -version = "0.14.0" -description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" -optional = false -python-versions = ">=3.7" -files = [ - {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, - {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, -] - -[[package]] -name = "httpcore" -version = "1.0.5" -description = "A minimal low-level HTTP client." -optional = false -python-versions = ">=3.8" -files = [ - {file = "httpcore-1.0.5-py3-none-any.whl", hash = "sha256:421f18bac248b25d310f3cacd198d55b8e6125c107797b609ff9b7a6ba7991b5"}, - {file = "httpcore-1.0.5.tar.gz", hash = "sha256:34a38e2f9291467ee3b44e89dd52615370e152954ba21721378a87b2960f7a61"}, -] - -[package.dependencies] -certifi = "*" -h11 = ">=0.13,<0.15" - -[package.extras] -asyncio = ["anyio (>=4.0,<5.0)"] -http2 = ["h2 (>=3,<5)"] -socks = ["socksio (==1.*)"] -trio = ["trio (>=0.22.0,<0.26.0)"] - -[[package]] -name = "httpx" -version = "0.27.0" -description = "The next generation HTTP client." -optional = false -python-versions = ">=3.8" -files = [ - {file = "httpx-0.27.0-py3-none-any.whl", hash = "sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5"}, - {file = "httpx-0.27.0.tar.gz", hash = "sha256:a0cb88a46f32dc874e04ee956e4c2764aba2aa228f650b06788ba6bda2962ab5"}, -] - -[package.dependencies] -anyio = "*" -certifi = "*" -httpcore = "==1.*" -idna = "*" -sniffio = "*" - -[package.extras] -brotli = ["brotli", "brotlicffi"] -cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] -http2 = ["h2 (>=3,<5)"] -socks = ["socksio (==1.*)"] - -[[package]] -name = "identify" -version = "2.5.36" -description = "File identification library for Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "identify-2.5.36-py2.py3-none-any.whl", hash = "sha256:37d93f380f4de590500d9dba7db359d0d3da95ffe7f9de1753faa159e71e7dfa"}, - {file = "identify-2.5.36.tar.gz", hash = "sha256:e5e00f54165f9047fbebeb4a560f9acfb8af4c88232be60a488e9b68d122745d"}, -] - -[package.extras] -license = ["ukkonen"] - -[[package]] -name = "idna" -version = "3.7" -description = "Internationalized Domain Names in Applications (IDNA)" -optional = false -python-versions = ">=3.5" -files = [ - {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, - {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, -] - -[[package]] -name = "infrahub-sdk" -version = "0.12.2" -description = "Python Client to interact with Infrahub" -optional = false -python-versions = "<4.0,>=3.9" -files = [ - {file = "infrahub_sdk-0.12.2-py3-none-any.whl", hash = "sha256:d5469f96d0ed6260266f74cbe0ae2dc62b28d37635862f0a0825d90be8836afe"}, - {file = "infrahub_sdk-0.12.2.tar.gz", hash = "sha256:627cb26ec619824ccc44aded3853c8514245e81dc62ae336ea219d8371e1064a"}, -] - -[package.dependencies] -gitpython = ">=3,<4" -graphql-core = ">=3.1,<3.3" -httpx = [ - {version = ">=0.20", markers = "python_version >= \"3.9\" and python_version < \"3.11\""}, - {version = ">=0.23", markers = "python_version >= \"3.11\""}, -] -Jinja2 = {version = ">=3,<4", optional = true, markers = "extra == \"ctl\" or extra == \"tests\" or extra == \"all\""} -numpy = [ - {version = ">=1.24.2,<2.0.0", optional = true, markers = "python_version >= \"3.9\" and python_version < \"3.12\" and extra == \"ctl\" or python_version >= \"3.9\" and python_version < \"3.12\" and extra == \"all\""}, - {version = ">=1.26.2,<2.0.0", optional = true, markers = "python_version >= \"3.12\" and extra == \"ctl\" or python_version >= \"3.12\" and extra == \"all\""}, -] -pendulum = [ - {version = ">=2", markers = "python_version >= \"3.9\" and python_version < \"3.12\""}, - {version = ">=3", markers = "python_version >= \"3.12\""}, -] -pyarrow = {version = ">=14,<15", optional = true, markers = "extra == \"ctl\" or extra == \"all\""} -pydantic = ">=2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" -pydantic-settings = ">=2.0" -pytest = {version = "*", optional = true, markers = "extra == \"tests\" or extra == \"all\""} -pyyaml = {version = ">=6,<7", optional = true, markers = "extra == \"ctl\" or extra == \"tests\" or extra == \"all\""} -rich = {version = ">=13,<14", optional = true, markers = "extra == \"ctl\" or extra == \"tests\" or extra == \"all\""} -toml = {version = ">=0.10,<0.11", optional = true, markers = "extra == \"ctl\" or extra == \"all\""} -typer = {version = ">=0,<1", optional = true, markers = "extra == \"ctl\" or extra == \"all\""} -ujson = ">=5,<6" - -[package.extras] -all = ["Jinja2 (>=3,<4)", "numpy (>=1.24.2,<2.0.0)", "numpy (>=1.26.2,<2.0.0)", "pyarrow (>=14,<15)", "pytest", "pyyaml (>=6,<7)", "rich (>=13,<14)", "toml (>=0.10,<0.11)", "typer (>=0,<1)"] -ctl = ["Jinja2 (>=3,<4)", "numpy (>=1.24.2,<2.0.0)", "numpy (>=1.26.2,<2.0.0)", "pyarrow (>=14,<15)", "pyyaml (>=6,<7)", "rich (>=13,<14)", "toml (>=0.10,<0.11)", "typer (>=0,<1)"] -tests = ["Jinja2 (>=3,<4)", "pytest", "pyyaml (>=6,<7)", "rich (>=13,<14)"] - -[[package]] -name = "iniconfig" -version = "2.0.0" -description = "brain-dead simple config-ini parsing" -optional = false -python-versions = ">=3.7" -files = [ - {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, - {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, -] - -[[package]] -name = "ipython" -version = "8.18.1" -description = "IPython: Productive Interactive Computing" -optional = false -python-versions = ">=3.9" -files = [ - {file = "ipython-8.18.1-py3-none-any.whl", hash = "sha256:e8267419d72d81955ec1177f8a29aaa90ac80ad647499201119e2f05e99aa397"}, - {file = "ipython-8.18.1.tar.gz", hash = "sha256:ca6f079bb33457c66e233e4580ebfc4128855b4cf6370dddd73842a9563e8a27"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "sys_platform == \"win32\""} -decorator = "*" -exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} -jedi = ">=0.16" -matplotlib-inline = "*" -pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""} -prompt-toolkit = ">=3.0.41,<3.1.0" -pygments = ">=2.4.0" -stack-data = "*" -traitlets = ">=5" -typing-extensions = {version = "*", markers = "python_version < \"3.10\""} - -[package.extras] -all = ["black", "curio", "docrepr", "exceptiongroup", "ipykernel", "ipyparallel", "ipywidgets", "matplotlib", "matplotlib (!=3.2.0)", "nbconvert", "nbformat", "notebook", "numpy (>=1.22)", "pandas", "pickleshare", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio (<0.22)", "qtconsole", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "trio", "typing-extensions"] -black = ["black"] -doc = ["docrepr", "exceptiongroup", "ipykernel", "matplotlib", "pickleshare", "pytest (<7)", "pytest (<7.1)", "pytest-asyncio (<0.22)", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "stack-data", "testpath", "typing-extensions"] -kernel = ["ipykernel"] -nbconvert = ["nbconvert"] -nbformat = ["nbformat"] -notebook = ["ipywidgets", "notebook"] -parallel = ["ipyparallel"] -qtconsole = ["qtconsole"] -test = ["pickleshare", "pytest (<7.1)", "pytest-asyncio (<0.22)", "testpath"] -test-extra = ["curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.22)", "pandas", "pickleshare", "pytest (<7.1)", "pytest-asyncio (<0.22)", "testpath", "trio"] - -[[package]] -name = "isort" -version = "5.13.2" -description = "A Python utility / library to sort Python imports." -optional = false -python-versions = ">=3.8.0" -files = [ - {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, - {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, -] - -[package.extras] -colors = ["colorama (>=0.4.6)"] - -[[package]] -name = "jedi" -version = "0.19.1" -description = "An autocompletion tool for Python that can be used for text editors." -optional = false -python-versions = ">=3.6" -files = [ - {file = "jedi-0.19.1-py2.py3-none-any.whl", hash = "sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0"}, - {file = "jedi-0.19.1.tar.gz", hash = "sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd"}, -] - -[package.dependencies] -parso = ">=0.8.3,<0.9.0" - -[package.extras] -docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"] -qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] -testing = ["Django", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] - -[[package]] -name = "jinja2" -version = "3.1.4" -description = "A very fast and expressive template engine." -optional = false -python-versions = ">=3.7" -files = [ - {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, - {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, -] - -[package.dependencies] -MarkupSafe = ">=2.0" - -[package.extras] -i18n = ["Babel (>=2.7)"] - -[[package]] -name = "markdown-it-py" -version = "3.0.0" -description = "Python port of markdown-it. Markdown parsing, done right!" -optional = false -python-versions = ">=3.8" -files = [ - {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, - {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, -] - -[package.dependencies] -mdurl = ">=0.1,<1.0" - -[package.extras] -benchmarking = ["psutil", "pytest", "pytest-benchmark"] -code-style = ["pre-commit (>=3.0,<4.0)"] -compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] -linkify = ["linkify-it-py (>=1,<3)"] -plugins = ["mdit-py-plugins"] -profiling = ["gprof2dot"] -rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] -testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] - -[[package]] -name = "markupsafe" -version = "2.1.5" -description = "Safely add untrusted strings to HTML/XML markup." -optional = false -python-versions = ">=3.7" -files = [ - {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, - {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, -] - -[[package]] -name = "matplotlib-inline" -version = "0.1.7" -description = "Inline Matplotlib backend for Jupyter" -optional = false -python-versions = ">=3.8" -files = [ - {file = "matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca"}, - {file = "matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90"}, -] - -[package.dependencies] -traitlets = "*" - -[[package]] -name = "mccabe" -version = "0.7.0" -description = "McCabe checker, plugin for flake8" -optional = false -python-versions = ">=3.6" -files = [ - {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, - {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, -] - -[[package]] -name = "mdurl" -version = "0.1.2" -description = "Markdown URL utilities" -optional = false -python-versions = ">=3.7" -files = [ - {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, - {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, -] - -[[package]] -name = "mypy" -version = "1.10.0" -description = "Optional static typing for Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "mypy-1.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:da1cbf08fb3b851ab3b9523a884c232774008267b1f83371ace57f412fe308c2"}, - {file = "mypy-1.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:12b6bfc1b1a66095ab413160a6e520e1dc076a28f3e22f7fb25ba3b000b4ef99"}, - {file = "mypy-1.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e36fb078cce9904c7989b9693e41cb9711e0600139ce3970c6ef814b6ebc2b2"}, - {file = "mypy-1.10.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2b0695d605ddcd3eb2f736cd8b4e388288c21e7de85001e9f85df9187f2b50f9"}, - {file = "mypy-1.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:cd777b780312ddb135bceb9bc8722a73ec95e042f911cc279e2ec3c667076051"}, - {file = "mypy-1.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3be66771aa5c97602f382230165b856c231d1277c511c9a8dd058be4784472e1"}, - {file = "mypy-1.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8b2cbaca148d0754a54d44121b5825ae71868c7592a53b7292eeb0f3fdae95ee"}, - {file = "mypy-1.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ec404a7cbe9fc0e92cb0e67f55ce0c025014e26d33e54d9e506a0f2d07fe5de"}, - {file = "mypy-1.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e22e1527dc3d4aa94311d246b59e47f6455b8729f4968765ac1eacf9a4760bc7"}, - {file = "mypy-1.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:a87dbfa85971e8d59c9cc1fcf534efe664d8949e4c0b6b44e8ca548e746a8d53"}, - {file = "mypy-1.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a781f6ad4bab20eef8b65174a57e5203f4be627b46291f4589879bf4e257b97b"}, - {file = "mypy-1.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b808e12113505b97d9023b0b5e0c0705a90571c6feefc6f215c1df9381256e30"}, - {file = "mypy-1.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f55583b12156c399dce2df7d16f8a5095291354f1e839c252ec6c0611e86e2e"}, - {file = "mypy-1.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4cf18f9d0efa1b16478c4c129eabec36148032575391095f73cae2e722fcf9d5"}, - {file = "mypy-1.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:bc6ac273b23c6b82da3bb25f4136c4fd42665f17f2cd850771cb600bdd2ebeda"}, - {file = "mypy-1.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9fd50226364cd2737351c79807775136b0abe084433b55b2e29181a4c3c878c0"}, - {file = "mypy-1.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f90cff89eea89273727d8783fef5d4a934be2fdca11b47def50cf5d311aff727"}, - {file = "mypy-1.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fcfc70599efde5c67862a07a1aaf50e55bce629ace26bb19dc17cece5dd31ca4"}, - {file = "mypy-1.10.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:075cbf81f3e134eadaf247de187bd604748171d6b79736fa9b6c9685b4083061"}, - {file = "mypy-1.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:3f298531bca95ff615b6e9f2fc0333aae27fa48052903a0ac90215021cdcfa4f"}, - {file = "mypy-1.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fa7ef5244615a2523b56c034becde4e9e3f9b034854c93639adb667ec9ec2976"}, - {file = "mypy-1.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3236a4c8f535a0631f85f5fcdffba71c7feeef76a6002fcba7c1a8e57c8be1ec"}, - {file = "mypy-1.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a2b5cdbb5dd35aa08ea9114436e0d79aceb2f38e32c21684dcf8e24e1e92821"}, - {file = "mypy-1.10.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:92f93b21c0fe73dc00abf91022234c79d793318b8a96faac147cd579c1671746"}, - {file = "mypy-1.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:28d0e038361b45f099cc086d9dd99c15ff14d0188f44ac883010e172ce86c38a"}, - {file = "mypy-1.10.0-py3-none-any.whl", hash = "sha256:f8c083976eb530019175aabadb60921e73b4f45736760826aa1689dda8208aee"}, - {file = "mypy-1.10.0.tar.gz", hash = "sha256:3d087fcbec056c4ee34974da493a826ce316947485cef3901f511848e687c131"}, -] - -[package.dependencies] -mypy-extensions = ">=1.0.0" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = ">=4.1.0" - -[package.extras] -dmypy = ["psutil (>=4.0)"] -install-types = ["pip"] -mypyc = ["setuptools (>=50)"] -reports = ["lxml"] - -[[package]] -name = "mypy-extensions" -version = "1.0.0" -description = "Type system extensions for programs checked with the mypy type checker." -optional = false -python-versions = ">=3.5" -files = [ - {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, - {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, -] - -[[package]] -name = "netutils" -version = "1.9.1" -description = "Common helper functions useful in network automation." -optional = false -python-versions = "<4.0,>=3.8" -files = [ - {file = "netutils-1.9.1-py3-none-any.whl", hash = "sha256:0d6e9026cc529f365a63377159aed07769baee0bf7a7138fa86fce37b64dd9d4"}, - {file = "netutils-1.9.1.tar.gz", hash = "sha256:8ad8b5e02eb9d6692d0aaaf9c0f36da1a81f520f426a79d0e08e56cf7dbb3476"}, -] - -[package.extras] -optionals = ["jsonschema (>=4.17.3,<5.0.0)", "napalm (>=4.0.0,<5.0.0)"] - -[[package]] -name = "nodeenv" -version = "1.9.1" -description = "Node.js virtual environment builder" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -files = [ - {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"}, - {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"}, -] - -[[package]] -name = "numpy" -version = "1.26.4" -description = "Fundamental package for array computing in Python" -optional = false -python-versions = ">=3.9" -files = [ - {file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"}, - {file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"}, - {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4"}, - {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f"}, - {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a"}, - {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2"}, - {file = "numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07"}, - {file = "numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5"}, - {file = "numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71"}, - {file = "numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef"}, - {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e"}, - {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5"}, - {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a"}, - {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a"}, - {file = "numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20"}, - {file = "numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2"}, - {file = "numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218"}, - {file = "numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b"}, - {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b"}, - {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed"}, - {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a"}, - {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0"}, - {file = "numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110"}, - {file = "numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818"}, - {file = "numpy-1.26.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7349ab0fa0c429c82442a27a9673fc802ffdb7c7775fad780226cb234965e53c"}, - {file = "numpy-1.26.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:52b8b60467cd7dd1e9ed082188b4e6bb35aa5cdd01777621a1658910745b90be"}, - {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5241e0a80d808d70546c697135da2c613f30e28251ff8307eb72ba696945764"}, - {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3"}, - {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:679b0076f67ecc0138fd2ede3a8fd196dddc2ad3254069bcb9faf9a79b1cebcd"}, - {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:47711010ad8555514b434df65f7d7b076bb8261df1ca9bb78f53d3b2db02e95c"}, - {file = "numpy-1.26.4-cp39-cp39-win32.whl", hash = "sha256:a354325ee03388678242a4d7ebcd08b5c727033fcff3b2f536aea978e15ee9e6"}, - {file = "numpy-1.26.4-cp39-cp39-win_amd64.whl", hash = "sha256:3373d5d70a5fe74a2c1bb6d2cfd9609ecf686d47a2d7b1d37a8f3b6bf6003aea"}, - {file = "numpy-1.26.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:afedb719a9dcfc7eaf2287b839d8198e06dcd4cb5d276a3df279231138e83d30"}, - {file = "numpy-1.26.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95a7476c59002f2f6c590b9b7b998306fba6a5aa646b1e22ddfeaf8f78c3a29c"}, - {file = "numpy-1.26.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7e50d0a0cc3189f9cb0aeb3a6a6af18c16f59f004b866cd2be1c14b36134a4a0"}, - {file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"}, -] - -[[package]] -name = "packaging" -version = "23.2" -description = "Core utilities for Python packages" -optional = false -python-versions = ">=3.7" -files = [ - {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, - {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, -] - -[[package]] -name = "parso" -version = "0.8.4" -description = "A Python Parser" -optional = false -python-versions = ">=3.6" -files = [ - {file = "parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18"}, - {file = "parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d"}, -] - -[package.extras] -qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] -testing = ["docopt", "pytest"] - -[[package]] -name = "pathspec" -version = "0.12.1" -description = "Utility library for gitignore style pattern matching of file paths." -optional = false -python-versions = ">=3.8" -files = [ - {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, - {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, -] - -[[package]] -name = "pendulum" -version = "3.0.0" -description = "Python datetimes made easy" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pendulum-3.0.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2cf9e53ef11668e07f73190c805dbdf07a1939c3298b78d5a9203a86775d1bfd"}, - {file = "pendulum-3.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fb551b9b5e6059377889d2d878d940fd0bbb80ae4810543db18e6f77b02c5ef6"}, - {file = "pendulum-3.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c58227ac260d5b01fc1025176d7b31858c9f62595737f350d22124a9a3ad82d"}, - {file = "pendulum-3.0.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:60fb6f415fea93a11c52578eaa10594568a6716602be8430b167eb0d730f3332"}, - {file = "pendulum-3.0.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b69f6b4dbcb86f2c2fe696ba991e67347bcf87fe601362a1aba6431454b46bde"}, - {file = "pendulum-3.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:138afa9c373ee450ede206db5a5e9004fd3011b3c6bbe1e57015395cd076a09f"}, - {file = "pendulum-3.0.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:83d9031f39c6da9677164241fd0d37fbfc9dc8ade7043b5d6d62f56e81af8ad2"}, - {file = "pendulum-3.0.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0c2308af4033fa534f089595bcd40a95a39988ce4059ccd3dc6acb9ef14ca44a"}, - {file = "pendulum-3.0.0-cp310-none-win_amd64.whl", hash = "sha256:9a59637cdb8462bdf2dbcb9d389518c0263799189d773ad5c11db6b13064fa79"}, - {file = "pendulum-3.0.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:3725245c0352c95d6ca297193192020d1b0c0f83d5ee6bb09964edc2b5a2d508"}, - {file = "pendulum-3.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6c035f03a3e565ed132927e2c1b691de0dbf4eb53b02a5a3c5a97e1a64e17bec"}, - {file = "pendulum-3.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:597e66e63cbd68dd6d58ac46cb7a92363d2088d37ccde2dae4332ef23e95cd00"}, - {file = "pendulum-3.0.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99a0f8172e19f3f0c0e4ace0ad1595134d5243cf75985dc2233e8f9e8de263ca"}, - {file = "pendulum-3.0.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:77d8839e20f54706aed425bec82a83b4aec74db07f26acd039905d1237a5e1d4"}, - {file = "pendulum-3.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afde30e8146292b059020fbc8b6f8fd4a60ae7c5e6f0afef937bbb24880bdf01"}, - {file = "pendulum-3.0.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:660434a6fcf6303c4efd36713ca9212c753140107ee169a3fc6c49c4711c2a05"}, - {file = "pendulum-3.0.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dee9e5a48c6999dc1106eb7eea3e3a50e98a50651b72c08a87ee2154e544b33e"}, - {file = "pendulum-3.0.0-cp311-none-win_amd64.whl", hash = "sha256:d4cdecde90aec2d67cebe4042fd2a87a4441cc02152ed7ed8fb3ebb110b94ec4"}, - {file = "pendulum-3.0.0-cp311-none-win_arm64.whl", hash = "sha256:773c3bc4ddda2dda9f1b9d51fe06762f9200f3293d75c4660c19b2614b991d83"}, - {file = "pendulum-3.0.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:409e64e41418c49f973d43a28afe5df1df4f1dd87c41c7c90f1a63f61ae0f1f7"}, - {file = "pendulum-3.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a38ad2121c5ec7c4c190c7334e789c3b4624798859156b138fcc4d92295835dc"}, - {file = "pendulum-3.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fde4d0b2024b9785f66b7f30ed59281bd60d63d9213cda0eb0910ead777f6d37"}, - {file = "pendulum-3.0.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b2c5675769fb6d4c11238132962939b960fcb365436b6d623c5864287faa319"}, - {file = "pendulum-3.0.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8af95e03e066826f0f4c65811cbee1b3123d4a45a1c3a2b4fc23c4b0dff893b5"}, - {file = "pendulum-3.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2165a8f33cb15e06c67070b8afc87a62b85c5a273e3aaa6bc9d15c93a4920d6f"}, - {file = "pendulum-3.0.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ad5e65b874b5e56bd942546ea7ba9dd1d6a25121db1c517700f1c9de91b28518"}, - {file = "pendulum-3.0.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:17fe4b2c844bbf5f0ece69cfd959fa02957c61317b2161763950d88fed8e13b9"}, - {file = "pendulum-3.0.0-cp312-none-win_amd64.whl", hash = "sha256:78f8f4e7efe5066aca24a7a57511b9c2119f5c2b5eb81c46ff9222ce11e0a7a5"}, - {file = "pendulum-3.0.0-cp312-none-win_arm64.whl", hash = "sha256:28f49d8d1e32aae9c284a90b6bb3873eee15ec6e1d9042edd611b22a94ac462f"}, - {file = "pendulum-3.0.0-cp37-cp37m-macosx_10_12_x86_64.whl", hash = "sha256:d4e2512f4e1a4670284a153b214db9719eb5d14ac55ada5b76cbdb8c5c00399d"}, - {file = "pendulum-3.0.0-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:3d897eb50883cc58d9b92f6405245f84b9286cd2de6e8694cb9ea5cb15195a32"}, - {file = "pendulum-3.0.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e169cc2ca419517f397811bbe4589cf3cd13fca6dc38bb352ba15ea90739ebb"}, - {file = "pendulum-3.0.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f17c3084a4524ebefd9255513692f7e7360e23c8853dc6f10c64cc184e1217ab"}, - {file = "pendulum-3.0.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:826d6e258052715f64d05ae0fc9040c0151e6a87aae7c109ba9a0ed930ce4000"}, - {file = "pendulum-3.0.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2aae97087872ef152a0c40e06100b3665d8cb86b59bc8471ca7c26132fccd0f"}, - {file = "pendulum-3.0.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:ac65eeec2250d03106b5e81284ad47f0d417ca299a45e89ccc69e36130ca8bc7"}, - {file = "pendulum-3.0.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a5346d08f3f4a6e9e672187faa179c7bf9227897081d7121866358af369f44f9"}, - {file = "pendulum-3.0.0-cp37-none-win_amd64.whl", hash = "sha256:235d64e87946d8f95c796af34818c76e0f88c94d624c268693c85b723b698aa9"}, - {file = "pendulum-3.0.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:6a881d9c2a7f85bc9adafcfe671df5207f51f5715ae61f5d838b77a1356e8b7b"}, - {file = "pendulum-3.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d7762d2076b9b1cb718a6631ad6c16c23fc3fac76cbb8c454e81e80be98daa34"}, - {file = "pendulum-3.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e8e36a8130819d97a479a0e7bf379b66b3b1b520e5dc46bd7eb14634338df8c"}, - {file = "pendulum-3.0.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7dc843253ac373358ffc0711960e2dd5b94ab67530a3e204d85c6e8cb2c5fa10"}, - {file = "pendulum-3.0.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0a78ad3635d609ceb1e97d6aedef6a6a6f93433ddb2312888e668365908c7120"}, - {file = "pendulum-3.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b30a137e9e0d1f751e60e67d11fc67781a572db76b2296f7b4d44554761049d6"}, - {file = "pendulum-3.0.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:c95984037987f4a457bb760455d9ca80467be792236b69d0084f228a8ada0162"}, - {file = "pendulum-3.0.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d29c6e578fe0f893766c0d286adbf0b3c726a4e2341eba0917ec79c50274ec16"}, - {file = "pendulum-3.0.0-cp38-none-win_amd64.whl", hash = "sha256:deaba8e16dbfcb3d7a6b5fabdd5a38b7c982809567479987b9c89572df62e027"}, - {file = "pendulum-3.0.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:b11aceea5b20b4b5382962b321dbc354af0defe35daa84e9ff3aae3c230df694"}, - {file = "pendulum-3.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a90d4d504e82ad236afac9adca4d6a19e4865f717034fc69bafb112c320dcc8f"}, - {file = "pendulum-3.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:825799c6b66e3734227756fa746cc34b3549c48693325b8b9f823cb7d21b19ac"}, - {file = "pendulum-3.0.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad769e98dc07972e24afe0cff8d365cb6f0ebc7e65620aa1976fcfbcadc4c6f3"}, - {file = "pendulum-3.0.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6fc26907eb5fb8cc6188cc620bc2075a6c534d981a2f045daa5f79dfe50d512"}, - {file = "pendulum-3.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c717eab1b6d898c00a3e0fa7781d615b5c5136bbd40abe82be100bb06df7a56"}, - {file = "pendulum-3.0.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:3ddd1d66d1a714ce43acfe337190be055cdc221d911fc886d5a3aae28e14b76d"}, - {file = "pendulum-3.0.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:822172853d7a9cf6da95d7b66a16c7160cb99ae6df55d44373888181d7a06edc"}, - {file = "pendulum-3.0.0-cp39-none-win_amd64.whl", hash = "sha256:840de1b49cf1ec54c225a2a6f4f0784d50bd47f68e41dc005b7f67c7d5b5f3ae"}, - {file = "pendulum-3.0.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3b1f74d1e6ffe5d01d6023870e2ce5c2191486928823196f8575dcc786e107b1"}, - {file = "pendulum-3.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:729e9f93756a2cdfa77d0fc82068346e9731c7e884097160603872686e570f07"}, - {file = "pendulum-3.0.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e586acc0b450cd21cbf0db6bae386237011b75260a3adceddc4be15334689a9a"}, - {file = "pendulum-3.0.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22e7944ffc1f0099a79ff468ee9630c73f8c7835cd76fdb57ef7320e6a409df4"}, - {file = "pendulum-3.0.0-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:fa30af36bd8e50686846bdace37cf6707bdd044e5cb6e1109acbad3277232e04"}, - {file = "pendulum-3.0.0-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:440215347b11914ae707981b9a57ab9c7b6983ab0babde07063c6ee75c0dc6e7"}, - {file = "pendulum-3.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:314c4038dc5e6a52991570f50edb2f08c339debdf8cea68ac355b32c4174e820"}, - {file = "pendulum-3.0.0-pp37-pypy37_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5acb1d386337415f74f4d1955c4ce8d0201978c162927d07df8eb0692b2d8533"}, - {file = "pendulum-3.0.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a789e12fbdefaffb7b8ac67f9d8f22ba17a3050ceaaa635cd1cc4645773a4b1e"}, - {file = "pendulum-3.0.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:860aa9b8a888e5913bd70d819306749e5eb488e6b99cd6c47beb701b22bdecf5"}, - {file = "pendulum-3.0.0-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:5ebc65ea033ef0281368217fbf59f5cb05b338ac4dd23d60959c7afcd79a60a0"}, - {file = "pendulum-3.0.0-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:d9fef18ab0386ef6a9ac7bad7e43ded42c83ff7ad412f950633854f90d59afa8"}, - {file = "pendulum-3.0.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:1c134ba2f0571d0b68b83f6972e2307a55a5a849e7dac8505c715c531d2a8795"}, - {file = "pendulum-3.0.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:385680812e7e18af200bb9b4a49777418c32422d05ad5a8eb85144c4a285907b"}, - {file = "pendulum-3.0.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9eec91cd87c59fb32ec49eb722f375bd58f4be790cae11c1b70fac3ee4f00da0"}, - {file = "pendulum-3.0.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4386bffeca23c4b69ad50a36211f75b35a4deb6210bdca112ac3043deb7e494a"}, - {file = "pendulum-3.0.0-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:dfbcf1661d7146d7698da4b86e7f04814221081e9fe154183e34f4c5f5fa3bf8"}, - {file = "pendulum-3.0.0-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:04a1094a5aa1daa34a6b57c865b25f691848c61583fb22722a4df5699f6bf74c"}, - {file = "pendulum-3.0.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5b0ec85b9045bd49dd3a3493a5e7ddfd31c36a2a60da387c419fa04abcaecb23"}, - {file = "pendulum-3.0.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:0a15b90129765b705eb2039062a6daf4d22c4e28d1a54fa260892e8c3ae6e157"}, - {file = "pendulum-3.0.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:bb8f6d7acd67a67d6fedd361ad2958ff0539445ef51cbe8cd288db4306503cd0"}, - {file = "pendulum-3.0.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd69b15374bef7e4b4440612915315cc42e8575fcda2a3d7586a0d88192d0c88"}, - {file = "pendulum-3.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc00f8110db6898360c53c812872662e077eaf9c75515d53ecc65d886eec209a"}, - {file = "pendulum-3.0.0-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:83a44e8b40655d0ba565a5c3d1365d27e3e6778ae2a05b69124db9e471255c4a"}, - {file = "pendulum-3.0.0-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:1a3604e9fbc06b788041b2a8b78f75c243021e0f512447806a6d37ee5214905d"}, - {file = "pendulum-3.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:92c307ae7accebd06cbae4729f0ba9fa724df5f7d91a0964b1b972a22baa482b"}, - {file = "pendulum-3.0.0.tar.gz", hash = "sha256:5d034998dea404ec31fae27af6b22cff1708f830a1ed7353be4d1019bb9f584e"}, -] - -[package.dependencies] -python-dateutil = ">=2.6" -tzdata = ">=2020.1" - -[package.extras] -test = ["time-machine (>=2.6.0)"] - -[[package]] -name = "pexpect" -version = "4.9.0" -description = "Pexpect allows easy control of interactive console applications." -optional = false -python-versions = "*" -files = [ - {file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"}, - {file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"}, -] - -[package.dependencies] -ptyprocess = ">=0.5" - -[[package]] -name = "platformdirs" -version = "4.2.2" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." -optional = false -python-versions = ">=3.8" -files = [ - {file = "platformdirs-4.2.2-py3-none-any.whl", hash = "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee"}, - {file = "platformdirs-4.2.2.tar.gz", hash = "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"}, -] - -[package.extras] -docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] -type = ["mypy (>=1.8)"] - -[[package]] -name = "pluggy" -version = "1.5.0" -description = "plugin and hook calling mechanisms for python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, - {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, -] - -[package.extras] -dev = ["pre-commit", "tox"] -testing = ["pytest", "pytest-benchmark"] - -[[package]] -name = "pprintpp" -version = "0.4.0" -description = "A drop-in replacement for pprint that's actually pretty" -optional = false -python-versions = "*" -files = [ - {file = "pprintpp-0.4.0-py2.py3-none-any.whl", hash = "sha256:b6b4dcdd0c0c0d75e4d7b2f21a9e933e5b2ce62b26e1a54537f9651ae5a5c01d"}, - {file = "pprintpp-0.4.0.tar.gz", hash = "sha256:ea826108e2c7f49dc6d66c752973c3fc9749142a798d6b254e1e301cfdbc6403"}, -] - -[[package]] -name = "pre-commit" -version = "2.21.0" -description = "A framework for managing and maintaining multi-language pre-commit hooks." -optional = false -python-versions = ">=3.7" -files = [ - {file = "pre_commit-2.21.0-py2.py3-none-any.whl", hash = "sha256:e2f91727039fc39a92f58a588a25b87f936de6567eed4f0e673e0507edc75bad"}, - {file = "pre_commit-2.21.0.tar.gz", hash = "sha256:31ef31af7e474a8d8995027fefdfcf509b5c913ff31f2015b4ec4beb26a6f658"}, -] - -[package.dependencies] -cfgv = ">=2.0.0" -identify = ">=1.0.0" -nodeenv = ">=0.11.1" -pyyaml = ">=5.1" -virtualenv = ">=20.10.0" - -[[package]] -name = "prompt-toolkit" -version = "3.0.47" -description = "Library for building powerful interactive command lines in Python" -optional = false -python-versions = ">=3.7.0" -files = [ - {file = "prompt_toolkit-3.0.47-py3-none-any.whl", hash = "sha256:0d7bfa67001d5e39d02c224b663abc33687405033a8c422d0d675a5a13361d10"}, - {file = "prompt_toolkit-3.0.47.tar.gz", hash = "sha256:1e1b29cb58080b1e69f207c893a1a7bf16d127a5c30c9d17a25a5d77792e5360"}, -] - -[package.dependencies] -wcwidth = "*" - -[[package]] -name = "ptyprocess" -version = "0.7.0" -description = "Run a subprocess in a pseudo terminal" -optional = false -python-versions = "*" -files = [ - {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, - {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, -] - -[[package]] -name = "pure-eval" -version = "0.2.2" -description = "Safely evaluate AST nodes without side effects" -optional = false -python-versions = "*" -files = [ - {file = "pure_eval-0.2.2-py3-none-any.whl", hash = "sha256:01eaab343580944bc56080ebe0a674b39ec44a945e6d09ba7db3cb8cec289350"}, - {file = "pure_eval-0.2.2.tar.gz", hash = "sha256:2b45320af6dfaa1750f543d714b6d1c520a1688dec6fd24d339063ce0aaa9ac3"}, -] - -[package.extras] -tests = ["pytest"] - -[[package]] -name = "pyarrow" -version = "14.0.2" -description = "Python library for Apache Arrow" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pyarrow-14.0.2-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:ba9fe808596c5dbd08b3aeffe901e5f81095baaa28e7d5118e01354c64f22807"}, - {file = "pyarrow-14.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:22a768987a16bb46220cef490c56c671993fbee8fd0475febac0b3e16b00a10e"}, - {file = "pyarrow-14.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2dbba05e98f247f17e64303eb876f4a80fcd32f73c7e9ad975a83834d81f3fda"}, - {file = "pyarrow-14.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a898d134d00b1eca04998e9d286e19653f9d0fcb99587310cd10270907452a6b"}, - {file = "pyarrow-14.0.2-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:87e879323f256cb04267bb365add7208f302df942eb943c93a9dfeb8f44840b1"}, - {file = "pyarrow-14.0.2-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:76fc257559404ea5f1306ea9a3ff0541bf996ff3f7b9209fc517b5e83811fa8e"}, - {file = "pyarrow-14.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:b0c4a18e00f3a32398a7f31da47fefcd7a927545b396e1f15d0c85c2f2c778cd"}, - {file = "pyarrow-14.0.2-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:87482af32e5a0c0cce2d12eb3c039dd1d853bd905b04f3f953f147c7a196915b"}, - {file = "pyarrow-14.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:059bd8f12a70519e46cd64e1ba40e97eae55e0cbe1695edd95384653d7626b23"}, - {file = "pyarrow-14.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f16111f9ab27e60b391c5f6d197510e3ad6654e73857b4e394861fc79c37200"}, - {file = "pyarrow-14.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06ff1264fe4448e8d02073f5ce45a9f934c0f3db0a04460d0b01ff28befc3696"}, - {file = "pyarrow-14.0.2-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:6dd4f4b472ccf4042f1eab77e6c8bce574543f54d2135c7e396f413046397d5a"}, - {file = "pyarrow-14.0.2-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:32356bfb58b36059773f49e4e214996888eeea3a08893e7dbde44753799b2a02"}, - {file = "pyarrow-14.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:52809ee69d4dbf2241c0e4366d949ba035cbcf48409bf404f071f624ed313a2b"}, - {file = "pyarrow-14.0.2-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:c87824a5ac52be210d32906c715f4ed7053d0180c1060ae3ff9b7e560f53f944"}, - {file = "pyarrow-14.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a25eb2421a58e861f6ca91f43339d215476f4fe159eca603c55950c14f378cc5"}, - {file = "pyarrow-14.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c1da70d668af5620b8ba0a23f229030a4cd6c5f24a616a146f30d2386fec422"}, - {file = "pyarrow-14.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2cc61593c8e66194c7cdfae594503e91b926a228fba40b5cf25cc593563bcd07"}, - {file = "pyarrow-14.0.2-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:78ea56f62fb7c0ae8ecb9afdd7893e3a7dbeb0b04106f5c08dbb23f9c0157591"}, - {file = "pyarrow-14.0.2-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:37c233ddbce0c67a76c0985612fef27c0c92aef9413cf5aa56952f359fcb7379"}, - {file = "pyarrow-14.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:e4b123ad0f6add92de898214d404e488167b87b5dd86e9a434126bc2b7a5578d"}, - {file = "pyarrow-14.0.2-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:e354fba8490de258be7687f341bc04aba181fc8aa1f71e4584f9890d9cb2dec2"}, - {file = "pyarrow-14.0.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:20e003a23a13da963f43e2b432483fdd8c38dc8882cd145f09f21792e1cf22a1"}, - {file = "pyarrow-14.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc0de7575e841f1595ac07e5bc631084fd06ca8b03c0f2ecece733d23cd5102a"}, - {file = "pyarrow-14.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66e986dc859712acb0bd45601229021f3ffcdfc49044b64c6d071aaf4fa49e98"}, - {file = "pyarrow-14.0.2-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:f7d029f20ef56673a9730766023459ece397a05001f4e4d13805111d7c2108c0"}, - {file = "pyarrow-14.0.2-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:209bac546942b0d8edc8debda248364f7f668e4aad4741bae58e67d40e5fcf75"}, - {file = "pyarrow-14.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:1e6987c5274fb87d66bb36816afb6f65707546b3c45c44c28e3c4133c010a881"}, - {file = "pyarrow-14.0.2-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:a01d0052d2a294a5f56cc1862933014e696aa08cc7b620e8c0cce5a5d362e976"}, - {file = "pyarrow-14.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a51fee3a7db4d37f8cda3ea96f32530620d43b0489d169b285d774da48ca9785"}, - {file = "pyarrow-14.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64df2bf1ef2ef14cee531e2dfe03dd924017650ffaa6f9513d7a1bb291e59c15"}, - {file = "pyarrow-14.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c0fa3bfdb0305ffe09810f9d3e2e50a2787e3a07063001dcd7adae0cee3601a"}, - {file = "pyarrow-14.0.2-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:c65bf4fd06584f058420238bc47a316e80dda01ec0dfb3044594128a6c2db794"}, - {file = "pyarrow-14.0.2-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:63ac901baec9369d6aae1cbe6cca11178fb018a8d45068aaf5bb54f94804a866"}, - {file = "pyarrow-14.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:75ee0efe7a87a687ae303d63037d08a48ef9ea0127064df18267252cfe2e9541"}, - {file = "pyarrow-14.0.2.tar.gz", hash = "sha256:36cef6ba12b499d864d1def3e990f97949e0b79400d08b7cf74504ffbd3eb025"}, -] - -[package.dependencies] -numpy = ">=1.16.6" - -[[package]] -name = "pydantic" -version = "2.7.4" -description = "Data validation using Python type hints" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pydantic-2.7.4-py3-none-any.whl", hash = "sha256:ee8538d41ccb9c0a9ad3e0e5f07bf15ed8015b481ced539a1759d8cc89ae90d0"}, - {file = "pydantic-2.7.4.tar.gz", hash = "sha256:0c84efd9548d545f63ac0060c1e4d39bb9b14db8b3c0652338aecc07b5adec52"}, -] - -[package.dependencies] -annotated-types = ">=0.4.0" -pydantic-core = "2.18.4" -typing-extensions = ">=4.6.1" - -[package.extras] -email = ["email-validator (>=2.0.0)"] - -[[package]] -name = "pydantic-core" -version = "2.18.4" -description = "Core functionality for Pydantic validation and serialization" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pydantic_core-2.18.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:f76d0ad001edd426b92233d45c746fd08f467d56100fd8f30e9ace4b005266e4"}, - {file = "pydantic_core-2.18.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:59ff3e89f4eaf14050c8022011862df275b552caef8082e37b542b066ce1ff26"}, - {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a55b5b16c839df1070bc113c1f7f94a0af4433fcfa1b41799ce7606e5c79ce0a"}, - {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4d0dcc59664fcb8974b356fe0a18a672d6d7cf9f54746c05f43275fc48636851"}, - {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8951eee36c57cd128f779e641e21eb40bc5073eb28b2d23f33eb0ef14ffb3f5d"}, - {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4701b19f7e3a06ea655513f7938de6f108123bf7c86bbebb1196eb9bd35cf724"}, - {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e00a3f196329e08e43d99b79b286d60ce46bed10f2280d25a1718399457e06be"}, - {file = "pydantic_core-2.18.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:97736815b9cc893b2b7f663628e63f436018b75f44854c8027040e05230eeddb"}, - {file = "pydantic_core-2.18.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6891a2ae0e8692679c07728819b6e2b822fb30ca7445f67bbf6509b25a96332c"}, - {file = "pydantic_core-2.18.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bc4ff9805858bd54d1a20efff925ccd89c9d2e7cf4986144b30802bf78091c3e"}, - {file = "pydantic_core-2.18.4-cp310-none-win32.whl", hash = "sha256:1b4de2e51bbcb61fdebd0ab86ef28062704f62c82bbf4addc4e37fa4b00b7cbc"}, - {file = "pydantic_core-2.18.4-cp310-none-win_amd64.whl", hash = "sha256:6a750aec7bf431517a9fd78cb93c97b9b0c496090fee84a47a0d23668976b4b0"}, - {file = "pydantic_core-2.18.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:942ba11e7dfb66dc70f9ae66b33452f51ac7bb90676da39a7345e99ffb55402d"}, - {file = "pydantic_core-2.18.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b2ebef0e0b4454320274f5e83a41844c63438fdc874ea40a8b5b4ecb7693f1c4"}, - {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a642295cd0c8df1b86fc3dced1d067874c353a188dc8e0f744626d49e9aa51c4"}, - {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5f09baa656c904807e832cf9cce799c6460c450c4ad80803517032da0cd062e2"}, - {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:98906207f29bc2c459ff64fa007afd10a8c8ac080f7e4d5beff4c97086a3dabd"}, - {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19894b95aacfa98e7cb093cd7881a0c76f55731efad31073db4521e2b6ff5b7d"}, - {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0fbbdc827fe5e42e4d196c746b890b3d72876bdbf160b0eafe9f0334525119c8"}, - {file = "pydantic_core-2.18.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f85d05aa0918283cf29a30b547b4df2fbb56b45b135f9e35b6807cb28bc47951"}, - {file = "pydantic_core-2.18.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e85637bc8fe81ddb73fda9e56bab24560bdddfa98aa64f87aaa4e4b6730c23d2"}, - {file = "pydantic_core-2.18.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2f5966897e5461f818e136b8451d0551a2e77259eb0f73a837027b47dc95dab9"}, - {file = "pydantic_core-2.18.4-cp311-none-win32.whl", hash = "sha256:44c7486a4228413c317952e9d89598bcdfb06399735e49e0f8df643e1ccd0558"}, - {file = "pydantic_core-2.18.4-cp311-none-win_amd64.whl", hash = "sha256:8a7164fe2005d03c64fd3b85649891cd4953a8de53107940bf272500ba8a788b"}, - {file = "pydantic_core-2.18.4-cp311-none-win_arm64.whl", hash = "sha256:4e99bc050fe65c450344421017f98298a97cefc18c53bb2f7b3531eb39bc7805"}, - {file = "pydantic_core-2.18.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:6f5c4d41b2771c730ea1c34e458e781b18cc668d194958e0112455fff4e402b2"}, - {file = "pydantic_core-2.18.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2fdf2156aa3d017fddf8aea5adfba9f777db1d6022d392b682d2a8329e087cef"}, - {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4748321b5078216070b151d5271ef3e7cc905ab170bbfd27d5c83ee3ec436695"}, - {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:847a35c4d58721c5dc3dba599878ebbdfd96784f3fb8bb2c356e123bdcd73f34"}, - {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3c40d4eaad41f78e3bbda31b89edc46a3f3dc6e171bf0ecf097ff7a0ffff7cb1"}, - {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:21a5e440dbe315ab9825fcd459b8814bb92b27c974cbc23c3e8baa2b76890077"}, - {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01dd777215e2aa86dfd664daed5957704b769e726626393438f9c87690ce78c3"}, - {file = "pydantic_core-2.18.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4b06beb3b3f1479d32befd1f3079cc47b34fa2da62457cdf6c963393340b56e9"}, - {file = "pydantic_core-2.18.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:564d7922e4b13a16b98772441879fcdcbe82ff50daa622d681dd682175ea918c"}, - {file = "pydantic_core-2.18.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0eb2a4f660fcd8e2b1c90ad566db2b98d7f3f4717c64fe0a83e0adb39766d5b8"}, - {file = "pydantic_core-2.18.4-cp312-none-win32.whl", hash = "sha256:8b8bab4c97248095ae0c4455b5a1cd1cdd96e4e4769306ab19dda135ea4cdb07"}, - {file = "pydantic_core-2.18.4-cp312-none-win_amd64.whl", hash = "sha256:14601cdb733d741b8958224030e2bfe21a4a881fb3dd6fbb21f071cabd48fa0a"}, - {file = "pydantic_core-2.18.4-cp312-none-win_arm64.whl", hash = "sha256:c1322d7dd74713dcc157a2b7898a564ab091ca6c58302d5c7b4c07296e3fd00f"}, - {file = "pydantic_core-2.18.4-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:823be1deb01793da05ecb0484d6c9e20baebb39bd42b5d72636ae9cf8350dbd2"}, - {file = "pydantic_core-2.18.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ebef0dd9bf9b812bf75bda96743f2a6c5734a02092ae7f721c048d156d5fabae"}, - {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae1d6df168efb88d7d522664693607b80b4080be6750c913eefb77e34c12c71a"}, - {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f9899c94762343f2cc2fc64c13e7cae4c3cc65cdfc87dd810a31654c9b7358cc"}, - {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99457f184ad90235cfe8461c4d70ab7dd2680e28821c29eca00252ba90308c78"}, - {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18f469a3d2a2fdafe99296a87e8a4c37748b5080a26b806a707f25a902c040a8"}, - {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7cdf28938ac6b8b49ae5e92f2735056a7ba99c9b110a474473fd71185c1af5d"}, - {file = "pydantic_core-2.18.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:938cb21650855054dc54dfd9120a851c974f95450f00683399006aa6e8abb057"}, - {file = "pydantic_core-2.18.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:44cd83ab6a51da80fb5adbd9560e26018e2ac7826f9626bc06ca3dc074cd198b"}, - {file = "pydantic_core-2.18.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:972658f4a72d02b8abfa2581d92d59f59897d2e9f7e708fdabe922f9087773af"}, - {file = "pydantic_core-2.18.4-cp38-none-win32.whl", hash = "sha256:1d886dc848e60cb7666f771e406acae54ab279b9f1e4143babc9c2258213daa2"}, - {file = "pydantic_core-2.18.4-cp38-none-win_amd64.whl", hash = "sha256:bb4462bd43c2460774914b8525f79b00f8f407c945d50881568f294c1d9b4443"}, - {file = "pydantic_core-2.18.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:44a688331d4a4e2129140a8118479443bd6f1905231138971372fcde37e43528"}, - {file = "pydantic_core-2.18.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a2fdd81edd64342c85ac7cf2753ccae0b79bf2dfa063785503cb85a7d3593223"}, - {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86110d7e1907ab36691f80b33eb2da87d780f4739ae773e5fc83fb272f88825f"}, - {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:46387e38bd641b3ee5ce247563b60c5ca098da9c56c75c157a05eaa0933ed154"}, - {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:123c3cec203e3f5ac7b000bd82235f1a3eced8665b63d18be751f115588fea30"}, - {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dc1803ac5c32ec324c5261c7209e8f8ce88e83254c4e1aebdc8b0a39f9ddb443"}, - {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53db086f9f6ab2b4061958d9c276d1dbe3690e8dd727d6abf2321d6cce37fa94"}, - {file = "pydantic_core-2.18.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:abc267fa9837245cc28ea6929f19fa335f3dc330a35d2e45509b6566dc18be23"}, - {file = "pydantic_core-2.18.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a0d829524aaefdebccb869eed855e2d04c21d2d7479b6cada7ace5448416597b"}, - {file = "pydantic_core-2.18.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:509daade3b8649f80d4e5ff21aa5673e4ebe58590b25fe42fac5f0f52c6f034a"}, - {file = "pydantic_core-2.18.4-cp39-none-win32.whl", hash = "sha256:ca26a1e73c48cfc54c4a76ff78df3727b9d9f4ccc8dbee4ae3f73306a591676d"}, - {file = "pydantic_core-2.18.4-cp39-none-win_amd64.whl", hash = "sha256:c67598100338d5d985db1b3d21f3619ef392e185e71b8d52bceacc4a7771ea7e"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:574d92eac874f7f4db0ca653514d823a0d22e2354359d0759e3f6a406db5d55d"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1f4d26ceb5eb9eed4af91bebeae4b06c3fb28966ca3a8fb765208cf6b51102ab"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77450e6d20016ec41f43ca4a6c63e9fdde03f0ae3fe90e7c27bdbeaece8b1ed4"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d323a01da91851a4f17bf592faf46149c9169d68430b3146dcba2bb5e5719abc"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43d447dd2ae072a0065389092a231283f62d960030ecd27565672bd40746c507"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:578e24f761f3b425834f297b9935e1ce2e30f51400964ce4801002435a1b41ef"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:81b5efb2f126454586d0f40c4d834010979cb80785173d1586df845a632e4e6d"}, - {file = "pydantic_core-2.18.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ab86ce7c8f9bea87b9d12c7f0af71102acbf5ecbc66c17796cff45dae54ef9a5"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:90afc12421df2b1b4dcc975f814e21bc1754640d502a2fbcc6d41e77af5ec312"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:51991a89639a912c17bef4b45c87bd83593aee0437d8102556af4885811d59f5"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:293afe532740370aba8c060882f7d26cfd00c94cae32fd2e212a3a6e3b7bc15e"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b48ece5bde2e768197a2d0f6e925f9d7e3e826f0ad2271120f8144a9db18d5c8"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:eae237477a873ab46e8dd748e515c72c0c804fb380fbe6c85533c7de51f23a8f"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:834b5230b5dfc0c1ec37b2fda433b271cbbc0e507560b5d1588e2cc1148cf1ce"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e858ac0a25074ba4bce653f9b5d0a85b7456eaddadc0ce82d3878c22489fa4ee"}, - {file = "pydantic_core-2.18.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2fd41f6eff4c20778d717af1cc50eca52f5afe7805ee530a4fbd0bae284f16e9"}, - {file = "pydantic_core-2.18.4.tar.gz", hash = "sha256:ec3beeada09ff865c344ff3bc2f427f5e6c26401cc6113d77e372c3fdac73864"}, -] - -[package.dependencies] -typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" - -[[package]] -name = "pydantic-settings" -version = "2.4.0" -description = "Settings management using Pydantic" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pydantic_settings-2.4.0-py3-none-any.whl", hash = "sha256:bb6849dc067f1687574c12a639e231f3a6feeed0a12d710c1382045c5db1c315"}, - {file = "pydantic_settings-2.4.0.tar.gz", hash = "sha256:ed81c3a0f46392b4d7c0a565c05884e6e54b3456e6f0fe4d8814981172dc9a88"}, -] - -[package.dependencies] -pydantic = ">=2.7.0" -python-dotenv = ">=0.21.0" - -[package.extras] -azure-key-vault = ["azure-identity (>=1.16.0)", "azure-keyvault-secrets (>=4.8.0)"] -toml = ["tomli (>=2.0.1)"] -yaml = ["pyyaml (>=6.0.1)"] - -[[package]] -name = "pygments" -version = "2.18.0" -description = "Pygments is a syntax highlighting package written in Python." -optional = false -python-versions = ">=3.8" -files = [ - {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, - {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, -] - -[package.extras] -windows-terminal = ["colorama (>=0.4.6)"] - -[[package]] -name = "pylint" -version = "3.2.3" -description = "python code static checker" -optional = false -python-versions = ">=3.8.0" -files = [ - {file = "pylint-3.2.3-py3-none-any.whl", hash = "sha256:b3d7d2708a3e04b4679e02d99e72329a8b7ee8afb8d04110682278781f889fa8"}, - {file = "pylint-3.2.3.tar.gz", hash = "sha256:02f6c562b215582386068d52a30f520d84fdbcf2a95fc7e855b816060d048b60"}, -] - -[package.dependencies] -astroid = ">=3.2.2,<=3.3.0-dev0" -colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} -dill = [ - {version = ">=0.2", markers = "python_version < \"3.11\""}, - {version = ">=0.3.7", markers = "python_version >= \"3.12\""}, - {version = ">=0.3.6", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, -] -isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" -mccabe = ">=0.6,<0.8" -platformdirs = ">=2.2.0" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -tomlkit = ">=0.10.1" -typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} - -[package.extras] -spelling = ["pyenchant (>=3.2,<4.0)"] -testutils = ["gitpython (>3)"] - -[[package]] -name = "pytest" -version = "8.2.2" -description = "pytest: simple powerful testing with Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pytest-8.2.2-py3-none-any.whl", hash = "sha256:c434598117762e2bd304e526244f67bf66bbd7b5d6cf22138be51ff661980343"}, - {file = "pytest-8.2.2.tar.gz", hash = "sha256:de4bb8104e201939ccdc688b27a89a7be2079b22e2bd2b07f806b6ba71117977"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "sys_platform == \"win32\""} -exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} -iniconfig = "*" -packaging = "*" -pluggy = ">=1.5,<2.0" -tomli = {version = ">=1", markers = "python_version < \"3.11\""} - -[package.extras] -dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] - -[[package]] -name = "pytest-asyncio" -version = "0.23.7" -description = "Pytest support for asyncio" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pytest_asyncio-0.23.7-py3-none-any.whl", hash = "sha256:009b48127fbe44518a547bddd25611551b0e43ccdbf1e67d12479f569832c20b"}, - {file = "pytest_asyncio-0.23.7.tar.gz", hash = "sha256:5f5c72948f4c49e7db4f29f2521d4031f1c27f86e57b046126654083d4770268"}, -] - -[package.dependencies] -pytest = ">=7.0.0,<9" - -[package.extras] -docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"] -testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"] - -[[package]] -name = "pytest-clarity" -version = "1.0.1" -description = "A plugin providing an alternative, colourful diff output for failing assertions." -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "pytest-clarity-1.0.1.tar.gz", hash = "sha256:505fe345fad4fe11c6a4187fe683f2c7c52c077caa1e135f3e483fe112db7772"}, -] - -[package.dependencies] -pprintpp = ">=0.4.0" -pytest = ">=3.5.0" -rich = ">=8.0.0" - -[[package]] -name = "pytest-cov" -version = "4.1.0" -description = "Pytest plugin for measuring coverage." -optional = false -python-versions = ">=3.7" -files = [ - {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, - {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, -] - -[package.dependencies] -coverage = {version = ">=5.2.1", extras = ["toml"]} -pytest = ">=4.6" - -[package.extras] -testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] - -[[package]] -name = "pytest-httpx" -version = "0.30.0" -description = "Send responses to httpx." -optional = false -python-versions = ">=3.9" -files = [ - {file = "pytest-httpx-0.30.0.tar.gz", hash = "sha256:755b8edca87c974dd4f3605c374fda11db84631de3d163b99c0df5807023a19a"}, - {file = "pytest_httpx-0.30.0-py3-none-any.whl", hash = "sha256:6d47849691faf11d2532565d0c8e0e02b9f4ee730da31687feae315581d7520c"}, -] - -[package.dependencies] -httpx = "==0.27.*" -pytest = ">=7,<9" - -[package.extras] -testing = ["pytest-asyncio (==0.23.*)", "pytest-cov (==4.*)"] - -[[package]] -name = "pytest-xdist" -version = "3.6.1" -description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pytest_xdist-3.6.1-py3-none-any.whl", hash = "sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7"}, - {file = "pytest_xdist-3.6.1.tar.gz", hash = "sha256:ead156a4db231eec769737f57668ef58a2084a34b2e55c4a8fa20d861107300d"}, -] - -[package.dependencies] -execnet = ">=2.1" -pytest = ">=7.0.0" - -[package.extras] -psutil = ["psutil (>=3.0)"] -setproctitle = ["setproctitle"] -testing = ["filelock"] - -[[package]] -name = "python-dateutil" -version = "2.9.0.post0" -description = "Extensions to the standard Python datetime module" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -files = [ - {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, - {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, -] - -[package.dependencies] -six = ">=1.5" - -[[package]] -name = "python-dotenv" -version = "1.0.1" -description = "Read key-value pairs from a .env file and set them as environment variables" -optional = false -python-versions = ">=3.8" -files = [ - {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, - {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, -] - -[package.extras] -cli = ["click (>=5.0)"] - -[[package]] -name = "pyyaml" -version = "6.0.1" -description = "YAML parser and emitter for Python" -optional = false -python-versions = ">=3.6" -files = [ - {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, - {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, - {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, - {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, - {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, - {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, - {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, - {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, - {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, - {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, - {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, - {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, - {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, - {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, - {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, - {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, - {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, - {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, - {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, - {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, - {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, - {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, - {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, - {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, - {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, - {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, - {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, - {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, - {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, -] - -[[package]] -name = "redis" -version = "4.6.0" -description = "Python client for Redis database and key-value store" -optional = false -python-versions = ">=3.7" -files = [ - {file = "redis-4.6.0-py3-none-any.whl", hash = "sha256:e2b03db868160ee4591de3cb90d40ebb50a90dd302138775937f6a42b7ed183c"}, - {file = "redis-4.6.0.tar.gz", hash = "sha256:585dc516b9eb042a619ef0a39c3d7d55fe81bdb4df09a52c9cdde0d07bf1aa7d"}, -] - -[package.dependencies] -async-timeout = {version = ">=4.0.2", markers = "python_full_version <= \"3.11.2\""} - -[package.extras] -hiredis = ["hiredis (>=1.0.0)"] -ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)"] - -[[package]] -name = "requests" -version = "2.32.3" -description = "Python HTTP for Humans." -optional = false -python-versions = ">=3.8" -files = [ - {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, - {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, -] - -[package.dependencies] -certifi = ">=2017.4.17" -charset-normalizer = ">=2,<4" -idna = ">=2.5,<4" -urllib3 = ">=1.21.1,<3" - -[package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] - -[[package]] -name = "rich" -version = "13.7.1" -description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" -optional = false -python-versions = ">=3.7.0" -files = [ - {file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"}, - {file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"}, -] - -[package.dependencies] -markdown-it-py = ">=2.2.0" -pygments = ">=2.13.0,<3.0.0" - -[package.extras] -jupyter = ["ipywidgets (>=7.5.1,<9)"] - -[[package]] -name = "ruff" -version = "0.5.0" -description = "An extremely fast Python linter and code formatter, written in Rust." -optional = false -python-versions = ">=3.7" -files = [ - {file = "ruff-0.5.0-py3-none-linux_armv6l.whl", hash = "sha256:ee770ea8ab38918f34e7560a597cc0a8c9a193aaa01bfbd879ef43cb06bd9c4c"}, - {file = "ruff-0.5.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:38f3b8327b3cb43474559d435f5fa65dacf723351c159ed0dc567f7ab735d1b6"}, - {file = "ruff-0.5.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7594f8df5404a5c5c8f64b8311169879f6cf42142da644c7e0ba3c3f14130370"}, - {file = "ruff-0.5.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:adc7012d6ec85032bc4e9065110df205752d64010bed5f958d25dbee9ce35de3"}, - {file = "ruff-0.5.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d505fb93b0fabef974b168d9b27c3960714d2ecda24b6ffa6a87ac432905ea38"}, - {file = "ruff-0.5.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9dc5cfd3558f14513ed0d5b70ce531e28ea81a8a3b1b07f0f48421a3d9e7d80a"}, - {file = "ruff-0.5.0-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:db3ca35265de239a1176d56a464b51557fce41095c37d6c406e658cf80bbb362"}, - {file = "ruff-0.5.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b1a321c4f68809fddd9b282fab6a8d8db796b270fff44722589a8b946925a2a8"}, - {file = "ruff-0.5.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2c4dfcd8d34b143916994b3876b63d53f56724c03f8c1a33a253b7b1e6bf2a7d"}, - {file = "ruff-0.5.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81e5facfc9f4a674c6a78c64d38becfbd5e4f739c31fcd9ce44c849f1fad9e4c"}, - {file = "ruff-0.5.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e589e27971c2a3efff3fadafb16e5aef7ff93250f0134ec4b52052b673cf988d"}, - {file = "ruff-0.5.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d2ffbc3715a52b037bcb0f6ff524a9367f642cdc5817944f6af5479bbb2eb50e"}, - {file = "ruff-0.5.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:cd096e23c6a4f9c819525a437fa0a99d1c67a1b6bb30948d46f33afbc53596cf"}, - {file = "ruff-0.5.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:46e193b36f2255729ad34a49c9a997d506e58f08555366b2108783b3064a0e1e"}, - {file = "ruff-0.5.0-py3-none-win32.whl", hash = "sha256:49141d267100f5ceff541b4e06552e98527870eafa1acc9dec9139c9ec5af64c"}, - {file = "ruff-0.5.0-py3-none-win_amd64.whl", hash = "sha256:e9118f60091047444c1b90952736ee7b1792910cab56e9b9a9ac20af94cd0440"}, - {file = "ruff-0.5.0-py3-none-win_arm64.whl", hash = "sha256:ed5c4df5c1fb4518abcb57725b576659542bdbe93366f4f329e8f398c4b71178"}, - {file = "ruff-0.5.0.tar.gz", hash = "sha256:eb641b5873492cf9bd45bc9c5ae5320648218e04386a5f0c264ad6ccce8226a1"}, -] - -[[package]] -name = "shellingham" -version = "1.5.4" -description = "Tool to Detect Surrounding Shell" -optional = false -python-versions = ">=3.7" -files = [ - {file = "shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686"}, - {file = "shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de"}, -] - -[[package]] -name = "six" -version = "1.16.0" -description = "Python 2 and 3 compatibility utilities" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" -files = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, -] - -[[package]] -name = "smmap" -version = "5.0.1" -description = "A pure Python implementation of a sliding window memory map manager" -optional = false -python-versions = ">=3.7" -files = [ - {file = "smmap-5.0.1-py3-none-any.whl", hash = "sha256:e6d8668fa5f93e706934a62d7b4db19c8d9eb8cf2adbb75ef1b675aa332b69da"}, - {file = "smmap-5.0.1.tar.gz", hash = "sha256:dceeb6c0028fdb6734471eb07c0cd2aae706ccaecab45965ee83f11c8d3b1f62"}, -] - -[[package]] -name = "sniffio" -version = "1.3.1" -description = "Sniff out which async library your code is running under" -optional = false -python-versions = ">=3.7" -files = [ - {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, - {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, -] - -[[package]] -name = "stack-data" -version = "0.6.3" -description = "Extract data from python stack frames and tracebacks for informative displays" -optional = false -python-versions = "*" -files = [ - {file = "stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695"}, - {file = "stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9"}, -] - -[package.dependencies] -asttokens = ">=2.1.0" -executing = ">=1.2.0" -pure-eval = "*" - -[package.extras] -tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] - -[[package]] -name = "structlog" -version = "22.3.0" -description = "Structured Logging for Python" -optional = false -python-versions = ">=3.7" -files = [ - {file = "structlog-22.3.0-py3-none-any.whl", hash = "sha256:b403f344f902b220648fa9f286a23c0cc5439a5844d271fec40562dbadbc70ad"}, - {file = "structlog-22.3.0.tar.gz", hash = "sha256:e7509391f215e4afb88b1b80fa3ea074be57a5a17d794bd436a5c949da023333"}, -] - -[package.extras] -dev = ["structlog[docs,tests,typing]"] -docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-mermaid", "twisted"] -tests = ["coverage[toml]", "freezegun (>=0.2.8)", "pretend", "pytest (>=6.0)", "pytest-asyncio (>=0.17)", "simplejson"] -typing = ["mypy", "rich", "twisted"] - -[[package]] -name = "toml" -version = "0.10.2" -description = "Python Library for Tom's Obvious, Minimal Language" -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" -files = [ - {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, - {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, -] - -[[package]] -name = "tomli" -version = "2.0.1" -description = "A lil' TOML parser" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, -] - -[[package]] -name = "tomlkit" -version = "0.12.5" -description = "Style preserving TOML library" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tomlkit-0.12.5-py3-none-any.whl", hash = "sha256:af914f5a9c59ed9d0762c7b64d3b5d5df007448eb9cd2edc8a46b1eafead172f"}, - {file = "tomlkit-0.12.5.tar.gz", hash = "sha256:eef34fba39834d4d6b73c9ba7f3e4d1c417a4e56f89a7e96e090dd0d24b8fb3c"}, -] - -[[package]] -name = "tqdm" -version = "4.66.5" -description = "Fast, Extensible Progress Meter" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tqdm-4.66.5-py3-none-any.whl", hash = "sha256:90279a3770753eafc9194a0364852159802111925aa30eb3f9d85b0e805ac7cd"}, - {file = "tqdm-4.66.5.tar.gz", hash = "sha256:e1020aef2e5096702d8a025ac7d16b1577279c9d63f8375b63083e9a5f0fcbad"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[package.extras] -dev = ["pytest (>=6)", "pytest-cov", "pytest-timeout", "pytest-xdist"] -notebook = ["ipywidgets (>=6)"] -slack = ["slack-sdk"] -telegram = ["requests"] - -[[package]] -name = "traitlets" -version = "5.14.3" -description = "Traitlets Python configuration system" -optional = false -python-versions = ">=3.8" -files = [ - {file = "traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f"}, - {file = "traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7"}, -] - -[package.extras] -docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] -test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<8.2)", "pytest-mock", "pytest-mypy-testing"] - -[[package]] -name = "typer" -version = "0.12.3" -description = "Typer, build great CLIs. Easy to code. Based on Python type hints." -optional = false -python-versions = ">=3.7" -files = [ - {file = "typer-0.12.3-py3-none-any.whl", hash = "sha256:070d7ca53f785acbccba8e7d28b08dcd88f79f1fbda035ade0aecec71ca5c914"}, - {file = "typer-0.12.3.tar.gz", hash = "sha256:49e73131481d804288ef62598d97a1ceef3058905aa536a1134f90891ba35482"}, -] - -[package.dependencies] -click = ">=8.0.0" -rich = ">=10.11.0" -shellingham = ">=1.3.0" -typing-extensions = ">=3.7.4.3" - -[[package]] -name = "typer-cli" -version = "0.12.3" -description = "Typer, build great CLIs. Easy to code. Based on Python type hints." -optional = false -python-versions = ">=3.7" -files = [ - {file = "typer_cli-0.12.3-py3-none-any.whl", hash = "sha256:33ee23585e3013804354e0db21b06880960056c6a52012bdaba62dbf6e9e46ed"}, - {file = "typer_cli-0.12.3.tar.gz", hash = "sha256:1f66684489ea15b08f613d76948c4f99d6b69c69de47010bbc0032d9de4d45b5"}, -] - -[package.dependencies] -typer = "0.12.3" - -[[package]] -name = "types-python-slugify" -version = "8.0.2.20240310" -description = "Typing stubs for python-slugify" -optional = false -python-versions = ">=3.8" -files = [ - {file = "types-python-slugify-8.0.2.20240310.tar.gz", hash = "sha256:5157b508c7fed587520c70d77f62aea0fafdc6620893c2ec8972f13a1faf5560"}, - {file = "types_python_slugify-8.0.2.20240310-py3-none-any.whl", hash = "sha256:0efec18b802c69ebd22dcee55c91afaeaa80e1e40ddd66ccabf69fd42ce87b74"}, -] - -[[package]] -name = "types-pyyaml" -version = "6.0.12.20240311" -description = "Typing stubs for PyYAML" -optional = false -python-versions = ">=3.8" -files = [ - {file = "types-PyYAML-6.0.12.20240311.tar.gz", hash = "sha256:a9e0f0f88dc835739b0c1ca51ee90d04ca2a897a71af79de9aec5f38cb0a5342"}, - {file = "types_PyYAML-6.0.12.20240311-py3-none-any.whl", hash = "sha256:b845b06a1c7e54b8e5b4c683043de0d9caf205e7434b3edc678ff2411979b8f6"}, -] - -[[package]] -name = "types-toml" -version = "0.10.8.20240310" -description = "Typing stubs for toml" -optional = false -python-versions = ">=3.8" -files = [ - {file = "types-toml-0.10.8.20240310.tar.gz", hash = "sha256:3d41501302972436a6b8b239c850b26689657e25281b48ff0ec06345b8830331"}, - {file = "types_toml-0.10.8.20240310-py3-none-any.whl", hash = "sha256:627b47775d25fa29977d9c70dc0cbab3f314f32c8d8d0c012f2ef5de7aaec05d"}, -] - -[[package]] -name = "types-ujson" -version = "5.10.0.20240515" -description = "Typing stubs for ujson" -optional = false -python-versions = ">=3.8" -files = [ - {file = "types-ujson-5.10.0.20240515.tar.gz", hash = "sha256:ceae7127f0dafe4af5dd0ecf98ee13e9d75951ef963b5c5a9b7ea92e0d71f0d7"}, - {file = "types_ujson-5.10.0.20240515-py3-none-any.whl", hash = "sha256:02bafc36b3a93d2511757a64ff88bd505e0a57fba08183a9150fbcfcb2015310"}, -] - -[[package]] -name = "typing-extensions" -version = "4.12.2" -description = "Backported and Experimental Type Hints for Python 3.8+" -optional = false -python-versions = ">=3.8" -files = [ - {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, - {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, -] - -[[package]] -name = "tzdata" -version = "2024.1" -description = "Provider of IANA time zone data" -optional = false -python-versions = ">=2" -files = [ - {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"}, - {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, -] - -[[package]] -name = "ujson" -version = "5.10.0" -description = "Ultra fast JSON encoder and decoder for Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "ujson-5.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2601aa9ecdbee1118a1c2065323bda35e2c5a2cf0797ef4522d485f9d3ef65bd"}, - {file = "ujson-5.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:348898dd702fc1c4f1051bc3aacbf894caa0927fe2c53e68679c073375f732cf"}, - {file = "ujson-5.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22cffecf73391e8abd65ef5f4e4dd523162a3399d5e84faa6aebbf9583df86d6"}, - {file = "ujson-5.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26b0e2d2366543c1bb4fbd457446f00b0187a2bddf93148ac2da07a53fe51569"}, - {file = "ujson-5.10.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:caf270c6dba1be7a41125cd1e4fc7ba384bf564650beef0df2dd21a00b7f5770"}, - {file = "ujson-5.10.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a245d59f2ffe750446292b0094244df163c3dc96b3ce152a2c837a44e7cda9d1"}, - {file = "ujson-5.10.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:94a87f6e151c5f483d7d54ceef83b45d3a9cca7a9cb453dbdbb3f5a6f64033f5"}, - {file = "ujson-5.10.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:29b443c4c0a113bcbb792c88bea67b675c7ca3ca80c3474784e08bba01c18d51"}, - {file = "ujson-5.10.0-cp310-cp310-win32.whl", hash = "sha256:c18610b9ccd2874950faf474692deee4223a994251bc0a083c114671b64e6518"}, - {file = "ujson-5.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:924f7318c31874d6bb44d9ee1900167ca32aa9b69389b98ecbde34c1698a250f"}, - {file = "ujson-5.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a5b366812c90e69d0f379a53648be10a5db38f9d4ad212b60af00bd4048d0f00"}, - {file = "ujson-5.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:502bf475781e8167f0f9d0e41cd32879d120a524b22358e7f205294224c71126"}, - {file = "ujson-5.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b91b5d0d9d283e085e821651184a647699430705b15bf274c7896f23fe9c9d8"}, - {file = "ujson-5.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:129e39af3a6d85b9c26d5577169c21d53821d8cf68e079060602e861c6e5da1b"}, - {file = "ujson-5.10.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f77b74475c462cb8b88680471193064d3e715c7c6074b1c8c412cb526466efe9"}, - {file = "ujson-5.10.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7ec0ca8c415e81aa4123501fee7f761abf4b7f386aad348501a26940beb1860f"}, - {file = "ujson-5.10.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ab13a2a9e0b2865a6c6db9271f4b46af1c7476bfd51af1f64585e919b7c07fd4"}, - {file = "ujson-5.10.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:57aaf98b92d72fc70886b5a0e1a1ca52c2320377360341715dd3933a18e827b1"}, - {file = "ujson-5.10.0-cp311-cp311-win32.whl", hash = "sha256:2987713a490ceb27edff77fb184ed09acdc565db700ee852823c3dc3cffe455f"}, - {file = "ujson-5.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:f00ea7e00447918ee0eff2422c4add4c5752b1b60e88fcb3c067d4a21049a720"}, - {file = "ujson-5.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:98ba15d8cbc481ce55695beee9f063189dce91a4b08bc1d03e7f0152cd4bbdd5"}, - {file = "ujson-5.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a9d2edbf1556e4f56e50fab7d8ff993dbad7f54bac68eacdd27a8f55f433578e"}, - {file = "ujson-5.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6627029ae4f52d0e1a2451768c2c37c0c814ffc04f796eb36244cf16b8e57043"}, - {file = "ujson-5.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8ccb77b3e40b151e20519c6ae6d89bfe3f4c14e8e210d910287f778368bb3d1"}, - {file = "ujson-5.10.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3caf9cd64abfeb11a3b661329085c5e167abbe15256b3b68cb5d914ba7396f3"}, - {file = "ujson-5.10.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6e32abdce572e3a8c3d02c886c704a38a1b015a1fb858004e03d20ca7cecbb21"}, - {file = "ujson-5.10.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a65b6af4d903103ee7b6f4f5b85f1bfd0c90ba4eeac6421aae436c9988aa64a2"}, - {file = "ujson-5.10.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:604a046d966457b6cdcacc5aa2ec5314f0e8c42bae52842c1e6fa02ea4bda42e"}, - {file = "ujson-5.10.0-cp312-cp312-win32.whl", hash = "sha256:6dea1c8b4fc921bf78a8ff00bbd2bfe166345f5536c510671bccececb187c80e"}, - {file = "ujson-5.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:38665e7d8290188b1e0d57d584eb8110951a9591363316dd41cf8686ab1d0abc"}, - {file = "ujson-5.10.0-cp313-cp313-macosx_10_9_x86_64.whl", hash = "sha256:618efd84dc1acbd6bff8eaa736bb6c074bfa8b8a98f55b61c38d4ca2c1f7f287"}, - {file = "ujson-5.10.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:38d5d36b4aedfe81dfe251f76c0467399d575d1395a1755de391e58985ab1c2e"}, - {file = "ujson-5.10.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67079b1f9fb29ed9a2914acf4ef6c02844b3153913eb735d4bf287ee1db6e557"}, - {file = "ujson-5.10.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7d0e0ceeb8fe2468c70ec0c37b439dd554e2aa539a8a56365fd761edb418988"}, - {file = "ujson-5.10.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:59e02cd37bc7c44d587a0ba45347cc815fb7a5fe48de16bf05caa5f7d0d2e816"}, - {file = "ujson-5.10.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2a890b706b64e0065f02577bf6d8ca3b66c11a5e81fb75d757233a38c07a1f20"}, - {file = "ujson-5.10.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:621e34b4632c740ecb491efc7f1fcb4f74b48ddb55e65221995e74e2d00bbff0"}, - {file = "ujson-5.10.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b9500e61fce0cfc86168b248104e954fead61f9be213087153d272e817ec7b4f"}, - {file = "ujson-5.10.0-cp313-cp313-win32.whl", hash = "sha256:4c4fc16f11ac1612f05b6f5781b384716719547e142cfd67b65d035bd85af165"}, - {file = "ujson-5.10.0-cp313-cp313-win_amd64.whl", hash = "sha256:4573fd1695932d4f619928fd09d5d03d917274381649ade4328091ceca175539"}, - {file = "ujson-5.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a984a3131da7f07563057db1c3020b1350a3e27a8ec46ccbfbf21e5928a43050"}, - {file = "ujson-5.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:73814cd1b9db6fc3270e9d8fe3b19f9f89e78ee9d71e8bd6c9a626aeaeaf16bd"}, - {file = "ujson-5.10.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61e1591ed9376e5eddda202ec229eddc56c612b61ac6ad07f96b91460bb6c2fb"}, - {file = "ujson-5.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2c75269f8205b2690db4572a4a36fe47cd1338e4368bc73a7a0e48789e2e35a"}, - {file = "ujson-5.10.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7223f41e5bf1f919cd8d073e35b229295aa8e0f7b5de07ed1c8fddac63a6bc5d"}, - {file = "ujson-5.10.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d4dc2fd6b3067c0782e7002ac3b38cf48608ee6366ff176bbd02cf969c9c20fe"}, - {file = "ujson-5.10.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:232cc85f8ee3c454c115455195a205074a56ff42608fd6b942aa4c378ac14dd7"}, - {file = "ujson-5.10.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:cc6139531f13148055d691e442e4bc6601f6dba1e6d521b1585d4788ab0bfad4"}, - {file = "ujson-5.10.0-cp38-cp38-win32.whl", hash = "sha256:e7ce306a42b6b93ca47ac4a3b96683ca554f6d35dd8adc5acfcd55096c8dfcb8"}, - {file = "ujson-5.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:e82d4bb2138ab05e18f089a83b6564fee28048771eb63cdecf4b9b549de8a2cc"}, - {file = "ujson-5.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dfef2814c6b3291c3c5f10065f745a1307d86019dbd7ea50e83504950136ed5b"}, - {file = "ujson-5.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4734ee0745d5928d0ba3a213647f1c4a74a2a28edc6d27b2d6d5bd9fa4319e27"}, - {file = "ujson-5.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d47ebb01bd865fdea43da56254a3930a413f0c5590372a1241514abae8aa7c76"}, - {file = "ujson-5.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dee5e97c2496874acbf1d3e37b521dd1f307349ed955e62d1d2f05382bc36dd5"}, - {file = "ujson-5.10.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7490655a2272a2d0b072ef16b0b58ee462f4973a8f6bbe64917ce5e0a256f9c0"}, - {file = "ujson-5.10.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ba17799fcddaddf5c1f75a4ba3fd6441f6a4f1e9173f8a786b42450851bd74f1"}, - {file = "ujson-5.10.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:2aff2985cef314f21d0fecc56027505804bc78802c0121343874741650a4d3d1"}, - {file = "ujson-5.10.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ad88ac75c432674d05b61184178635d44901eb749786c8eb08c102330e6e8996"}, - {file = "ujson-5.10.0-cp39-cp39-win32.whl", hash = "sha256:2544912a71da4ff8c4f7ab5606f947d7299971bdd25a45e008e467ca638d13c9"}, - {file = "ujson-5.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:3ff201d62b1b177a46f113bb43ad300b424b7847f9c5d38b1b4ad8f75d4a282a"}, - {file = "ujson-5.10.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5b6fee72fa77dc172a28f21693f64d93166534c263adb3f96c413ccc85ef6e64"}, - {file = "ujson-5.10.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:61d0af13a9af01d9f26d2331ce49bb5ac1fb9c814964018ac8df605b5422dcb3"}, - {file = "ujson-5.10.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ecb24f0bdd899d368b715c9e6664166cf694d1e57be73f17759573a6986dd95a"}, - {file = "ujson-5.10.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fbd8fd427f57a03cff3ad6574b5e299131585d9727c8c366da4624a9069ed746"}, - {file = "ujson-5.10.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:beeaf1c48e32f07d8820c705ff8e645f8afa690cca1544adba4ebfa067efdc88"}, - {file = "ujson-5.10.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:baed37ea46d756aca2955e99525cc02d9181de67f25515c468856c38d52b5f3b"}, - {file = "ujson-5.10.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7663960f08cd5a2bb152f5ee3992e1af7690a64c0e26d31ba7b3ff5b2ee66337"}, - {file = "ujson-5.10.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:d8640fb4072d36b08e95a3a380ba65779d356b2fee8696afeb7794cf0902d0a1"}, - {file = "ujson-5.10.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78778a3aa7aafb11e7ddca4e29f46bc5139131037ad628cc10936764282d6753"}, - {file = "ujson-5.10.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0111b27f2d5c820e7f2dbad7d48e3338c824e7ac4d2a12da3dc6061cc39c8e6"}, - {file = "ujson-5.10.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:c66962ca7565605b355a9ed478292da628b8f18c0f2793021ca4425abf8b01e5"}, - {file = "ujson-5.10.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ba43cc34cce49cf2d4bc76401a754a81202d8aa926d0e2b79f0ee258cb15d3a4"}, - {file = "ujson-5.10.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:ac56eb983edce27e7f51d05bc8dd820586c6e6be1c5216a6809b0c668bb312b8"}, - {file = "ujson-5.10.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f44bd4b23a0e723bf8b10628288c2c7c335161d6840013d4d5de20e48551773b"}, - {file = "ujson-5.10.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c10f4654e5326ec14a46bcdeb2b685d4ada6911050aa8baaf3501e57024b804"}, - {file = "ujson-5.10.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0de4971a89a762398006e844ae394bd46991f7c385d7a6a3b93ba229e6dac17e"}, - {file = "ujson-5.10.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:e1402f0564a97d2a52310ae10a64d25bcef94f8dd643fcf5d310219d915484f7"}, - {file = "ujson-5.10.0.tar.gz", hash = "sha256:b3cd8f3c5d8c7738257f1018880444f7b7d9b66232c64649f562d7ba86ad4bc1"}, -] - -[[package]] -name = "urllib3" -version = "2.2.2" -description = "HTTP library with thread-safe connection pooling, file post, and more." -optional = false -python-versions = ">=3.8" -files = [ - {file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"}, - {file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"}, -] - -[package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] -h2 = ["h2 (>=4,<5)"] -socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] -zstd = ["zstandard (>=0.18.0)"] - -[[package]] -name = "virtualenv" -version = "20.26.2" -description = "Virtual Python Environment builder" -optional = false -python-versions = ">=3.7" -files = [ - {file = "virtualenv-20.26.2-py3-none-any.whl", hash = "sha256:a624db5e94f01ad993d476b9ee5346fdf7b9de43ccaee0e0197012dc838a0e9b"}, - {file = "virtualenv-20.26.2.tar.gz", hash = "sha256:82bf0f4eebbb78d36ddaee0283d43fe5736b53880b8a8cdcd37390a07ac3741c"}, -] - -[package.dependencies] -distlib = ">=0.3.7,<1" -filelock = ">=3.12.2,<4" -platformdirs = ">=3.9.1,<5" - -[package.extras] -docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] -test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] - -[[package]] -name = "wcwidth" -version = "0.2.13" -description = "Measures the displayed width of unicode strings in a terminal" -optional = false -python-versions = "*" -files = [ - {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, - {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, -] - -[[package]] -name = "yamllint" -version = "1.35.1" -description = "A linter for YAML files." -optional = false -python-versions = ">=3.8" -files = [ - {file = "yamllint-1.35.1-py3-none-any.whl", hash = "sha256:2e16e504bb129ff515b37823b472750b36b6de07963bd74b307341ef5ad8bdc3"}, - {file = "yamllint-1.35.1.tar.gz", hash = "sha256:7a003809f88324fd2c877734f2d575ee7881dd9043360657cc8049c809eba6cd"}, -] - -[package.dependencies] -pathspec = ">=0.5.3" -pyyaml = "*" - -[package.extras] -dev = ["doc8", "flake8", "flake8-import-order", "rstcheck[sphinx]", "sphinx"] - -[metadata] -lock-version = "2.0" -python-versions = ">=3.9, < 3.13" -content-hash = "83deb599bdfd22feb32ca5971f96380738a59ac5df19f4e11c6688d7080b3616" diff --git a/sync/potenda/potenda/__init__.py b/sync/potenda/potenda/__init__.py deleted file mode 100644 index efd25e81a0..0000000000 --- a/sync/potenda/potenda/__init__.py +++ /dev/null @@ -1,80 +0,0 @@ -from typing import List, Optional - -from tqdm import tqdm - -from diffsync import DiffSync -from diffsync.diff import Diff -from diffsync.enum import DiffSyncFlags -from diffsync.logging import enable_console_logging -from infrahub_sync import SyncInstance - - -class Potenda: - def __init__( - self, - source: DiffSync, - destination: DiffSync, - config: SyncInstance, - top_level: List[str], - partition=None, - show_progress: Optional[bool] = False, - ): - self.top_level = top_level - - self.config = config - - self.source = source - self.destination = destination - - self.source.top_level = top_level - self.destination.top_level = top_level - - self.partition = partition - self.progress_bar = None - self.show_progress = show_progress - enable_console_logging(verbosity=1) - self.flags = DiffSyncFlags.SKIP_UNMATCHED_DST - - def _print_callback(self, stage: str, elements_processed: int, total_models: int): - """Callback for DiffSync using tqdm""" - if self.show_progress: - if self.progress_bar is None: - self.progress_bar = tqdm(total=total_models, desc=stage, unit="models") - - self.progress_bar.n = elements_processed - self.progress_bar.refresh() - - if elements_processed == total_models: - self.progress_bar.close() - self.progress_bar = None - - def source_load(self): - try: - print(f"Load: Importing data from {self.source}") - self.source.load() - except Exception as exc: - raise ValueError(f"An error occurred while loading {self.source}: {str(exc)}") from exc - - def destination_load(self): - try: - print(f"Load: Importing data from {self.destination}") - self.destination.load() - except Exception as exc: - raise ValueError(f"An error occurred while loading {self.destination}: {str(exc)}") from exc - - def load(self): - try: - self.source_load() - self.destination_load() - except Exception as exc: - raise ValueError(f"An error occurred while loading the sync: {str(exc)}") from exc - - def diff(self) -> Diff: - print(f"Diff: Comparing data from {self.source} to {self.destination}") - self.progress_bar = None - return self.destination.diff_from(self.source, flags=self.flags, callback=self._print_callback) - - def sync(self, diff: Optional[Diff] = None): - print(f"Sync: Importing data from {self.source} to {self.destination} based on Diff") - self.progress_bar = None - return self.destination.sync_from(self.source, diff=diff, flags=self.flags, callback=self._print_callback) diff --git a/sync/pyproject.toml b/sync/pyproject.toml deleted file mode 100644 index 9437075242..0000000000 --- a/sync/pyproject.toml +++ /dev/null @@ -1,238 +0,0 @@ -[tool.poetry] -name = "infrahub-sync" -version = "0.5.0" -description = "Infrahub-Sync is a versatile Python package that synchronizes data between a source and a destination system" -authors = ["OpsMill "] -readme = "README.md" -license = "Apache-2.0" -homepage = "https://opsmill.com" -repository = "https://github.com/opsmill/infrahub" -documentation = "https://docs.infrahub.app/integrations/sync/" -packages = [ - { include = "infrahub_sync", from = "infrahub-sync"}, - { include = "potenda", from = "potenda"}, -] -classifiers = [ - "Intended Audience :: Developers", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", -] - -[tool.poetry.dependencies] -python = ">=3.9, < 3.13" -infrahub-sdk = {version = "^0,>=0.11", extras = ["all"]} -structlog = "^22.3.0" -diffsync = { version = ">=1.10,<2.0 || >=2.0", extras = ["redis"] } -netutils = "^1.9.1" -tqdm = "^4.66.5" - -[tool.poetry.group.dev.dependencies] -pytest = "*" -pytest-clarity = "^1.0.1" -pytest-cov = "^4.0.0" -pytest-httpx = "*" -yamllint = "*" -pylint = "*" -mypy = "*" -ipython = "*" -pytest-asyncio = "*" -requests = "*" -pre-commit = "^2.20.0" -types-toml = "*" -types-ujson = "*" -types-pyyaml = "*" -typer-cli = "*" -ruff = "0.5.0" -pytest-xdist = "^3.3.1" -types-python-slugify = "^8.0.0.3" - -[tool.poetry.scripts] -infrahub-sync = "infrahub_sync.cli:app" - -[tool.coverage.run] -branch = true - -[tool.coverage.report] -exclude_lines = [ - "if TYPE_CHECKING:", - "raise NotImplementedError()" -] - -[tool.pylint.general] -extension-pkg-whitelist = [ - "pydantic", - "ujson", -] - -[tool.pylint.format] -disable = "logging-fstring-interpolation" - -[tool.pylint.basic] -# No docstrings required for private methods (Pylint default), or for test_ functions. -no-docstring-rgx="^(_|test_)" - -[tool.pylint.messages_control] -# Line length is enforced by Black, so pylint doesn't need to check it. -# Pylint and Black disagree about how to format multi-line arrays; Black wins. -# assigning-non-slot,no-member,unsupported-membership-test,unsubscriptable-object,unsupported-assignment-operation,not-an-iterable -# are disabled because of our move to pydantic 2, pylint does not seem to respect the type hint for pydantic 2 model fields. -disable = """, - line-too-long, - missing-module-docstring, - missing-function-docstring, - missing-class-docstring, - consider-using-from-import, - invalid-name, - too-many-arguments, - too-many-locals, - keyword-arg-before-vararg, - too-few-public-methods, - too-many-instance-attributes, - fixme, - consider-using-f-string, - protected-access, - import-self, - wrong-import-order, - assigning-non-slot, - no-member, - unsupported-membership-test, - unsubscriptable-object, - unsupported-assignment-operation, - not-an-iterable, - multiple-statements, - """ - -[tool.pylint.miscellaneous] -notes = """, - FIXME, - XXX, - """ - -[tool.pylint.similarities] -min-similarity-lines = 20 - -[tool.pytest.ini_options] -asyncio_mode = "auto" -testpaths = [ - "tests" -] -filterwarnings = [ - "ignore:Module already imported so cannot be rewritten", - "ignore:the imp module is deprecated", - "ignore:Deprecated call to", -] -addopts = "-vs --cov-report term-missing --cov-report xml --dist loadscope" - -[tool.mypy] -pretty = true -ignore_missing_imports = true -disallow_untyped_defs = true - -[tool.ruff] -line-length = 120 - -exclude = [ - ".git", - ".tox", - ".venv", - "env", - "_build", - "build", - "dist", - "examples", -] - - -[tool.ruff.lint] -preview = true - -task-tags = [ - "FIXME", - "TODO", - "XXX", -] - -select = [ - "ANN", # flake8-annotations - "ASYNC", # flake8-async - "B", # flake8-bugbear - "C4", # flake8-comprehensions - "C90", # mccabe complexity - "DJ", # flake8-django - "DTZ", # flake8-datetimez - "E", # pycodestyle errors - "EXE", # flake8-executable - "F", # pyflakes - "I", # isort-like checks - "ICN", # flake8-import-conventions - "INP", # flake8-no-pep420 - "N", # pep8-naming - "PERF", # Perflint - "PIE", # flake8-pie - "PL", # pylint - "PTH", # flake8-use-pathlib - "PYI", # flake8-pyi - "Q", # flake8-quotes - "RET", # flake8-return - "S", # flake8-bandit - "TCH", # flake8-type-checking - "T10", # flake8-debugger - "UP", # pyupgrade - "W", # pycodestyle warnings - "YTT", # flake8-2020 -] - -ignore = [ - -################################################################################################## -# The ignored rules below should be removed once the code has been updated, they are included # -# like this so that we can reactivate them one by one. Alternatively ignored after further # -# investigation if they are deemed to not make sense. # -################################################################################################## - "ANN001", # Missing type annotation for function argument - "ANN002", # Missing type annotation for `*args` - "ANN003", # Missing type annotation for `**kwargs` - "ANN201", # Missing return type annotation for public function - "ANN202", # Missing return type annotation for private function - "ANN204", # Missing return type annotation for special method - "ANN206", # Missing return type annotation for classmethod - "ANN401", # Dynamically typed expressions (typing.Any) are disallowed - "B904", # Within an `except` clause, raise exceptions with `raise ... from err` or `raise ... from None` to distinguish them from errors in exception handling - "C416", # Unnecessary `list` comprehension (rewrite using `list()`) - "INP001", # File is part of an implicit namespace package. Add an `__init__.py`. - "PERF401", # Use a list comprehension to create a transformed list - "PLR0912", # Too many branches - "PLR0913", # Too many arguments in function definition - "PLR0917", # Too many positional arguments - "PLR1702", # Too many nested blocks - "PLR6301", # Method could be a function, class method, or static method - "RET504", # Unnecessary assignment to `ptd` before `return` statement - "S701", # By default, jinja2 sets `autoescape` to `False`. Consider using `autoescape=True` -] - -#https://docs.astral.sh/ruff/formatter/black/ -[tool.ruff.format] -quote-style = "double" -indent-style = "space" -skip-magic-trailing-comma = false -line-ending = "auto" - -[tool.ruff.lint.isort] -known-first-party = ["diffsync", "infrahub_sync", "potenda"] - -[tool.ruff.lint.pycodestyle] -max-line-length = 150 - -[tool.ruff.lint.mccabe] -# Target max-complexity=10 -max-complexity = 17 - -[tool.ruff.lint.per-file-ignores] - - -[build-system] -requires = ["poetry-core"] -build-backend = "poetry.core.masonry.api" diff --git a/tasks/__init__.py b/tasks/__init__.py index b901182e54..daf9233883 100644 --- a/tasks/__init__.py +++ b/tasks/__init__.py @@ -2,7 +2,8 @@ from invoke import Collection, Context, task -from . import backend, demo, dev, docs, main, performance, schema, sdk, sync +from . import backend, demo, dev, docs, main, performance, schema, sdk +from .utils import ESCAPED_REPO_PATH ns = Collection() ns.add_collection(sdk) @@ -13,11 +14,10 @@ ns.add_collection(demo) ns.add_collection(main) ns.add_collection(schema) -ns.add_collection(sync) @task -def yamllint(context: Context): +def yamllint(context: Context) -> None: """This will run yamllint to validate formatting of all yaml files.""" exec_cmd = "yamllint -s ." @@ -25,21 +25,27 @@ def yamllint(context: Context): @task(name="format") -def format_all(context: Context): +def format_all(context: Context) -> None: main.format_all(context) - sdk.format_all(context) backend.format_all(context) - sync.format_all(context) @task(name="lint") -def lint_all(context: Context): +def lint_all(context: Context) -> None: yamllint(context) - sdk.lint(context) backend.lint(context) - sync.lint(context) + + +@task +def pull(context: Context) -> None: + """Pull the latest changes from Github and update the submodule to the proper commit.""" + commands = ["git pull", "git submodule update"] + with context.cd(ESCAPED_REPO_PATH): + for command in commands: + context.run(command, pty=True) ns.add_task(format_all) ns.add_task(lint_all) ns.add_task(yamllint) +ns.add_task(pull) diff --git a/tasks/backend.py b/tasks/backend.py index e5fd07df8b..7e3cfe67a5 100644 --- a/tasks/backend.py +++ b/tasks/backend.py @@ -2,6 +2,7 @@ from typing import Any, Optional from invoke import Context, task +from invoke.runners import Result from .shared import ( BUILD_NAME, @@ -25,7 +26,7 @@ # ---------------------------------------------------------------------------- -def _format_ruff(context: Context): +def _format_ruff(context: Context) -> None: """Run ruff to format all Python files.""" print(f" - [{NAMESPACE}] Format code with ruff") @@ -36,7 +37,7 @@ def _format_ruff(context: Context): @task(name="format") -def format_all(context: Context): +def format_all(context: Context) -> None: """This will run all formatter.""" _format_ruff(context) @@ -48,11 +49,11 @@ def format_all(context: Context): # Testing tasks # ---------------------------------------------------------------------------- @task -def ruff(context: Context, docker: bool = False): +def ruff(context: Context, docker: bool = False) -> None: """Run ruff to check that Python files adherence to black standards.""" print(f" - [{NAMESPACE}] Check code with ruff") - exec_cmd = f"ruff check --diff {MAIN_DIRECTORY} --config {REPO_BASE}/pyproject.toml" + exec_cmd = f"poetry run ruff check --diff {MAIN_DIRECTORY} --config {REPO_BASE}/pyproject.toml" if docker: compose_files_cmd = build_test_compose_files_cmd(database=False) @@ -64,11 +65,11 @@ def ruff(context: Context, docker: bool = False): @task -def mypy(context: Context, docker: bool = False): +def mypy(context: Context, docker: bool = False) -> None: """This will run mypy for the specified name and Python version.""" print(f" - [{NAMESPACE}] Check code with mypy") - exec_cmd = f"mypy --show-error-codes {MAIN_DIRECTORY}" + exec_cmd = f"poetry run mypy --show-error-codes {MAIN_DIRECTORY}" if docker: compose_files_cmd = build_test_compose_files_cmd(database=False) @@ -80,11 +81,11 @@ def mypy(context: Context, docker: bool = False): @task -def pylint(context: Context, docker: bool = False): +def pylint(context: Context, docker: bool = False) -> None: """This will run pylint for the specified name and Python version.""" print(f" - [{NAMESPACE}] Check code with pylint") - exec_cmd = f"pylint --ignore-paths {MAIN_DIRECTORY}/tests {MAIN_DIRECTORY}" + exec_cmd = f"poetry run pylint --ignore-paths {MAIN_DIRECTORY}/tests {MAIN_DIRECTORY}" if docker: compose_files_cmd = build_test_compose_files_cmd(database=False) @@ -96,7 +97,7 @@ def pylint(context: Context, docker: bool = False): @task -def lint(context: Context, docker: bool = False): +def lint(context: Context, docker: bool = False) -> None: """This will run all linter.""" ruff(context, docker=docker) mypy(context, docker=docker) @@ -106,19 +107,17 @@ def lint(context: Context, docker: bool = False): @task(optional=["database"]) -def test_unit(context: Context, database: str = INFRAHUB_DATABASE): +def test_unit(context: Context, database: str = INFRAHUB_DATABASE) -> Optional[Result]: with context.cd(ESCAPED_REPO_PATH): - compose_files_cmd = build_test_compose_files_cmd(database=database) - base_cmd = f"{get_env_vars(context)} docker compose {compose_files_cmd} -p {BUILD_NAME} run {build_test_envs()} infrahub-test" - exec_cmd = f"pytest -n {NBR_WORKERS} -v --cov=infrahub {MAIN_DIRECTORY}/tests/unit" + exec_cmd = f"poetry run pytest -n {NBR_WORKERS} -v --cov=infrahub {MAIN_DIRECTORY}/tests/unit" if database == "neo4j": exec_cmd += " --neo4j" - print(f"{base_cmd} {exec_cmd}") - return execute_command(context=context, command=f"{base_cmd} {exec_cmd}") + print(f"{exec_cmd}") + return execute_command(context=context, command=f"{exec_cmd}") @task(optional=["database"]) -def test_core(context: Context, database: str = INFRAHUB_DATABASE): +def test_core(context: Context, database: str = INFRAHUB_DATABASE) -> Optional[Result]: with context.cd(ESCAPED_REPO_PATH): compose_files_cmd = build_test_compose_files_cmd(database=database) base_cmd = f"{get_env_vars(context)} docker compose {compose_files_cmd} -p {BUILD_NAME} run {build_test_envs()} infrahub-test" @@ -130,19 +129,19 @@ def test_core(context: Context, database: str = INFRAHUB_DATABASE): @task(optional=["database"]) -def test_integration(context: Context, database: str = INFRAHUB_DATABASE): +def test_integration(context: Context, database: str = INFRAHUB_DATABASE) -> Optional[Result]: with context.cd(ESCAPED_REPO_PATH): - compose_files_cmd = build_test_compose_files_cmd(database=database) - base_cmd = f"{get_env_vars(context)} docker compose {compose_files_cmd} -p {BUILD_NAME} run {build_test_envs()} infrahub-test" - exec_cmd = f"pytest -n {NBR_WORKERS} -v --cov=infrahub {MAIN_DIRECTORY}/tests/integration" + exec_cmd = f"poetry run pytest -n {NBR_WORKERS} -v --cov=infrahub {MAIN_DIRECTORY}/tests/integration" if database == "neo4j": exec_cmd += " --neo4j" - print(f"{base_cmd} {exec_cmd}") - return execute_command(context=context, command=f"{base_cmd} {exec_cmd}") + print(f"{exec_cmd=}") + return execute_command(context=context, command=f"{exec_cmd}") @task -def test_scale_env_start(context: Context, database: str = INFRAHUB_DATABASE, gunicorn_workers: int = 4): +def test_scale_env_start( + context: Context, database: str = INFRAHUB_DATABASE, gunicorn_workers: int = 4 +) -> Optional[Result]: with context.cd(ESCAPED_REPO_PATH): compose_files_cmd = build_test_scale_compose_files_cmd(database=database) command = f"{get_env_vars(context)} GUNICORN_WORKERS={gunicorn_workers} docker compose {compose_files_cmd} -p {BUILD_NAME} up -d" @@ -150,7 +149,7 @@ def test_scale_env_start(context: Context, database: str = INFRAHUB_DATABASE, gu @task -def test_scale_env_destroy(context: Context, database: str = INFRAHUB_DATABASE): +def test_scale_env_destroy(context: Context, database: str = INFRAHUB_DATABASE) -> Optional[Result]: with context.cd(ESCAPED_REPO_PATH): compose_files_cmd = build_test_scale_compose_files_cmd(database=database) command = f"{get_env_vars(context)} docker compose {compose_files_cmd} -p {BUILD_NAME} down --remove-orphans --volumes" @@ -161,13 +160,13 @@ def test_scale_env_destroy(context: Context, database: str = INFRAHUB_DATABASE): def test_scale( context: Context, schema: Path = f"{ESCAPED_REPO_PATH}/backend/tests/scale/schema.yml", - stager: str = None, - amount: int = None, - test: str = None, - attrs: int = None, - rels: int = None, - changes: int = None, -): + stager: Optional[str] = None, + amount: Optional[int] = None, + test: Optional[str] = None, + attrs: Optional[int] = None, + rels: Optional[int] = None, + changes: Optional[int] = None, +) -> Optional[Result]: args = [] if stager: args.extend(["--stager", stager]) @@ -198,7 +197,7 @@ def test_scale( @task(default=True) -def format_and_lint(context: Context): +def format_and_lint(context: Context) -> None: format_all(context) lint(context) @@ -209,14 +208,14 @@ def format_and_lint(context: Context): @task -def generate(context: Context): +def generate(context: Context) -> None: """Generate internal backend models.""" _generate_schemas(context=context) _generate_protocols(context=context) @task -def validate_generated(context: Context, docker: bool = False): +def validate_generated(context: Context, docker: bool = False) -> None: """Validate that the generated documentation is committed to Git.""" _generate_schemas(context=context) @@ -230,7 +229,7 @@ def validate_generated(context: Context, docker: bool = False): context.run(exec_cmd) -def _generate_schemas(context: Context): +def _generate_schemas(context: Context) -> None: from jinja2 import Environment, FileSystemLoader, StrictUndefined from infrahub.core.schema.definitions.internal import ( @@ -321,7 +320,7 @@ def _sort_and_filter_models( return sorted(filtered, key=lambda k: (k["namespace"].lower(), k["name"].lower())) -def _generate_protocols(context: Context): +def _generate_protocols(context: Context) -> None: from jinja2 import Environment, FileSystemLoader, StrictUndefined from infrahub.core.schema.definitions.core import core_models diff --git a/tasks/container_ops.py b/tasks/container_ops.py index 859231a1ec..024e4158c3 100644 --- a/tasks/container_ops.py +++ b/tasks/container_ops.py @@ -1,9 +1,19 @@ from __future__ import annotations import sys -from typing import TYPE_CHECKING, Optional - -from .shared import AVAILABLE_SERVICES, BUILD_NAME, build_compose_files_cmd, execute_command, get_env_vars +from typing import TYPE_CHECKING + +from .shared import ( + AVAILABLE_SERVICES, + BUILD_NAME, + SERVICE_SERVER_NAME, + SERVICE_WORKER_NAME, + Namespace, + build_compose_files_cmd, + execute_command, + get_compose_cmd, + get_env_vars, +) from .utils import ESCAPED_REPO_PATH if TYPE_CHECKING: @@ -15,8 +25,8 @@ def build_images( python_ver: str, nocache: bool, database: str, - namespace: str, - service: Optional[str] = None, + namespace: Namespace, + service: str | None = None, ) -> None: if service and service not in AVAILABLE_SERVICES: sys.exit(f"{service} is not a valid service ({AVAILABLE_SERVICES})") @@ -25,7 +35,8 @@ def build_images( with context.cd(ESCAPED_REPO_PATH): compose_files_cmd = build_compose_files_cmd(database=database, namespace=namespace) - base_cmd = f"{get_env_vars(context, namespace=namespace)} docker compose {compose_files_cmd} -p {BUILD_NAME}" + compose_cmd = get_compose_cmd(namespace=namespace) + base_cmd = f"{get_env_vars(context, namespace=namespace)} {compose_cmd} {compose_files_cmd} -p {BUILD_NAME}" print(f"base_cmd={base_cmd}") exec_cmd = f"build --build-arg PYTHON_VER={python_ver}" print(f"exec_cmd={exec_cmd}") @@ -41,74 +52,78 @@ def build_images( def destroy_environment( context: Context, database: str, - namespace: str, + namespace: Namespace, ) -> None: with context.cd(ESCAPED_REPO_PATH): compose_files_cmd = build_compose_files_cmd(database=database, namespace=namespace) - - command = f"{get_env_vars(context)} docker compose {compose_files_cmd} -p {BUILD_NAME} down --remove-orphans --volumes --timeout 1" + compose_cmd = get_compose_cmd(namespace=namespace) + command = f"{get_env_vars(context)} {compose_cmd} {compose_files_cmd} -p {BUILD_NAME} down --remove-orphans --volumes --timeout 1" execute_command(context=context, command=command) -def pull_images(context: Context, database: str, namespace: str) -> None: +def pull_images(context: Context, database: str, namespace: Namespace) -> None: with context.cd(ESCAPED_REPO_PATH): compose_files_cmd = build_compose_files_cmd(database=database, namespace=namespace) - + compose_cmd = get_compose_cmd(namespace=namespace) for service in AVAILABLE_SERVICES: - if "infrahub" in service: + if service in [SERVICE_SERVER_NAME, SERVICE_WORKER_NAME]: continue - command = f"{get_env_vars(context, namespace=namespace)} docker compose {compose_files_cmd} -p {BUILD_NAME} pull {service}" + command = f"{get_env_vars(context, namespace=namespace)} {compose_cmd} {compose_files_cmd} -p {BUILD_NAME} pull {service}" execute_command(context=context, command=command) -def restart_services(context: Context, database: str, namespace: str) -> None: +def restart_services(context: Context, database: str, namespace: Namespace) -> None: with context.cd(ESCAPED_REPO_PATH): compose_files_cmd = build_compose_files_cmd(database=database, namespace=namespace) - base_cmd = f"{get_env_vars(context, namespace=namespace)} docker compose {compose_files_cmd} -p {BUILD_NAME}" + compose_cmd = get_compose_cmd(namespace=namespace) + base_cmd = f"{get_env_vars(context, namespace=namespace)} {compose_cmd} {compose_files_cmd} -p {BUILD_NAME}" - execute_command(context=context, command=f"{base_cmd} restart infrahub-server") - execute_command(context=context, command=f"{base_cmd} restart infrahub-git") + execute_command(context=context, command=f"{base_cmd} restart {SERVICE_SERVER_NAME}") + execute_command(context=context, command=f"{base_cmd} restart {SERVICE_WORKER_NAME}") -def show_service_status(context: Context, database: str, namespace: str) -> None: +def show_service_status(context: Context, database: str, namespace: Namespace) -> None: with context.cd(ESCAPED_REPO_PATH): compose_files_cmd = build_compose_files_cmd(database=database, namespace=namespace) - command = f"{get_env_vars(context, namespace=namespace)} docker compose {compose_files_cmd} -p {BUILD_NAME} ps" + compose_cmd = get_compose_cmd(namespace=namespace) + command = f"{get_env_vars(context, namespace=namespace)} {compose_cmd} {compose_files_cmd} -p {BUILD_NAME} ps" execute_command(context=context, command=command) -def start_services(context: Context, database: str, namespace: str, wait: bool) -> None: +def start_services(context: Context, database: str, namespace: Namespace, wait: bool) -> None: with context.cd(ESCAPED_REPO_PATH): compose_files_cmd = build_compose_files_cmd(database=database, namespace=namespace) + compose_cmd = get_compose_cmd(namespace=namespace) should_wait = " --wait" if wait else "" - command = f"{get_env_vars(context, namespace=namespace)} docker compose {compose_files_cmd} -p {BUILD_NAME} up -d{should_wait}" + command = f"{get_env_vars(context, namespace=namespace)} {compose_cmd} {compose_files_cmd} -p {BUILD_NAME} up -d{should_wait}" execute_command(context=context, command=command) -def stop_services(context: Context, database: str, namespace: str) -> None: +def stop_services(context: Context, database: str, namespace: Namespace) -> None: with context.cd(ESCAPED_REPO_PATH): compose_files_cmd = build_compose_files_cmd(database=database, namespace=namespace) - command = ( - f"{get_env_vars(context, namespace=namespace)} docker compose {compose_files_cmd} -p {BUILD_NAME} down" - ) + compose_cmd = get_compose_cmd(namespace=namespace) + command = f"{get_env_vars(context, namespace=namespace)} {compose_cmd} {compose_files_cmd} -p {BUILD_NAME} down" execute_command(context=context, command=command) -def migrate_database(context: Context, database: str, namespace: str) -> None: +def migrate_database(context: Context, database: str, namespace: Namespace) -> None: """Apply the latest database migrations.""" with context.cd(ESCAPED_REPO_PATH): compose_files_cmd = build_compose_files_cmd(database=database, namespace=namespace) - base_cmd = f"{get_env_vars(context, namespace=namespace)} docker compose {compose_files_cmd} -p {BUILD_NAME}" - command = f"{base_cmd} run infrahub-server infrahub db migrate" + compose_cmd = get_compose_cmd(namespace=namespace) + base_cmd = f"{get_env_vars(context, namespace=namespace)} {compose_cmd} {compose_files_cmd} -p {BUILD_NAME}" + command = f"{base_cmd} run {SERVICE_SERVER_NAME} infrahub db migrate" execute_command(context=context, command=command) -def update_core_schema(context: Context, database: str, namespace: str, debug: bool = False) -> None: +def update_core_schema(context: Context, database: str, namespace: Namespace, debug: bool = False) -> None: """Update the core schema.""" with context.cd(ESCAPED_REPO_PATH): compose_files_cmd = build_compose_files_cmd(database=database, namespace=namespace) - base_cmd = f"{get_env_vars(context, namespace=namespace)} docker compose {compose_files_cmd} -p {BUILD_NAME}" - command = f"{base_cmd} run infrahub-server infrahub db update-core-schema" + compose_cmd = get_compose_cmd(namespace=namespace) + base_cmd = f"{get_env_vars(context, namespace=namespace)} {compose_cmd} {compose_files_cmd} -p {BUILD_NAME}" + command = f"{base_cmd} run {SERVICE_SERVER_NAME} infrahub db update-core-schema" if debug: command += " --debug" execute_command(context=context, command=command) diff --git a/tasks/demo.py b/tasks/demo.py index 94231b2337..6dfa94b464 100644 --- a/tasks/demo.py +++ b/tasks/demo.py @@ -16,18 +16,22 @@ stop_services, update_core_schema, ) -from .infra_ops import load_infrastructure_data, load_infrastructure_schema +from .infra_ops import load_infrastructure_data, load_infrastructure_menu, load_infrastructure_schema from .shared import ( BUILD_NAME, INFRAHUB_DATABASE, PYTHON_VER, + SERVICE_SERVER_NAME, + Namespace, build_compose_files_cmd, execute_command, get_env_vars, ) from .utils import ESCAPED_REPO_PATH -NAMESPACE = "DEMO" +NAMESPACE = Namespace.DEFAULT + +SERVICE_WORKER_NAME = "infrahub-git" @task(optional=["database"]) @@ -37,7 +41,7 @@ def build( python_ver: str = PYTHON_VER, nocache: bool = False, database: str = INFRAHUB_DATABASE, -): +) -> None: """Build an image with the provided name and python version. Args: @@ -51,7 +55,7 @@ def build( @task(optional=["database"]) -def pull(context: Context, database: str = INFRAHUB_DATABASE): +def pull(context: Context, database: str = INFRAHUB_DATABASE) -> None: """Pull external containers from registry.""" pull_images(context=context, database=database, namespace=NAMESPACE) @@ -62,68 +66,79 @@ def pull(context: Context, database: str = INFRAHUB_DATABASE): @task(optional=["database"]) -def start(context: Context, database: str = INFRAHUB_DATABASE, wait: bool = False): +def start(context: Context, database: str = INFRAHUB_DATABASE, wait: bool = False) -> None: """Start a local instance of Infrahub within docker compose.""" start_services(context=context, database=database, namespace=NAMESPACE, wait=wait) @task(optional=["database"]) -def restart(context: Context, database: str = INFRAHUB_DATABASE): - """Restart Infrahub API Server and Git Agent within docker compose.""" +def restart(context: Context, database: str = INFRAHUB_DATABASE) -> None: + """Restart Infrahub API Server and Task worker within docker compose.""" restart_services(context=context, database=database, namespace=NAMESPACE) @task(optional=["database"]) -def stop(context: Context, database: str = INFRAHUB_DATABASE): +def stop(context: Context, database: str = INFRAHUB_DATABASE) -> None: """Stop the running instance of Infrahub.""" stop_services(context=context, database=database, namespace=NAMESPACE) @task(optional=["database"]) -def destroy(context: Context, database: str = INFRAHUB_DATABASE): +def destroy(context: Context, database: str = INFRAHUB_DATABASE) -> None: """Destroy all containers and volumes.""" destroy_environment(context=context, database=database, namespace=NAMESPACE) @task(optional=["database"]) -def migrate(context: Context, database: str = INFRAHUB_DATABASE): +def migrate(context: Context, database: str = INFRAHUB_DATABASE) -> None: """Apply the latest database migrations.""" migrate_database(context=context, database=database, namespace=NAMESPACE) update_core_schema(context=context, database=database, namespace=NAMESPACE) @task(optional=["database"]) -def cli_server(context: Context, database: str = INFRAHUB_DATABASE): +def cli_server(context: Context, database: str = INFRAHUB_DATABASE) -> None: """Launch a bash shell inside the running Infrahub container.""" with context.cd(ESCAPED_REPO_PATH): compose_files_cmd = build_compose_files_cmd(database=database) - command = f"{get_env_vars(context)} docker compose {compose_files_cmd} -p {BUILD_NAME} run infrahub-server bash" + command = ( + f"{get_env_vars(context)} docker compose {compose_files_cmd} -p {BUILD_NAME} run {SERVICE_SERVER_NAME} bash" + ) execute_command(context=context, command=command) @task(optional=["database"]) -def cli_git(context: Context, database: str = INFRAHUB_DATABASE): +def cli_git(context: Context, database: str = INFRAHUB_DATABASE) -> None: """Launch a bash shell inside the running Infrahub container.""" with context.cd(ESCAPED_REPO_PATH): compose_files_cmd = build_compose_files_cmd(database=database) - command = f"{get_env_vars(context)} docker compose {compose_files_cmd} -p {BUILD_NAME} run infrahub-git bash" + command = ( + f"{get_env_vars(context)} docker compose {compose_files_cmd} -p {BUILD_NAME} run {SERVICE_WORKER_NAME} bash" + ) execute_command(context=context, command=command) @task(optional=["database"]) -def status(context: Context, database: str = INFRAHUB_DATABASE): +def status(context: Context, database: str = INFRAHUB_DATABASE) -> None: """Display the status of all containers.""" show_service_status(context=context, database=database, namespace=NAMESPACE) @task(optional=["database"]) -def load_infra_schema(context: Context, database: str = INFRAHUB_DATABASE): +def load_infra_schema(context: Context, database: str = INFRAHUB_DATABASE) -> None: """Load the base schema for infrastructure.""" load_infrastructure_schema(context=context, database=database, namespace=NAMESPACE, add_wait=False) + load_infrastructure_menu(context=context, database=database, namespace=NAMESPACE) restart_services(context=context, database=database, namespace=NAMESPACE) @task(optional=["database"]) -def load_infra_data(context: Context, database: str = INFRAHUB_DATABASE): +def load_infra_menu(context: Context, database: str = INFRAHUB_DATABASE) -> None: + """Load the base schema for infrastructure.""" + load_infrastructure_menu(context=context, database=database, namespace=NAMESPACE) + + +@task(optional=["database"]) +def load_infra_data(context: Context, database: str = INFRAHUB_DATABASE) -> None: """Load infrastructure demo data.""" load_infrastructure_data(context=context, database=database, namespace=NAMESPACE) diff --git a/tasks/dev.py b/tasks/dev.py index 94f76b94af..dd306cab7c 100644 --- a/tasks/dev.py +++ b/tasks/dev.py @@ -3,7 +3,7 @@ import re from enum import Enum from pathlib import Path -from typing import TYPE_CHECKING, Any, Optional +from typing import TYPE_CHECKING, Any from invoke.tasks import task @@ -18,14 +18,17 @@ stop_services, update_core_schema, ) -from .infra_ops import load_infrastructure_data, load_infrastructure_schema +from .infra_ops import load_infrastructure_data, load_infrastructure_menu, load_infrastructure_schema from .shared import ( BUILD_NAME, INFRAHUB_DATABASE, PYTHON_VER, + SERVICE_WORKER_NAME, + Namespace, build_compose_files_cmd, build_dev_compose_files_cmd, execute_command, + get_compose_cmd, get_env_vars, ) from .utils import ESCAPED_REPO_PATH @@ -34,17 +37,17 @@ from invoke.context import Context -NAMESPACE = "DEV" +NAMESPACE = Namespace.DEV @task(optional=["database"]) def build( context: Context, - service: Optional[str] = None, + service: str | None = None, python_ver: str = PYTHON_VER, nocache: bool = False, database: str = INFRAHUB_DATABASE, -): +) -> None: """Build an image with the provided name and python version. Args: @@ -58,27 +61,29 @@ def build( @task(optional=["database"]) -def debug(context: Context, database: str = INFRAHUB_DATABASE): +def debug(context: Context, database: str = INFRAHUB_DATABASE) -> None: """Start a local instance of Infrahub in debug mode.""" with context.cd(ESCAPED_REPO_PATH): compose_files_cmd = build_compose_files_cmd(database=database) - command = f"{get_env_vars(context, namespace=NAMESPACE)} docker compose {compose_files_cmd} -p {BUILD_NAME} up" + compose_cmd = get_compose_cmd(namespace=NAMESPACE) + command = f"{get_env_vars(context, namespace=NAMESPACE)} {compose_cmd} {compose_files_cmd} -p {BUILD_NAME} up" execute_command(context=context, command=command) @task(optional=["database"]) -def deps(context: Context, database: str = INFRAHUB_DATABASE): +def deps(context: Context, database: str = INFRAHUB_DATABASE) -> None: """Start local instances of dependencies (Databases and Message Bus).""" with context.cd(ESCAPED_REPO_PATH): dev_compose_files_cmd = build_dev_compose_files_cmd(database=database) + compose_cmd = get_compose_cmd(namespace=NAMESPACE) command = ( - f"{get_env_vars(context, namespace=NAMESPACE)} docker compose {dev_compose_files_cmd} -p {BUILD_NAME} up -d" + f"{get_env_vars(context, namespace=NAMESPACE)} {compose_cmd} {dev_compose_files_cmd} -p {BUILD_NAME} up -d" ) execute_command(context=context, command=command) @task -def destroy(context: Context, database: str = INFRAHUB_DATABASE): +def destroy(context: Context, database: str = INFRAHUB_DATABASE) -> None: """Destroy all containers and volumes.""" destroy_environment(context=context, database=database, namespace=NAMESPACE) @@ -87,89 +92,92 @@ def destroy(context: Context, database: str = INFRAHUB_DATABASE): def infra_git_create( context: Context, database: str = INFRAHUB_DATABASE, - name="demo-edge", - location="/remote/infrahub-demo-edge", -): + name: str = "demo-edge", + location: str = "/remote/infrahub-demo-edge", +) -> None: """Load some demo data.""" with context.cd(ESCAPED_REPO_PATH): compose_files_cmd = build_compose_files_cmd(database=database, namespace=NAMESPACE) - base_cmd = f"{get_env_vars(context, namespace=NAMESPACE)} docker compose {compose_files_cmd} -p {BUILD_NAME}" + compose_cmd = get_compose_cmd(namespace=NAMESPACE) + base_cmd = f"{get_env_vars(context, namespace=NAMESPACE)} {compose_cmd} {compose_files_cmd} -p {BUILD_NAME}" execute_command( context=context, - command=f"{base_cmd} run infrahub-git infrahubctl repository add {name} {location}", + command=f"{base_cmd} run {SERVICE_WORKER_NAME} infrahubctl repository add {name} {location}", ) @task(optional=["database"]) -def infra_git_import(context: Context, database: str = INFRAHUB_DATABASE): +def infra_git_import(context: Context, database: str = INFRAHUB_DATABASE) -> None: """Load some demo data.""" REPO_NAME = "infrahub-demo-edge" with context.cd(ESCAPED_REPO_PATH): compose_files_cmd = build_compose_files_cmd(database=database, namespace=NAMESPACE) - base_cmd = f"{get_env_vars(context, namespace=NAMESPACE)} docker compose {compose_files_cmd} -p {BUILD_NAME}" + compose_cmd = get_compose_cmd(namespace=NAMESPACE) + base_cmd = f"{get_env_vars(context, namespace=NAMESPACE)} {compose_cmd} {compose_files_cmd} -p {BUILD_NAME}" execute_command( context=context, - command=f"{base_cmd} run infrahub-git cp -r backend/tests/fixtures/repos/{REPO_NAME}/initial__main /remote/{REPO_NAME}", + command=f"{base_cmd} run {SERVICE_WORKER_NAME} cp -r backend/tests/fixtures/repos/{REPO_NAME}/initial__main /remote/{REPO_NAME}", ) execute_command( context=context, - command=f"{base_cmd} exec --workdir /remote/{REPO_NAME} infrahub-git git init --initial-branch main", + command=f"{base_cmd} exec --workdir /remote/{REPO_NAME} {SERVICE_WORKER_NAME} git init --initial-branch main", ) execute_command( context=context, - command=f"{base_cmd} exec --workdir /remote/{REPO_NAME} infrahub-git git add .", + command=f"{base_cmd} exec --workdir /remote/{REPO_NAME} {SERVICE_WORKER_NAME} git add .", ) execute_command( context=context, - command=f"{base_cmd} exec --workdir /remote/{REPO_NAME} infrahub-git git commit -m first", + command=f"{base_cmd} exec --workdir /remote/{REPO_NAME} {SERVICE_WORKER_NAME} git commit -m first", ) @task(optional=["database"]) -def load_infra_data(context: Context, database: str = INFRAHUB_DATABASE): +def load_infra_data(context: Context, database: str = INFRAHUB_DATABASE) -> None: """Load infrastructure demo data.""" load_infrastructure_data(context=context, database=database, namespace=NAMESPACE) @task(optional=["database"]) -def load_infra_schema(context: Context, database: str = INFRAHUB_DATABASE): +def load_infra_schema(context: Context, database: str = INFRAHUB_DATABASE) -> None: """Load the base schema for infrastructure.""" load_infrastructure_schema(context=context, database=database, namespace=NAMESPACE, add_wait=False) + load_infrastructure_menu(context=context, database=database, namespace=NAMESPACE) restart_services(context=context, database=database, namespace=NAMESPACE) @task(optional=["database"]) -def pull(context: Context, database: str = INFRAHUB_DATABASE): +def pull(context: Context, database: str = INFRAHUB_DATABASE) -> None: """Pull external containers from registry.""" pull_images(context=context, database=database, namespace=NAMESPACE) @task(optional=["database"]) -def restart(context: Context, database: str = INFRAHUB_DATABASE): - """Restart Infrahub API Server and Git Agent within docker compose.""" +def restart(context: Context, database: str = INFRAHUB_DATABASE) -> None: + """Restart Infrahub API Server and Task worker within docker compose.""" restart_services(context=context, database=database, namespace=NAMESPACE) @task(optional=["database"]) -def status(context: Context, database: str = INFRAHUB_DATABASE): +def status(context: Context, database: str = INFRAHUB_DATABASE) -> None: """Display the status of all containers.""" show_service_status(context=context, database=database, namespace=NAMESPACE) @task(optional=["database"]) -def start(context: Context, database: str = INFRAHUB_DATABASE, wait: bool = False): +def start(context: Context, database: str = INFRAHUB_DATABASE, wait: bool = False) -> None: """Start a local instance of Infrahub within docker compose.""" start_services(context=context, database=database, namespace=NAMESPACE, wait=wait) @task(optional=["database"]) -def stop(context: Context, database: str = INFRAHUB_DATABASE): +def stop(context: Context, database: str = INFRAHUB_DATABASE) -> None: """Stop the running instance of Infrahub.""" stop_services(context=context, database=database, namespace=NAMESPACE) @task(optional=["database"]) -def migrate(context: Context, database: str = INFRAHUB_DATABASE): +def migrate(context: Context, database: str = INFRAHUB_DATABASE) -> None: """Apply the latest database migrations.""" migrate_database(context=context, database=database, namespace=NAMESPACE) update_core_schema(context=context, database=database, namespace=NAMESPACE, debug=True) @@ -183,12 +191,12 @@ def get_version_from_pyproject() -> str: @task -def update_helm_chart(context, chart_file: Optional[str] = "helm/Chart.yaml"): +def update_helm_chart(context: Context, chart_file: str | None = "helm/Chart.yaml") -> None: """Update helm/Chart.yaml with the current version from pyproject.toml.""" version = get_version_from_pyproject() version_pattern = r"^appVersion:\s*[\d\.\-a-zA-Z]+" - def replace_version(match): + def replace_version(match: str) -> str: return f"appVersion: {version}" chart_path = Path(chart_file) @@ -201,12 +209,12 @@ def replace_version(match): @task -def update_docker_compose(context, docker_file: Optional[str] = "docker-compose.yml"): +def update_docker_compose(context: Context, docker_file: str | None = "docker-compose.yml") -> None: """Update docker-compose.yml with the current version from pyproject.toml.""" version = get_version_from_pyproject() version_pattern = r"registry.opsmill.io/opsmill/infrahub:\$\{VERSION:-[\d\.\-a-zA-Z]+\}" - def replace_version(match): + def replace_version(match: str) -> str: return f"registry.opsmill.io/opsmill/infrahub:${{VERSION:-{version}}}" docker_path = Path(docker_file) @@ -217,9 +225,19 @@ def replace_version(match): print(f"{docker_file} updated with version {version}") -def get_enum_mappings(): +def get_enum_mappings() -> dict: """Extracts enum mappings dynamically.""" - from infrahub.config import BrokerDriver, CacheDriver, StorageDriver, TraceExporterType, TraceTransportProtocol + from infrahub.config import ( + BrokerDriver, + CacheDriver, + Oauth2Provider, + OIDCProvider, + SSOProtocol, + StorageDriver, + TraceExporterType, + TraceTransportProtocol, + WorkflowDriver, + ) from infrahub.database.constants import DatabaseType enum_mappings = {} @@ -227,10 +245,14 @@ def get_enum_mappings(): for enum_class in [ BrokerDriver, CacheDriver, - DatabaseType, + Oauth2Provider, + OIDCProvider, + SSOProtocol, StorageDriver, TraceExporterType, TraceTransportProtocol, + WorkflowDriver, + DatabaseType, ]: for item in enum_class: enum_mappings[item] = item.value @@ -242,9 +264,11 @@ def update_docker_compose_env_vars( env_vars: list[str], env_defaults: dict[str, Any], enum_mappings: dict[Any, str], - docker_file: Optional[str] = "docker-compose.yml", + docker_file: str | None = "docker-compose.yml", ) -> None: """Update the docker-compose.yml file with the environment variables.""" + import json + docker_path = Path(docker_file) docker_compose = docker_path.read_text(encoding="utf-8").splitlines() @@ -272,12 +296,17 @@ def update_docker_compose_env_vars( new_config_lines = [] for var in all_vars: + if var.startswith("INFRAHUB_DEV"): + continue default_value = env_defaults.get(var, "") if isinstance(default_value, bool): - default_value = str(default_value).lower() + default_value_str = str(default_value).lower() elif isinstance(default_value, Enum): - default_value = enum_mappings.get(default_value, str(default_value)) - default_value_str = str(default_value) if default_value is not None else "" + default_value_str = enum_mappings.get(default_value, str(default_value)) + elif isinstance(default_value, list): + default_value_str = json.dumps(default_value) + else: + default_value_str = str(default_value) if default_value is not None else "" if var in existing_vars: line_idx = existing_vars[var] @@ -286,14 +315,24 @@ def update_docker_compose_env_vars( match = pattern.match(existing_value) if match and match.group(1) == var and match.group(2) == default_value_str: new_config_lines.append(docker_compose[line_idx]) - elif var in ["INFRAHUB_BROKER_USERNAME", "INFRAHUB_BROKER_PASSWORD"]: + elif var in [ + "INFRAHUB_BROKER_USERNAME", + "INFRAHUB_BROKER_PASSWORD", + "INFRAHUB_CACHE_USERNAME", + "INFRAHUB_CACHE_PASSWORD", + ]: key_name = var.replace("INFRAHUB_", "").lower() new_config_lines.append(f" {var}: &{key_name} ${{{var}:-{default_value_str}}}") elif default_value_str: new_config_lines.append(f" {var}: ${{{var}:-{default_value_str}}}") else: new_config_lines.append(f" {var}:") - elif var in ["INFRAHUB_BROKER_USERNAME", "INFRAHUB_BROKER_PASSWORD"]: + elif var in [ + "INFRAHUB_BROKER_USERNAME", + "INFRAHUB_BROKER_PASSWORD", + "INFRAHUB_CACHE_USERNAME", + "INFRAHUB_CACHE_PASSWORD", + ]: key_name = var.replace("INFRAHUB_", "").lower() new_config_lines.append(f" {var}: &{key_name} ${{{var}:-{default_value_str}}}") elif default_value_str: @@ -309,8 +348,8 @@ def update_docker_compose_env_vars( @task def gen_config_env( - context: Context, docker_file: Optional[str] = "docker-compose.yml", update_docker_file: Optional[bool] = False -): + context: Context, docker_file: str | None = "docker-compose.yml", update_docker_file: bool | None = False +) -> None: """Generate list of env vars required for configuration and update docker file.yml if need be.""" from pydantic_settings import BaseSettings from pydantic_settings.sources import EnvSettingsSource @@ -330,7 +369,7 @@ def gen_config_env( settings = Settings() env_defaults = {} - def fetch_fields(subset: BaseSettings): + def fetch_fields(subset: BaseSettings) -> None: env_settings = EnvSettingsSource( subset.__class__, env_prefix=subset.model_config.get("env_prefix"), @@ -348,8 +387,7 @@ def fetch_fields(subset: BaseSettings): subsettings = getattr(settings, subsetting) fetch_fields(subsettings) - if "PATH" in env_vars: - env_vars.remove("PATH") + env_vars.discard("PATH") if update_docker_file: update_docker_compose_env_vars( env_vars=sorted(env_vars), env_defaults=env_defaults, enum_mappings=enum_mappings, docker_file=docker_file diff --git a/tasks/docs.py b/tasks/docs.py index 6082a5e3e6..29ab2fc5e9 100644 --- a/tasks/docs.py +++ b/tasks/docs.py @@ -19,7 +19,7 @@ @task -def build(context: Context): +def build(context: Context) -> None: """Build documentation website.""" exec_cmd = "npm run build" @@ -31,55 +31,49 @@ def build(context: Context): @task -def generate(context: Context): +def generate(context: Context) -> None: """Generate all documentation output from code.""" _generate(context=context) @task -def generate_schema(context: Context): +def generate_schema(context: Context) -> None: """Generate documentation for the schema.""" _generate_infrahub_schema_documentation() @task -def generate_infrahub_cli(context: Context): +def generate_infrahub_cli(context: Context) -> None: """Generate documentation for the infrahub cli.""" _generate_infrahub_cli_documentation(context=context) @task -def generate_infrahubctl(context: Context): +def generate_infrahubctl(context: Context) -> None: """Generate documentation for the infrahubctl cli.""" _generate_infrahubctl_documentation(context=context) @task -def generate_infrahubsync_sync(context: Context): - """Generate documentation for the infrahub-sync cli.""" - _generate_infrahubsync_documentation(context=context) - - -@task -def generate_repository(context: Context): +def generate_repository(context: Context) -> None: """Generate documentation for the repository configuration file.""" _generate_infrahub_repository_configuration_documentation(context=context) @task -def generate_python_sdk(context: Context): +def generate_python_sdk(context: Context) -> None: """Generate documentation for the Python SDK.""" _generate_infrahub_sdk_configuration_documentation(context=context) @task -def generate_bus_events(context: Context): +def generate_bus_events(context: Context) -> None: """Generate documentation for the Bus events.""" _generate_infrahub_events_documentation(context=context) @task -def install(context: Context): +def install(context: Context) -> None: """Install documentation dependencies.""" exec_cmd = "npm install" @@ -91,7 +85,7 @@ def install(context: Context): @task -def validate(context: Context, docker: bool = False): +def validate(context: Context, docker: bool = False) -> None: """Validate that the generated documentation is committed to Git.""" if docker: @@ -109,7 +103,7 @@ def validate(context: Context, docker: bool = False): @task -def serve(context: Context): +def serve(context: Context) -> None: """Run documentation server in development mode.""" exec_cmd = "npm run serve" @@ -119,7 +113,7 @@ def serve(context: Context): @task -def vale(context: Context): +def vale(context: Context) -> None: """Run vale to validate the documentation.""" has_vale = check_if_command_available(context=context, command_name="vale") @@ -134,7 +128,7 @@ def vale(context: Context): @task -def markdownlint(context: Context): +def markdownlint(context: Context) -> None: has_markdownlint = check_if_command_available(context=context, command_name="markdownlint-cli2") if not has_markdownlint: @@ -147,7 +141,7 @@ def markdownlint(context: Context): @task -def format_markdownlint(context: Context): +def format_markdownlint(context: Context) -> None: """Run markdownlint-cli2 to format all .md/mdx files.""" print(" - [docs] Format code with markdownlint-cli2") @@ -157,19 +151,19 @@ def format_markdownlint(context: Context): @task -def format(context: Context): +def format(context: Context) -> None: """This will run all formatter.""" format_markdownlint(context) @task -def lint(context: Context): +def lint(context: Context) -> None: """This will run all linter.""" markdownlint(context) vale(context) -def _generate_infrahub_cli_documentation(context: Context): +def _generate_infrahub_cli_documentation(context: Context) -> None: """Generate the documentation for infrahub cli using typer-cli.""" CLI_COMMANDS = ( @@ -185,10 +179,9 @@ def _generate_infrahub_cli_documentation(context: Context): context.run(exec_cmd) -def _generate(context: Context): +def _generate(context: Context) -> None: """Generate documentation output from code.""" _generate_infrahub_cli_documentation(context=context) - # _generate_infrahubsync_documentation(context=context) _generate_infrahubctl_documentation(context=context) _generate_infrahub_schema_documentation() _generate_infrahub_repository_configuration_documentation() @@ -196,7 +189,7 @@ def _generate(context: Context): _generate_infrahub_events_documentation() -def _generate_infrahubctl_documentation(context: Context): +def _generate_infrahubctl_documentation(context: Context) -> None: """Generate the documentation for infrahubctl using typer-cli.""" from infrahub_sdk.ctl.cli import app @@ -214,16 +207,6 @@ def _generate_infrahubctl_documentation(context: Context): context.run(exec_cmd) -def _generate_infrahubsync_documentation(context: Context): - """Generate the documentation for infrahub-sync using typer-cli.""" - - print(" - Generate infrahub-sync CLI documentation") - exec_cmd = 'poetry run typer infrahub_sync.cli utils docs --name "infrahub-sync"' - exec_cmd += " --output docs/docs/integrations/sync/reference/cli.mdx" - with context.cd(ESCAPED_REPO_PATH): - context.run(exec_cmd) - - def _generate_infrahub_schema_documentation() -> None: """Generate documentation for the schema""" import jinja2 @@ -347,9 +330,7 @@ def _generate_infrahub_repository_configuration_documentation() -> None: for name, definition in schema["$defs"].items(): for property, value in definition["properties"].items(): - definitions[name]["properties"][property]["required"] = ( - True if property in definition["required"] else False - ) + definitions[name]["properties"][property]["required"] = property in definition["required"] if "anyOf" in value: definitions[name]["properties"][property]["type"] = ", ".join( [i["type"] for i in value["anyOf"] if i["type"] != "null"] diff --git a/tasks/infra_ops.py b/tasks/infra_ops.py index fe6c977ff9..9122804351 100644 --- a/tasks/infra_ops.py +++ b/tasks/infra_ops.py @@ -2,28 +2,49 @@ from typing import TYPE_CHECKING -from .shared import BUILD_NAME, build_compose_files_cmd, execute_command, get_env_vars +from .shared import ( + BUILD_NAME, + SERVICE_WORKER_NAME, + Namespace, + build_compose_files_cmd, + execute_command, + get_compose_cmd, + get_env_vars, +) from .utils import ESCAPED_REPO_PATH if TYPE_CHECKING: from invoke.context import Context -def load_infrastructure_data(context: Context, database: str, namespace: str) -> None: +def load_infrastructure_data(context: Context, database: str, namespace: Namespace) -> None: with context.cd(ESCAPED_REPO_PATH): compose_files_cmd = build_compose_files_cmd(database=database, namespace=namespace) - base_cmd = f"{get_env_vars(context, namespace=namespace)} docker compose {compose_files_cmd} -p {BUILD_NAME}" - command = f"{base_cmd} run infrahub-git infrahubctl run models/infrastructure_edge.py" + compose_cmd = get_compose_cmd(namespace=namespace) + base_cmd = f"{get_env_vars(context, namespace=namespace)} {compose_cmd} {compose_files_cmd} -p {BUILD_NAME}" + command = f"{base_cmd} run {SERVICE_WORKER_NAME} infrahubctl run models/infrastructure_edge.py" execute_command(context=context, command=command) def load_infrastructure_schema( - context: Context, database: str, namespace: str, add_wait: bool = True, target: str = "models/base" + context: Context, database: str, namespace: Namespace, add_wait: bool = True, target: str = "models/base" ) -> None: with context.cd(ESCAPED_REPO_PATH): compose_files_cmd = build_compose_files_cmd(database=database, namespace=namespace) - base_cmd = f"{get_env_vars(context, namespace=namespace)} docker compose {compose_files_cmd} -p {BUILD_NAME}" - command = f"{base_cmd} run infrahub-git infrahubctl schema load {target}" + compose_cmd = get_compose_cmd(namespace=namespace) + base_cmd = f"{get_env_vars(context, namespace=namespace)} {compose_cmd} {compose_files_cmd} -p {BUILD_NAME}" + command_schema = f"{base_cmd} run {SERVICE_WORKER_NAME} infrahubctl schema load {target}" if add_wait: - command += " --wait 30" + command_schema += " --wait 30" + execute_command(context=context, command=command_schema) + + +def load_infrastructure_menu( + context: Context, database: str, namespace: Namespace, menu_target: str = "models/base_menu.yml" +) -> None: + with context.cd(ESCAPED_REPO_PATH): + compose_files_cmd = build_compose_files_cmd(database=database, namespace=namespace) + compose_cmd = get_compose_cmd(namespace=namespace) + base_cmd = f"{get_env_vars(context, namespace=namespace)} {compose_cmd} {compose_files_cmd} -p {BUILD_NAME}" + command = f"{base_cmd} run {SERVICE_WORKER_NAME} infrahubctl menu load {menu_target}" execute_command(context=context, command=command) diff --git a/tasks/main.py b/tasks/main.py index e950c486c4..50788729f8 100644 --- a/tasks/main.py +++ b/tasks/main.py @@ -11,7 +11,7 @@ # ---------------------------------------------------------------------------- -def _format_ruff(context: Context): +def _format_ruff(context: Context) -> None: """Run ruff to format all Python files.""" print(f" - [{NAMESPACE}] Format code with ruff") @@ -22,7 +22,7 @@ def _format_ruff(context: Context): @task(name="format", default=True) -def format_all(context: Context): +def format_all(context: Context) -> None: """This will run all formatters.""" _format_ruff(context) diff --git a/tasks/performance.py b/tasks/performance.py index 5252eb9ed0..0f254aca0a 100644 --- a/tasks/performance.py +++ b/tasks/performance.py @@ -7,7 +7,7 @@ @task -def run(context: Context, directory: str = "utilities", dataset: str = "dataset03"): +def run(context: Context, directory: str = "utilities", dataset: str = "dataset03") -> None: """Launch a performance test using Locust. Gunicorn must be running""" PERFORMANCE_FILE_PREFIX = "locust_" NOW = datetime.now(tz=timezone.utc) diff --git a/tasks/schema.py b/tasks/schema.py index d5a73489a8..8c318a1de9 100644 --- a/tasks/schema.py +++ b/tasks/schema.py @@ -11,14 +11,14 @@ @task -def generate_jsonschema(context: Context): +def generate_jsonschema(context: Context) -> None: """Generate JSON schemas into ./generated""" generate_sdk_repository_config() generate_infrahub_node_schema() -def generate_infrahub_node_schema(): +def generate_infrahub_node_schema() -> None: from infrahub.api.schema import SchemaLoadAPI schema_dir = Path(f"{INFRAHUB_DIRECTORY}/schema") @@ -33,7 +33,7 @@ def generate_infrahub_node_schema(): write(file_path=schema_dir / "develop.json", content=content) -def generate_sdk_repository_config(): +def generate_sdk_repository_config() -> None: from infrahub_sdk.schema import InfrahubRepositoryConfig repository_dir = Path(f"{SDK_DIRECTORY}/repository-config") diff --git a/tasks/sdk.py b/tasks/sdk.py index bb606a3e73..6784c01fe5 100644 --- a/tasks/sdk.py +++ b/tasks/sdk.py @@ -1,6 +1,8 @@ import os +from typing import Optional from invoke import Context, task +from invoke.runners import Result from .shared import ( BUILD_NAME, @@ -23,7 +25,7 @@ # ---------------------------------------------------------------------------- -def _format_ruff(context: Context): +def _format_ruff(context: Context) -> None: """Run ruff to format all Python files.""" print(f" - [{NAMESPACE}] Format code with ruff") @@ -34,7 +36,7 @@ def _format_ruff(context: Context): @task(name="format") -def format_all(context: Context): +def format_all(context: Context) -> None: """This will run all formatter.""" _format_ruff(context) @@ -46,7 +48,7 @@ def format_all(context: Context): # Testing tasks # ---------------------------------------------------------------------------- @task -def ruff(context: Context, docker: bool = False): +def ruff(context: Context, docker: bool = False) -> None: """Run ruff to check that Python files adherence to black standards.""" print(f" - [{NAMESPACE}] Check code with ruff") @@ -69,7 +71,7 @@ def ruff(context: Context, docker: bool = False): @task -def mypy(context: Context, docker: bool = False): +def mypy(context: Context, docker: bool = False) -> None: """This will run mypy for the specified name and Python version.""" print(f" - [{NAMESPACE}] Check code with mypy") @@ -90,7 +92,7 @@ def mypy(context: Context, docker: bool = False): @task -def pylint(context: Context, docker: bool = False): +def pylint(context: Context, docker: bool = False) -> None: """This will run pylint for the specified name and Python version.""" print(f" - [{NAMESPACE}] Check code with pylint") @@ -111,7 +113,7 @@ def pylint(context: Context, docker: bool = False): @task -def lint(context: Context, docker: bool = False): +def lint(context: Context, docker: bool = False) -> Optional[Result]: """This will run all linter.""" ruff(context, docker=docker) mypy(context, docker=docker) @@ -121,7 +123,7 @@ def lint(context: Context, docker: bool = False): @task -def test_unit(context: Context): +def test_unit(context: Context) -> Optional[Result]: with context.cd(ESCAPED_REPO_PATH): compose_files_cmd = build_test_compose_files_cmd(database=False) base_cmd = f"{get_env_vars(context)} docker compose {compose_files_cmd} -p {BUILD_NAME} run {build_test_envs()} infrahub-test" @@ -131,7 +133,7 @@ def test_unit(context: Context): @task(optional=["database"]) -def test_integration(context: Context, database: str = INFRAHUB_DATABASE): +def test_integration(context: Context, database: str = INFRAHUB_DATABASE) -> Optional[Result]: with context.cd(ESCAPED_REPO_PATH): compose_files_cmd = build_test_compose_files_cmd(database=database) base_cmd = f"{get_env_vars(context)} docker compose {compose_files_cmd} -p {BUILD_NAME} run {build_test_envs()} infrahub-test" @@ -141,6 +143,6 @@ def test_integration(context: Context, database: str = INFRAHUB_DATABASE): @task(default=True) -def format_and_lint(context: Context): +def format_and_lint(context: Context) -> None: format_all(context) lint(context) diff --git a/tasks/shared.py b/tasks/shared.py index c0fd2d583a..5359293e77 100644 --- a/tasks/shared.py +++ b/tasks/shared.py @@ -17,6 +17,12 @@ class DatabaseType(str, Enum): MEMGRAPH = "memgraph" +class Namespace(str, Enum): + DEFAULT = "default" # aka demo + DEV = "dev" + TEST = "test" + + INVOKE_SUDO = os.getenv("INVOKE_SUDO", None) INVOKE_PTY = os.getenv("INVOKE_PTY", None) INFRAHUB_DATABASE = os.getenv("INFRAHUB_DB_TYPE", DatabaseType.NEO4J.value) @@ -26,12 +32,14 @@ class DatabaseType(str, Enum): DATABASE_DOCKER_IMAGE = os.getenv("DATABASE_DOCKER_IMAGE", None) MEMGRAPH_DOCKER_IMAGE = os.getenv("MEMGRAPH_DOCKER_IMAGE", "memgraph/memgraph-mage:1.19-memgraph-2.19-no-ml") -NEO4J_DOCKER_IMAGE = os.getenv("NEO4J_DOCKER_IMAGE", "neo4j:5.19.0-enterprise") +NEO4J_DOCKER_IMAGE = os.getenv("NEO4J_DOCKER_IMAGE", "neo4j:5.20.0-enterprise") MESSAGE_QUEUE_DOCKER_IMAGE = os.getenv( "MESSAGE_QUEUE_DOCKER_IMAGE", "rabbitmq:3.13.7-management" if not INFRAHUB_USE_NATS else "nats:2.10.14-alpine" ) CACHE_DOCKER_IMAGE = os.getenv("CACHE_DOCKER_IMAGE", "redis:7.2.4" if not INFRAHUB_USE_NATS else "nats:2.10.14-alpine") +TASK_MANAGER_DOCKER_IMAGE = os.getenv("TASK_MANAGER_DOCKER_IMAGE", "prefecthq/prefect:3.0.3-python3.12") + here = Path(__file__).parent.resolve() TOP_DIRECTORY_NAME = here.parent.name BUILD_NAME = os.getenv("INFRAHUB_BUILD_NAME", re.sub(r"[^a-zA-Z0-9_/.]", "", str(TOP_DIRECTORY_NAME))) @@ -42,7 +50,11 @@ class DatabaseType(str, Enum): NBR_WORKERS = os.getenv("PYTEST_XDIST_WORKER_COUNT", 1) GITHUB_ACTION = os.getenv("GITHUB_ACTION", False) -AVAILABLE_SERVICES = ["infrahub-git", "infrahub-server", "database", "message-queue"] + +SERVICE_SERVER_NAME = "server" +SERVICE_WORKER_NAME = "task-worker" +AVAILABLE_SERVICES = [SERVICE_SERVER_NAME, SERVICE_WORKER_NAME, "database", "message-queue", "task-manager", "cache"] + SUPPORTED_DATABASES = [DatabaseType.MEMGRAPH.value, DatabaseType.NEO4J.value] COMPOSE_FILES_DEPS = {False: "development/docker-compose-deps.yml", True: "development/docker-compose-deps-nats.yml"} @@ -51,15 +63,13 @@ class DatabaseType(str, Enum): TEST_COMPOSE_FILES_MEMGRAPH = [ COMPOSE_FILES_DEPS[INFRAHUB_USE_NATS], "development/docker-compose-test-database-memgraph.yml", - "development/docker-compose-test-cache.yml", - "development/docker-compose-test-message-queue.yml", + "development/docker-compose-test-deps.yml", TEST_COMPOSE_FILE, ] TEST_COMPOSE_FILES_NEO4J = [ COMPOSE_FILES_DEPS[INFRAHUB_USE_NATS], "development/docker-compose-test-database-neo4j.yml", - "development/docker-compose-test-cache.yml", - "development/docker-compose-test-message-queue.yml", + "development/docker-compose-test-deps.yml", TEST_COMPOSE_FILE, ] @@ -117,6 +127,8 @@ class DatabaseType(str, Enum): "database_logs", "git_data", "git_remote_data", + "workflow_data", + "workflow_db", "storage_data", ] @@ -176,6 +188,29 @@ def check_environment(context: Context) -> dict: return params +def dumb_terminal() -> bool: + return os.getenv("TERM", "").lower() == "dumb" + + +def get_compose_cmd(namespace: Namespace) -> str: + options = [] + + if namespace == Namespace.DEV: + options.append("--profile dev") + elif namespace == Namespace.DEFAULT: + options.append("--profile demo") + elif namespace == Namespace.TEST: + options.append("--profile test") + + if dumb_terminal(): + options.append("--ansi never") + + if len(options) > 0: + return f"docker compose {' '.join(options)}" + + return "docker compose" + + def execute_command(context: Context, command: str, print_cmd: bool = False) -> Optional[Result]: params = check_environment(context=context) @@ -189,7 +224,7 @@ def execute_command(context: Context, command: str, print_cmd: bool = False) -> return context.run(command, pty=params["pty"]) -def get_env_vars(context: Context, namespace: str = "default") -> str: +def get_env_vars(context: Context, namespace: Namespace = Namespace.DEFAULT) -> str: ENV_VARS_DICT = { "IMAGE_NAME": IMAGE_NAME, "IMAGE_VER": IMAGE_VER, @@ -198,22 +233,23 @@ def get_env_vars(context: Context, namespace: str = "default") -> str: "NBR_WORKERS": NBR_WORKERS, "CACHE_DOCKER_IMAGE": CACHE_DOCKER_IMAGE, "MESSAGE_QUEUE_DOCKER_IMAGE": MESSAGE_QUEUE_DOCKER_IMAGE, + "TASK_MANAGER_DOCKER_IMAGE": TASK_MANAGER_DOCKER_IMAGE, "INFRAHUB_DB_TYPE": INFRAHUB_DATABASE, } - if namespace == "DEV" and not REQUESTED_IMAGE_VER: + if namespace == Namespace.DEV and not REQUESTED_IMAGE_VER: ENV_VARS_DICT["IMAGE_VER"] = "local" if DATABASE_DOCKER_IMAGE: ENV_VARS_DICT["DATABASE_DOCKER_IMAGE"] = DATABASE_DOCKER_IMAGE - elif INFRAHUB_DATABASE == DatabaseType.NEO4J.value: + elif DatabaseType.NEO4J.value == INFRAHUB_DATABASE: ENV_VARS_DICT["DATABASE_DOCKER_IMAGE"] = NEO4J_DOCKER_IMAGE - elif INFRAHUB_DATABASE == DatabaseType.MEMGRAPH.value: + elif DatabaseType.MEMGRAPH.value == INFRAHUB_DATABASE: ENV_VARS_DICT["DATABASE_DOCKER_IMAGE"] = MEMGRAPH_DOCKER_IMAGE return " ".join([f"{key}={value}" for key, value in ENV_VARS_DICT.items()]) -def build_compose_files_cmd(database: str, namespace: str = "") -> str: +def build_compose_files_cmd(database: str, namespace: Namespace = Namespace.DEFAULT) -> str: if database not in SUPPORTED_DATABASES: sys.exit(f"{database} is not a valid database ({SUPPORTED_DATABASES})") @@ -228,7 +264,7 @@ def build_compose_files_cmd(database: str, namespace: str = "") -> str: else: COMPOSE_FILES.append(DEFAULT_FILE_NAME) - if "local" in IMAGE_VER or (namespace == "DEV" and not REQUESTED_IMAGE_VER): + if "local" in IMAGE_VER or (namespace == Namespace.DEV and not REQUESTED_IMAGE_VER): COMPOSE_FILES.append(LOCAL_FILE_NAME) if os.getenv("CI") is not None: @@ -295,7 +331,7 @@ def build_test_scale_compose_files_cmd( return f"-f {' -f '.join(TEST_SCALE_COMPOSE_FILES)}" -def build_test_envs(): +def build_test_envs() -> str: if GITHUB_ACTION: return f"-e {' -e '.join(GITHUB_ENVS_TO_PASS)}" diff --git a/tasks/sync.py b/tasks/sync.py deleted file mode 100644 index 0858ab67ff..0000000000 --- a/tasks/sync.py +++ /dev/null @@ -1,121 +0,0 @@ -from invoke import Context, task - -from .shared import ( - BUILD_NAME, - NBR_WORKERS, - build_test_compose_files_cmd, - build_test_envs, - execute_command, - get_env_vars, -) -from .utils import ESCAPED_REPO_PATH, REPO_BASE - -MAIN_DIRECTORY = "sync/infrahub-sync" -NAMESPACE = "SYNC" - - -# ---------------------------------------------------------------------------- -# Formatting tasks -# ---------------------------------------------------------------------------- - - -def _format_ruff(context: Context): - """Run ruff to format all Python files.""" - - print(f" - [{NAMESPACE}] Format code with ruff") - exec_cmd = f"ruff format {MAIN_DIRECTORY} --config {REPO_BASE}/pyproject.toml && " - exec_cmd += f"ruff check --fix {MAIN_DIRECTORY} --config {REPO_BASE}/pyproject.toml" - with context.cd(ESCAPED_REPO_PATH): - context.run(exec_cmd) - - -@task(name="format") -def format_all(context: Context): - """This will run all formatter.""" - - _format_ruff(context) - - print(f" - [{NAMESPACE}] All formatters have been executed!") - - -# ---------------------------------------------------------------------------- -# Testing tasks -# ---------------------------------------------------------------------------- -@task -def ruff(context: Context, docker: bool = False): - """Run ruff to check that Python files adherence to black standards.""" - - print(f" - [{NAMESPACE}] Check code with ruff") - exec_cmd = f"ruff check --diff {MAIN_DIRECTORY} --config {REPO_BASE}/pyproject.toml" - - if docker: - compose_files_cmd = build_test_compose_files_cmd(database=False) - exec_cmd = ( - f"{get_env_vars(context)} docker compose {compose_files_cmd} -p {BUILD_NAME} run infrahub-test {exec_cmd}" - ) - print(exec_cmd) - - with context.cd(ESCAPED_REPO_PATH): - context.run(exec_cmd) - - -@task -def mypy(context: Context, docker: bool = False): - """This will run mypy for the specified name and Python version.""" - - print(f" - [{NAMESPACE}] Check code with mypy") - exec_cmd = f"mypy --show-error-codes {MAIN_DIRECTORY}" - - if docker: - compose_files_cmd = build_test_compose_files_cmd(database=False) - exec_cmd = ( - f"{get_env_vars(context)} docker compose {compose_files_cmd} -p {BUILD_NAME} run infrahub-test {exec_cmd}" - ) - print(exec_cmd) - - with context.cd(ESCAPED_REPO_PATH): - context.run(exec_cmd) - - -@task -def pylint(context: Context, docker: bool = False): - """This will run pylint for the specified name and Python version.""" - - print(f" - [{NAMESPACE}] Check code with pylint") - exec_cmd = f"pylint ./{MAIN_DIRECTORY}/infrahub_sync" - - if docker: - compose_files_cmd = build_test_compose_files_cmd(database=False) - exec_cmd = ( - f"{get_env_vars(context)} docker compose {compose_files_cmd} -p {BUILD_NAME} run infrahub-test {exec_cmd}" - ) - print(exec_cmd) - - with context.cd(ESCAPED_REPO_PATH): - context.run(exec_cmd) - - -@task -def lint(context: Context, docker: bool = False): - """This will run all linter.""" - ruff(context, docker=docker) - pylint(context, docker=docker) - # mypy(context, docker=docker) - - print(f" - [{NAMESPACE}] All tests have passed!") - - -@task -def test_unit(context: Context): - with context.cd(ESCAPED_REPO_PATH): - compose_files_cmd = build_test_compose_files_cmd(database=False) - base_cmd = f"{get_env_vars(context)} docker compose {compose_files_cmd} -p {BUILD_NAME} run {build_test_envs()} infrahub-test" - exec_cmd = f"pytest -n {NBR_WORKERS} -v --cov=infrahub_sync {MAIN_DIRECTORY}/tests/unit" - print(f"{base_cmd} {exec_cmd}") - return execute_command(context=context, command=f"{base_cmd} {exec_cmd}") - - -@task(default=True) -def format_and_lint(context: Context): - format_all(context) - lint(context) diff --git a/utilities/db_backup/__main__.py b/utilities/db_backup/__main__.py index 760a4a1fdf..a3f81e82df 100644 --- a/utilities/db_backup/__main__.py +++ b/utilities/db_backup/__main__.py @@ -5,7 +5,7 @@ from dataclasses import dataclass from datetime import datetime, timezone from pathlib import Path -from typing import Dict, Generator, List, Optional +from typing import Any, Dict, Generator, List, Optional import docker from docker.models.containers import Container @@ -308,7 +308,7 @@ def backup( class Neo4jRestoreRunner(Neo4jBackupRestoreBase): backup_helper_container_name = "neo4j-restore-helper" - def __init__(self, *args, database_cypher_port: int = 7687, **kwargs) -> None: + def __init__(self, *args: Any, database_cypher_port: int = 7687, **kwargs: dict[str, Any]) -> None: super().__init__(*args, **kwargs) self.database_cypher_port = database_cypher_port neo4j_auth = os.environ.get("NEO4J_AUTH") diff --git a/utilities/db_query_benchmark.py b/utilities/db_query_benchmark.py deleted file mode 100644 index 2a80eed1f7..0000000000 --- a/utilities/db_query_benchmark.py +++ /dev/null @@ -1,235 +0,0 @@ -from pathlib import Path -from typing import Any - -import typer -from infrahub_sdk.async_typer import AsyncTyper -from rich.progress import ( - Progress, -) - -from infrahub import config -from infrahub.core import registry -from infrahub.core.constants import InfrahubKind -from infrahub.core.graph.constraints import ConstraintManagerNeo4j -from infrahub.core.graph.index import node_indexes, rel_indexes -from infrahub.core.graph.schema import GRAPH_SCHEMA -from infrahub.core.initialization import first_time_initialization, initialization -from infrahub.core.manager import NodeManager -from infrahub.core.schema import SchemaRoot -from infrahub.core.utils import delete_all_nodes -from infrahub.core.validators.uniqueness.model import NodeUniquenessQueryRequest -from infrahub.core.validators.uniqueness.query import NodeUniqueAttributeConstraintQuery -from infrahub.database import InfrahubDatabaseMode, get_db -from infrahub.database.analyzer import InfrahubDatabaseAnalyzer, query_stats -from infrahub.lock import initialize_lock -from infrahub.log import get_logger -from infrahub.test_data.gen_connected_nodes import GenerateConnectedNodes -from infrahub.test_data.gen_isolated_node import GenerateIsolatedNodes -from infrahub.test_data.gen_node_profile_node import ProfileAttribute - -app = AsyncTyper() - -log = get_logger() - - -@app.command() -async def isolated_node( - test_name: str = "isolated_node", - config_file: str = typer.Option("infrahub.toml", envvar="INFRAHUB_CONFIG"), - count: int = typer.Option(1000), - parallel: int = typer.Option(5), -) -> None: - config.load_and_exit(config_file_name=config_file) - - db = InfrahubDatabaseAnalyzer(mode=InfrahubDatabaseMode.DRIVER, driver=await get_db()) - log.info("Starting initialization .. ") - initialize_lock() - await initialization(db=db) - - default_branch = await registry.get_branch(db=db) - tag_schema = registry.schema.get_node_schema(name=InfrahubKind.TAG, branch=default_branch) - - query_stats.name = test_name - query_stats.measure_memory_usage = True - query_stats.output_location = Path.cwd() / "query_performance_results" - query_stats.start_tracking() - - log.info("Start loading data .. ") - with Progress() as progress: - loader = GenerateIsolatedNodes(db=db, progress=progress, concurrent_execution=parallel) - loader.add_callback(callback_name="query_50_tag", task=NodeManager.query, limit=50, schema=tag_schema) - await loader.load_data(nbr_tags=count, nbr_repository=count) - - query_stats.create_graphs() - - -@app.command() -async def connected_nodes( - test_name: str = "connected_nodes", - config_file: str = typer.Option("infrahub.toml", envvar="INFRAHUB_CONFIG"), - count: int = typer.Option(1000), - parallel: int = typer.Option(5), -) -> None: - config.load_and_exit(config_file_name=config_file) - - db = InfrahubDatabaseAnalyzer(mode=InfrahubDatabaseMode.DRIVER, driver=await get_db()) - log.info("Starting initialization .. ") - initialize_lock() - await initialization(db=db) - - default_branch = await registry.get_branch(db=db) - tag_schema = registry.schema.get_node_schema(name=InfrahubKind.TAG, branch=default_branch) - - query_stats.name = test_name - query_stats.measure_memory_usage = True - query_stats.output_location = Path.cwd() / "query_performance_results" - query_stats.start_tracking() - - log.info("Start loading data .. ") - with Progress() as progress: - loader = GenerateConnectedNodes(db=db, progress=progress, concurrent_execution=parallel) - loader.add_callback(callback_name="query_50_tag", task=NodeManager.query, limit=50, schema=tag_schema) - await loader.load_data(nbr_tags=count, nbr_repository=count) - - query_stats.create_graphs() - - -@app.command() -async def node_constraints_uniqueness( - test_name: str = "node_constraints_uniqueness", - config_file: str = typer.Option("infrahub.toml", envvar="INFRAHUB_CONFIG"), - count: int = typer.Option(1000), - parallel: int = typer.Option(5), -) -> None: - config.load_and_exit(config_file_name=config_file) - - db = InfrahubDatabaseAnalyzer(mode=InfrahubDatabaseMode.DRIVER, driver=await get_db()) - db.manager.index.init(nodes=node_indexes, rels=rel_indexes) - - constraint_manager = ConstraintManagerNeo4j.from_graph_schema(db=db, schema=GRAPH_SCHEMA) - - async with db.start_transaction() as dbt: - log.info("Delete All Nodes") - await delete_all_nodes(db=dbt) - - log.info("Remove existing constraints & indexes") - await dbt.manager.index.drop() - await constraint_manager.drop() - - await constraint_manager.add() - await first_time_initialization(db=dbt) - - log.info("Starting initialization .. ") - initialize_lock() - await initialization(db=db) - - default_branch = await registry.get_branch(db=db) - - query_stats.name = test_name - query_stats.measure_memory_usage = True - query_stats.output_location = Path.cwd() / "query_performance_results" - query_stats.start_tracking() - - query_unique_tag_name = await NodeUniqueAttributeConstraintQuery.init( - db=db, - branch=default_branch, - query_request=NodeUniquenessQueryRequest( - kind=InfrahubKind.TAG, unique_attribute_paths=[{"attribute_name": "name", "property_name": "value"}] - ), - ) - - log.info("Start loading data .. ") - with Progress() as progress: - loader = GenerateConnectedNodes(db=db, progress=progress, concurrent_execution=parallel) - loader.add_callback(callback_name="query_unique_tag_name", task=query_unique_tag_name.execute) - - await loader.load_data(nbr_tags=count, nbr_repository=count) - - query_stats.create_graphs() - - -@app.command() -async def profile_attribute( - test_name: str = "profile_attribute", - config_file: str = typer.Option("infrahub.toml", envvar="INFRAHUB_CONFIG"), - count: int = typer.Option(1000), - parallel: int = typer.Option(5), -) -> None: - config.load_and_exit(config_file_name=config_file) - - db = InfrahubDatabaseAnalyzer(mode=InfrahubDatabaseMode.DRIVER, driver=await get_db()) - db.manager.index.init(nodes=node_indexes, rels=rel_indexes) - - constraint_manager = ConstraintManagerNeo4j.from_graph_schema(db=db, schema=GRAPH_SCHEMA) - - async with db.start_transaction() as dbt: - log.info("Delete All Nodes") - await delete_all_nodes(db=dbt) - - log.info("Remove existing constraints & indexes") - await dbt.manager.index.drop() - await constraint_manager.drop() - - await constraint_manager.add() - await first_time_initialization(db=dbt) - - log.info("Starting initialization .. ") - initialize_lock() - await initialization(db=db) - - SCHEMA: dict[str, Any] = { - "nodes": [ - { - "name": "Car", - "namespace": "Test", - "default_filter": "name__value", - "display_labels": ["name__value", "color__value"], - "attributes": [ - {"name": "name", "kind": "Text", "unique": True}, - {"name": "nbr_seats", "kind": "Number"}, - {"name": "color", "kind": "Text", "default_value": "#444444", "max_length": 7}, - {"name": "is_electric", "kind": "Boolean"}, - ], - "relationships": [ - {"name": "owner", "peer": "TestPerson", "optional": False, "cardinality": "one"}, - ], - }, - { - "name": "Person", - "namespace": "Test", - "default_filter": "name__value", - "display_labels": ["name__value"], - "order_by": ["height__value"], - "attributes": [ - {"name": "name", "kind": "Text", "unique": True}, - {"name": "height", "kind": "Number", "optional": True}, - ], - "relationships": [{"name": "cars", "peer": "TestCar", "cardinality": "many"}], - }, - ], - } - - schema = SchemaRoot(**SCHEMA) - - default_branch = await registry.get_branch(db=db) - - registry.schema.register_schema(schema=schema, branch=default_branch.name) - - person_schema = registry.schema.get_node_schema(name="TestPerson", branch=default_branch) - - query_stats.name = test_name - query_stats.measure_memory_usage = False - query_stats.output_location = Path.cwd() / "query_performance_results" - query_stats.start_tracking() - - log.info("Start loading data .. ") - with Progress() as progress: - loader = ProfileAttribute(db=db, progress=progress, concurrent_execution=parallel) - loader.add_callback(callback_name="query_50_person", task=NodeManager.query, limit=50, schema=person_schema) - await loader.load_data(nbr_person=count) - - query_stats.create_graphs() - - -if __name__ == "__main__": - app() diff --git a/utilities/locust_dataset03_api_response_time.py b/utilities/locust_dataset03_api_response_time.py index 60cd8e69c6..80a85eef34 100644 --- a/utilities/locust_dataset03_api_response_time.py +++ b/utilities/locust_dataset03_api_response_time.py @@ -6,7 +6,7 @@ class InfrahubUser(HttpUser): # tasks = [InfrahubDataset03] @task - def query_device_names(self): + def query_device_names(self) -> None: query = """ query { device { @@ -20,7 +20,7 @@ def query_device_names(self): self.client.post("/graphql", json=data, name="query_device_names") @task - def query_one_device(self): + def query_one_device(self) -> None: query = """ query { device(name__value: "ord1-edge1"){ diff --git a/utilities/proposed_change_faker.py b/utilities/proposed_change_faker.py index 4a0d3e3672..03f2297540 100644 --- a/utilities/proposed_change_faker.py +++ b/utilities/proposed_change_faker.py @@ -168,7 +168,7 @@ async def create_proposed_change(client: InfrahubClient, log: logging.Logger) -> return new_proposed_change -async def run(client: InfrahubClient, log: logging.Logger, branch: str): +async def run(client: InfrahubClient, log: logging.Logger, branch: str) -> None: proposed_change = await create_proposed_change(client, log) repository = await create_repository(client, log) await create_validators(client, log, proposed_change, repository)